mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-04-20 06:32:13 +00:00
wip: player
This commit is contained in:
parent
a8a73ee9cc
commit
0fd3e29ec9
42 changed files with 1573 additions and 163 deletions
|
|
@ -1,13 +1,15 @@
|
|||
import config from 'eslint-config-standard-universal'
|
||||
import tseslint from 'typescript-eslint'
|
||||
import svelteConfig from './svelte.config.js'
|
||||
|
||||
export default tseslint.config(
|
||||
...config(),
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
tsconfigRootDir: import.meta.dirname
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
svelteConfig
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,15 +22,14 @@
|
|||
"@types/events": "^3.0.3",
|
||||
"@urql/introspection": "^1.1.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"bits-ui": "^0.21.16",
|
||||
"bits-ui": "^0.22.0",
|
||||
"cmdk-sv": "^0.0.18",
|
||||
"eslint-config-standard-universal": "^1.0.1",
|
||||
"eslint-config-standard-universal": "github:thaunknown/eslint-config-standard-universal",
|
||||
"globals": "^15.11.0",
|
||||
"gql.tada": "^1.8.10",
|
||||
"hayase-extensions": "github:hayase-app/extensions",
|
||||
"svelte": "^4.2.7",
|
||||
"svelte": "^4.2.19",
|
||||
"svelte-check": "^4.0.5",
|
||||
"svelte-eslint-parser": "^0.41.1",
|
||||
"svelte-radix": "^1.1.1",
|
||||
"svelte-sonner": "^0.3.28",
|
||||
"tailwindcss": "^3.4.13",
|
||||
|
|
@ -53,6 +52,7 @@
|
|||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"debug": "^4.3.7",
|
||||
"events": "^3.3.0",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"js-levenshtein": "^1.1.6",
|
||||
"lucide-svelte": "^0.452.0",
|
||||
|
|
|
|||
293
pnpm-lock.yaml
293
pnpm-lock.yaml
|
|
@ -13,25 +13,25 @@ importers:
|
|||
version: 5.1.0
|
||||
'@prgm/sveltekit-progress-bar':
|
||||
specifier: 2.0.0
|
||||
version: 2.0.0(@sveltejs/kit@2.8.1)(svelte@4.2.19)
|
||||
version: 2.0.0(@sveltejs/kit@2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)
|
||||
'@thaunknown/web-irc':
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
'@urql/exchange-auth':
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0(@urql/core@5.1.0)
|
||||
version: 2.2.0(@urql/core@5.1.0(graphql@16.10.0))
|
||||
'@urql/exchange-graphcache':
|
||||
specifier: ^7.2.1
|
||||
version: 7.2.1(@urql/core@5.1.0)(graphql@16.10.0)
|
||||
version: 7.2.1(@urql/core@5.1.0(graphql@16.10.0))(graphql@16.10.0)
|
||||
'@urql/exchange-request-policy':
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0(@urql/core@5.1.0)
|
||||
version: 1.2.0(@urql/core@5.1.0(graphql@16.10.0))
|
||||
'@urql/exchange-retry':
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0(@urql/core@5.1.0)
|
||||
version: 1.3.0(@urql/core@5.1.0(graphql@16.10.0))
|
||||
'@urql/svelte':
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(@urql/core@5.1.0)(svelte@4.2.19)
|
||||
version: 4.2.1(@urql/core@5.1.0(graphql@16.10.0))(svelte@4.2.19)
|
||||
abslink:
|
||||
specifier: ^1.0.9
|
||||
version: 1.0.9
|
||||
|
|
@ -50,6 +50,9 @@ importers:
|
|||
debug:
|
||||
specifier: ^4.3.7
|
||||
version: 4.3.7
|
||||
events:
|
||||
specifier: ^3.3.0
|
||||
version: 3.3.0
|
||||
idb-keyval:
|
||||
specifier: ^6.2.1
|
||||
version: 6.2.1
|
||||
|
|
@ -79,20 +82,20 @@ importers:
|
|||
version: 2.2.5
|
||||
urql:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(@urql/core@5.1.0)(react@19.0.0)
|
||||
version: 4.2.1(@urql/core@5.1.0(graphql@16.10.0))(react@19.0.0)
|
||||
devDependencies:
|
||||
'@gql.tada/svelte-support':
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1(svelte@4.2.19)(typescript@5.7.2)
|
||||
'@sveltejs/adapter-auto':
|
||||
specifier: ^3.2.5
|
||||
version: 3.2.5(@sveltejs/kit@2.8.1)
|
||||
version: 3.2.5(@sveltejs/kit@2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11))
|
||||
'@sveltejs/adapter-static':
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5(@sveltejs/kit@2.8.1)
|
||||
version: 3.0.5(@sveltejs/kit@2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11))
|
||||
'@sveltejs/kit':
|
||||
specifier: ^2.8.1
|
||||
version: 2.8.1(@sveltejs/vite-plugin-svelte@3.1.2)(svelte@4.2.19)(vite@5.4.11)
|
||||
version: 2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11)
|
||||
'@sveltejs/vite-plugin-svelte':
|
||||
specifier: ^3.1.2
|
||||
version: 3.1.2(svelte@4.2.19)(vite@5.4.11)
|
||||
|
|
@ -107,34 +110,31 @@ importers:
|
|||
version: 1.1.0(graphql@16.10.0)
|
||||
autoprefixer:
|
||||
specifier: ^10.4.20
|
||||
version: 10.4.20(postcss@8.4.49)
|
||||
version: 10.4.20(postcss@8.4.47)
|
||||
bits-ui:
|
||||
specifier: ^0.21.16
|
||||
version: 0.21.16(svelte@4.2.19)
|
||||
specifier: ^0.22.0
|
||||
version: 0.22.0(svelte@4.2.19)
|
||||
cmdk-sv:
|
||||
specifier: ^0.0.18
|
||||
version: 0.0.18(svelte@4.2.19)
|
||||
eslint-config-standard-universal:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1(@typescript-eslint/parser@8.18.0)(svelte@4.2.19)(typescript@5.7.2)
|
||||
specifier: github:thaunknown/eslint-config-standard-universal
|
||||
version: https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/d50760bd09eb47a2082cb7ec0310e0104ff4e799(@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
|
||||
gql.tada:
|
||||
specifier: ^1.8.10
|
||||
version: 1.8.10(@gql.tada/svelte-support@1.0.1)(graphql@16.10.0)(typescript@5.7.2)
|
||||
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/edf2e76fd25e9ed24cde1be03f82ce5703758e7a
|
||||
svelte:
|
||||
specifier: ^4.2.7
|
||||
specifier: ^4.2.19
|
||||
version: 4.2.19
|
||||
svelte-check:
|
||||
specifier: ^4.0.5
|
||||
version: 4.0.5(svelte@4.2.19)(typescript@5.7.2)
|
||||
svelte-eslint-parser:
|
||||
specifier: ^0.41.1
|
||||
version: 0.41.1(svelte@4.2.19)
|
||||
version: 4.0.5(picomatch@4.0.2)(svelte@4.2.19)(typescript@5.7.2)
|
||||
svelte-radix:
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1(svelte@4.2.19)
|
||||
|
|
@ -561,8 +561,8 @@ packages:
|
|||
'@rtsao/scc@1.1.0':
|
||||
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
||||
|
||||
'@stylistic/eslint-plugin@2.12.1':
|
||||
resolution: {integrity: sha512-fubZKIHSPuo07FgRTn6S4Nl0uXPRPYVNpyZzIDGfp7Fny6JjNus6kReLD7NI380JXi4HtUTSOZ34LBuNPO1XLQ==}
|
||||
'@stylistic/eslint-plugin@3.1.0':
|
||||
resolution: {integrity: sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: '>=8.40.0'
|
||||
|
|
@ -735,7 +735,6 @@ packages:
|
|||
|
||||
anitomyscript@https://codeload.github.com/thaunknown/anitomyscript/tar.gz/21a751e60a9efca83adb502212a3298f0e9a31e6:
|
||||
resolution: {tarball: https://codeload.github.com/thaunknown/anitomyscript/tar.gz/21a751e60a9efca83adb502212a3298f0e9a31e6}
|
||||
name: anitomyscript
|
||||
version: 2.0.7
|
||||
|
||||
ansi-regex@5.0.1:
|
||||
|
|
@ -835,6 +834,11 @@ packages:
|
|||
peerDependencies:
|
||||
svelte: ^4.0.0 || ^5.0.0-next.118
|
||||
|
||||
bits-ui@0.22.0:
|
||||
resolution: {integrity: sha512-r7Fw1HNgA4YxZBRcozl7oP0bheQ8EHh+kfMBZJgyFISix8t4p/nqDcHLmBgIiJ3T5XjYnJRorYDjIWaCfhb5fw==}
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0 || ^5.0.0
|
||||
|
||||
bittorrent-peerid@1.3.6:
|
||||
resolution: {integrity: sha512-VyLcUjVMEOdSpHaCG/7odvCdLbAB1y3l9A2V6WIje24uV7FkJPrQrH/RrlFmKxP89pFVDEnE+YlHaFujlFIZsg==}
|
||||
|
||||
|
|
@ -1095,8 +1099,9 @@ packages:
|
|||
peerDependencies:
|
||||
eslint: '>=6.0.0'
|
||||
|
||||
eslint-config-standard-universal@1.0.1:
|
||||
resolution: {integrity: sha512-vNoLFO1HYht4Lcl/QNovcreQxoS1wPDNaoIchdakm21B0EQlMdyHyylHDrFIJdkQANjaZEQwBchw7WfFN7ndjg==}
|
||||
eslint-config-standard-universal@https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/d50760bd09eb47a2082cb7ec0310e0104ff4e799:
|
||||
resolution: {tarball: https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/d50760bd09eb47a2082cb7ec0310e0104ff4e799}
|
||||
version: 1.0.4
|
||||
|
||||
eslint-import-resolver-node@0.3.9:
|
||||
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
|
||||
|
|
@ -1216,6 +1221,10 @@ packages:
|
|||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
events@3.3.0:
|
||||
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||
engines: {node: '>=0.8.x'}
|
||||
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
|
||||
|
|
@ -1388,7 +1397,6 @@ packages:
|
|||
|
||||
hayase-extensions@https://codeload.github.com/hayase-app/extensions/tar.gz/edf2e76fd25e9ed24cde1be03f82ce5703758e7a:
|
||||
resolution: {tarball: https://codeload.github.com/hayase-app/extensions/tar.gz/edf2e76fd25e9ed24cde1be03f82ce5703758e7a}
|
||||
name: hayase-extensions
|
||||
version: 1.0.5
|
||||
|
||||
idb-keyval@6.2.1:
|
||||
|
|
@ -2073,15 +2081,6 @@ packages:
|
|||
svelte: ^4.0.0 || ^5.0.0-next.0
|
||||
typescript: '>=5.0.0'
|
||||
|
||||
svelte-eslint-parser@0.41.1:
|
||||
resolution: {integrity: sha512-08ndI6zTghzI8SuJAFpvMbA/haPSGn3xz19pjre19yYMw8Nw/wQJ2PrZBI/L8ijGTgtkWCQQiLLy+Z1tfaCwNA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
svelte: ^3.37.0 || ^4.0.0 || ^5.0.0-next.191
|
||||
peerDependenciesMeta:
|
||||
svelte:
|
||||
optional: true
|
||||
|
||||
svelte-eslint-parser@0.43.0:
|
||||
resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
|
@ -2357,11 +2356,11 @@ packages:
|
|||
snapshots:
|
||||
|
||||
'@0no-co/graphql.web@1.0.11(graphql@16.10.0)':
|
||||
dependencies:
|
||||
optionalDependencies:
|
||||
graphql: 16.10.0
|
||||
|
||||
'@0no-co/graphql.web@1.0.12(graphql@16.10.0)':
|
||||
dependencies:
|
||||
optionalDependencies:
|
||||
graphql: 16.10.0
|
||||
|
||||
'@0no-co/graphqlsp@1.12.16(graphql@16.10.0)(typescript@5.7.2)':
|
||||
|
|
@ -2446,9 +2445,9 @@ snapshots:
|
|||
'@esbuild/win32-x64@0.21.5':
|
||||
optional: true
|
||||
|
||||
'@eslint-community/eslint-utils@4.4.1(eslint@9.17.0)':
|
||||
'@eslint-community/eslint-utils@4.4.1(eslint@9.17.0(jiti@1.21.6))':
|
||||
dependencies:
|
||||
eslint: 9.17.0
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.12.1': {}
|
||||
|
|
@ -2500,13 +2499,14 @@ snapshots:
|
|||
|
||||
'@fontsource-variable/nunito@5.1.0': {}
|
||||
|
||||
'@gql.tada/cli-utils@1.6.3(@0no-co/graphqlsp@1.12.16)(@gql.tada/svelte-support@1.0.1)(graphql@16.10.0)(typescript@5.7.2)':
|
||||
'@gql.tada/cli-utils@1.6.3(@0no-co/graphqlsp@1.12.16(graphql@16.10.0)(typescript@5.7.2))(@gql.tada/svelte-support@1.0.1(svelte@4.2.19)(typescript@5.7.2))(graphql@16.10.0)(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@0no-co/graphqlsp': 1.12.16(graphql@16.10.0)(typescript@5.7.2)
|
||||
'@gql.tada/internal': 1.0.8(graphql@16.10.0)(typescript@5.7.2)
|
||||
'@gql.tada/svelte-support': 1.0.1(svelte@4.2.19)(typescript@5.7.2)
|
||||
graphql: 16.10.0
|
||||
typescript: 5.7.2
|
||||
optionalDependencies:
|
||||
'@gql.tada/svelte-support': 1.0.1(svelte@4.2.19)(typescript@5.7.2)
|
||||
|
||||
'@gql.tada/internal@1.0.8(graphql@16.10.0)(typescript@5.7.2)':
|
||||
dependencies:
|
||||
|
|
@ -2593,9 +2593,9 @@ snapshots:
|
|||
|
||||
'@polka/url@1.0.0-next.28': {}
|
||||
|
||||
'@prgm/sveltekit-progress-bar@2.0.0(@sveltejs/kit@2.8.1)(svelte@4.2.19)':
|
||||
'@prgm/sveltekit-progress-bar@2.0.0(@sveltejs/kit@2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)':
|
||||
dependencies:
|
||||
'@sveltejs/kit': 2.8.1(@sveltejs/vite-plugin-svelte@3.1.2)(svelte@4.2.19)(vite@5.4.11)
|
||||
'@sveltejs/kit': 2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11)
|
||||
svelte: 4.2.19
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.27.2':
|
||||
|
|
@ -2654,10 +2654,10 @@ snapshots:
|
|||
|
||||
'@rtsao/scc@1.1.0': {}
|
||||
|
||||
'@stylistic/eslint-plugin@2.12.1(eslint@9.17.0)(typescript@5.7.2)':
|
||||
'@stylistic/eslint-plugin@3.1.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.18.0(eslint@9.17.0)(typescript@5.7.2)
|
||||
eslint: 9.17.0
|
||||
'@typescript-eslint/utils': 8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
eslint-visitor-keys: 4.2.0
|
||||
espree: 10.3.0
|
||||
estraverse: 5.3.0
|
||||
|
|
@ -2666,16 +2666,16 @@ snapshots:
|
|||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@sveltejs/adapter-auto@3.2.5(@sveltejs/kit@2.8.1)':
|
||||
'@sveltejs/adapter-auto@3.2.5(@sveltejs/kit@2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11))':
|
||||
dependencies:
|
||||
'@sveltejs/kit': 2.8.1(@sveltejs/vite-plugin-svelte@3.1.2)(svelte@4.2.19)(vite@5.4.11)
|
||||
'@sveltejs/kit': 2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11)
|
||||
import-meta-resolve: 4.1.0
|
||||
|
||||
'@sveltejs/adapter-static@3.0.5(@sveltejs/kit@2.8.1)':
|
||||
'@sveltejs/adapter-static@3.0.5(@sveltejs/kit@2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11))':
|
||||
dependencies:
|
||||
'@sveltejs/kit': 2.8.1(@sveltejs/vite-plugin-svelte@3.1.2)(svelte@4.2.19)(vite@5.4.11)
|
||||
'@sveltejs/kit': 2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11)
|
||||
|
||||
'@sveltejs/kit@2.8.1(@sveltejs/vite-plugin-svelte@3.1.2)(svelte@4.2.19)(vite@5.4.11)':
|
||||
'@sveltejs/kit@2.8.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11)':
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.19)(vite@5.4.11)
|
||||
'@types/cookie': 0.6.0
|
||||
|
|
@ -2693,7 +2693,7 @@ snapshots:
|
|||
tiny-glob: 0.2.9
|
||||
vite: 5.4.11
|
||||
|
||||
'@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2)(svelte@4.2.19)(vite@5.4.11)':
|
||||
'@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11)':
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.19)(vite@5.4.11)
|
||||
debug: 4.3.7
|
||||
|
|
@ -2704,7 +2704,7 @@ snapshots:
|
|||
|
||||
'@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11)':
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2)(svelte@4.2.19)(vite@5.4.11)
|
||||
'@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.11))(svelte@4.2.19)(vite@5.4.11)
|
||||
debug: 4.3.7
|
||||
deepmerge: 4.3.1
|
||||
kleur: 4.1.5
|
||||
|
|
@ -2742,15 +2742,15 @@ snapshots:
|
|||
|
||||
'@types/ms@0.7.34': {}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.18.0(@typescript-eslint/parser@8.18.0)(eslint@9.17.0)(typescript@5.7.2)':
|
||||
'@typescript-eslint/eslint-plugin@8.18.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))(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@typescript-eslint/parser': 8.18.0(eslint@9.17.0)(typescript@5.7.2)
|
||||
'@typescript-eslint/parser': 8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
|
||||
'@typescript-eslint/scope-manager': 8.18.0
|
||||
'@typescript-eslint/type-utils': 8.18.0(eslint@9.17.0)(typescript@5.7.2)
|
||||
'@typescript-eslint/utils': 8.18.0(eslint@9.17.0)(typescript@5.7.2)
|
||||
'@typescript-eslint/type-utils': 8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
|
||||
'@typescript-eslint/utils': 8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
|
||||
'@typescript-eslint/visitor-keys': 8.18.0
|
||||
eslint: 9.17.0
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
graphemer: 1.4.0
|
||||
ignore: 5.3.2
|
||||
natural-compare: 1.4.0
|
||||
|
|
@ -2759,14 +2759,14 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.18.0(eslint@9.17.0)(typescript@5.7.2)':
|
||||
'@typescript-eslint/parser@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.18.0
|
||||
'@typescript-eslint/types': 8.18.0
|
||||
'@typescript-eslint/typescript-estree': 8.18.0(typescript@5.7.2)
|
||||
'@typescript-eslint/visitor-keys': 8.18.0
|
||||
debug: 4.3.7
|
||||
eslint: 9.17.0
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
typescript: 5.7.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -2776,12 +2776,12 @@ snapshots:
|
|||
'@typescript-eslint/types': 8.18.0
|
||||
'@typescript-eslint/visitor-keys': 8.18.0
|
||||
|
||||
'@typescript-eslint/type-utils@8.18.0(eslint@9.17.0)(typescript@5.7.2)':
|
||||
'@typescript-eslint/type-utils@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 8.18.0(typescript@5.7.2)
|
||||
'@typescript-eslint/utils': 8.18.0(eslint@9.17.0)(typescript@5.7.2)
|
||||
'@typescript-eslint/utils': 8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
|
||||
debug: 4.3.7
|
||||
eslint: 9.17.0
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
ts-api-utils: 1.3.0(typescript@5.7.2)
|
||||
typescript: 5.7.2
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -2803,13 +2803,13 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.18.0(eslint@9.17.0)(typescript@5.7.2)':
|
||||
'@typescript-eslint/utils@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0)
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.6))
|
||||
'@typescript-eslint/scope-manager': 8.18.0
|
||||
'@typescript-eslint/types': 8.18.0
|
||||
'@typescript-eslint/typescript-estree': 8.18.0(typescript@5.7.2)
|
||||
eslint: 9.17.0
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
typescript: 5.7.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -2826,12 +2826,12 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- graphql
|
||||
|
||||
'@urql/exchange-auth@2.2.0(@urql/core@5.1.0)':
|
||||
'@urql/exchange-auth@2.2.0(@urql/core@5.1.0(graphql@16.10.0))':
|
||||
dependencies:
|
||||
'@urql/core': 5.1.0(graphql@16.10.0)
|
||||
wonka: 6.3.4
|
||||
|
||||
'@urql/exchange-graphcache@7.2.1(@urql/core@5.1.0)(graphql@16.10.0)':
|
||||
'@urql/exchange-graphcache@7.2.1(@urql/core@5.1.0(graphql@16.10.0))(graphql@16.10.0)':
|
||||
dependencies:
|
||||
'@0no-co/graphql.web': 1.0.11(graphql@16.10.0)
|
||||
'@urql/core': 5.1.0(graphql@16.10.0)
|
||||
|
|
@ -2839,12 +2839,12 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- graphql
|
||||
|
||||
'@urql/exchange-request-policy@1.2.0(@urql/core@5.1.0)':
|
||||
'@urql/exchange-request-policy@1.2.0(@urql/core@5.1.0(graphql@16.10.0))':
|
||||
dependencies:
|
||||
'@urql/core': 5.1.0(graphql@16.10.0)
|
||||
wonka: 6.3.4
|
||||
|
||||
'@urql/exchange-retry@1.3.0(@urql/core@5.1.0)':
|
||||
'@urql/exchange-retry@1.3.0(@urql/core@5.1.0(graphql@16.10.0))':
|
||||
dependencies:
|
||||
'@urql/core': 5.1.0(graphql@16.10.0)
|
||||
wonka: 6.3.4
|
||||
|
|
@ -2853,7 +2853,7 @@ snapshots:
|
|||
dependencies:
|
||||
graphql: 16.10.0
|
||||
|
||||
'@urql/svelte@4.2.1(@urql/core@5.1.0)(svelte@4.2.19)':
|
||||
'@urql/svelte@4.2.1(@urql/core@5.1.0(graphql@16.10.0))(svelte@4.2.19)':
|
||||
dependencies:
|
||||
'@urql/core': 5.1.0(graphql@16.10.0)
|
||||
svelte: 4.2.19
|
||||
|
|
@ -2955,14 +2955,14 @@ snapshots:
|
|||
is-array-buffer: 3.0.4
|
||||
is-shared-array-buffer: 1.0.3
|
||||
|
||||
autoprefixer@10.4.20(postcss@8.4.49):
|
||||
autoprefixer@10.4.20(postcss@8.4.47):
|
||||
dependencies:
|
||||
browserslist: 4.24.0
|
||||
caniuse-lite: 1.0.30001668
|
||||
fraction.js: 4.3.7
|
||||
normalize-range: 0.1.2
|
||||
picocolors: 1.1.0
|
||||
postcss: 8.4.49
|
||||
postcss: 8.4.47
|
||||
postcss-value-parser: 4.2.0
|
||||
|
||||
available-typed-arrays@1.0.7:
|
||||
|
|
@ -2991,6 +2991,13 @@ snapshots:
|
|||
nanoid: 5.0.7
|
||||
svelte: 4.2.19
|
||||
|
||||
bits-ui@0.22.0(svelte@4.2.19):
|
||||
dependencies:
|
||||
'@internationalized/date': 3.5.6
|
||||
'@melt-ui/svelte': 0.76.2(svelte@4.2.19)
|
||||
nanoid: 5.0.7
|
||||
svelte: 4.2.19
|
||||
|
||||
bittorrent-peerid@1.3.6: {}
|
||||
|
||||
bittorrent-tracker@9.19.0:
|
||||
|
|
@ -3056,6 +3063,7 @@ snapshots:
|
|||
bufferutil@4.0.9:
|
||||
dependencies:
|
||||
node-gyp-build: 4.8.2
|
||||
optional: true
|
||||
|
||||
call-bind@1.0.7:
|
||||
dependencies:
|
||||
|
|
@ -3111,7 +3119,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
'@types/estree': 1.0.6
|
||||
acorn: 8.12.1
|
||||
acorn: 8.14.0
|
||||
estree-walker: 3.0.3
|
||||
periscopic: 3.1.0
|
||||
|
||||
|
|
@ -3337,31 +3345,31 @@ snapshots:
|
|||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
||||
eslint-compat-utils@0.5.1(eslint@9.17.0):
|
||||
eslint-compat-utils@0.5.1(eslint@9.17.0(jiti@1.21.6)):
|
||||
dependencies:
|
||||
eslint: 9.17.0
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
semver: 7.6.3
|
||||
|
||||
eslint-config-standard-universal@1.0.1(@typescript-eslint/parser@8.18.0)(svelte@4.2.19)(typescript@5.7.2):
|
||||
eslint-config-standard-universal@https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/d50760bd09eb47a2082cb7ec0310e0104ff4e799(@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': 2.12.1(eslint@9.17.0)(typescript@5.7.2)
|
||||
eslint: 9.17.0
|
||||
'@stylistic/eslint-plugin': 3.1.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.0)(eslint@9.17.0)
|
||||
eslint-plugin-n: 17.15.0(eslint@9.17.0)
|
||||
eslint-plugin-promise: 7.2.1(eslint@9.17.0)
|
||||
eslint-plugin-svelte: 2.46.1(eslint@9.17.0)(svelte@4.2.19)
|
||||
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: 2.46.1(eslint@9.17.0(jiti@1.21.6))(svelte@4.2.19)
|
||||
globals: 15.13.0
|
||||
typescript-eslint: 8.18.0(eslint@9.17.0)(typescript@5.7.2)
|
||||
svelte: 4.2.19
|
||||
typescript: 5.7.2
|
||||
typescript-eslint: 8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
|
||||
transitivePeerDependencies:
|
||||
- '@typescript-eslint/parser'
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- jiti
|
||||
- supports-color
|
||||
- svelte
|
||||
- ts-node
|
||||
- typescript
|
||||
|
||||
eslint-import-resolver-node@0.3.9:
|
||||
dependencies:
|
||||
|
|
@ -3371,35 +3379,35 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.18.0)(eslint-import-resolver-node@0.3.9)(eslint@9.17.0):
|
||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint@9.17.0(jiti@1.21.6)):
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 8.18.0(eslint@9.17.0)(typescript@5.7.2)
|
||||
debug: 3.2.7
|
||||
eslint: 9.17.0
|
||||
optionalDependencies:
|
||||
'@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-import-resolver-node: 0.3.9
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-es-x@7.8.0(eslint@9.17.0):
|
||||
eslint-plugin-es-x@7.8.0(eslint@9.17.0(jiti@1.21.6)):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0)
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.6))
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
eslint: 9.17.0
|
||||
eslint-compat-utils: 0.5.1(eslint@9.17.0)
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
eslint-compat-utils: 0.5.1(eslint@9.17.0(jiti@1.21.6))
|
||||
|
||||
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.0)(eslint@9.17.0):
|
||||
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)):
|
||||
dependencies:
|
||||
'@rtsao/scc': 1.1.0
|
||||
'@typescript-eslint/parser': 8.18.0(eslint@9.17.0)(typescript@5.7.2)
|
||||
array-includes: 3.1.8
|
||||
array.prototype.findlastindex: 1.2.5
|
||||
array.prototype.flat: 1.3.2
|
||||
array.prototype.flatmap: 1.3.2
|
||||
debug: 3.2.7
|
||||
doctrine: 2.1.0
|
||||
eslint: 9.17.0
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.18.0)(eslint-import-resolver-node@0.3.9)(eslint@9.17.0)
|
||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint@9.17.0(jiti@1.21.6))
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.15.1
|
||||
is-glob: 4.0.3
|
||||
|
|
@ -3410,34 +3418,36 @@ snapshots:
|
|||
semver: 6.3.1
|
||||
string.prototype.trimend: 1.0.8
|
||||
tsconfig-paths: 3.15.0
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/parser': 8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-n@17.15.0(eslint@9.17.0):
|
||||
eslint-plugin-n@17.15.0(eslint@9.17.0(jiti@1.21.6)):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0)
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.6))
|
||||
enhanced-resolve: 5.17.1
|
||||
eslint: 9.17.0
|
||||
eslint-plugin-es-x: 7.8.0(eslint@9.17.0)
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
eslint-plugin-es-x: 7.8.0(eslint@9.17.0(jiti@1.21.6))
|
||||
get-tsconfig: 4.8.1
|
||||
globals: 15.13.0
|
||||
ignore: 5.3.2
|
||||
minimatch: 9.0.5
|
||||
semver: 7.6.3
|
||||
|
||||
eslint-plugin-promise@7.2.1(eslint@9.17.0):
|
||||
eslint-plugin-promise@7.2.1(eslint@9.17.0(jiti@1.21.6)):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0)
|
||||
eslint: 9.17.0
|
||||
'@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@2.46.1(eslint@9.17.0)(svelte@4.2.19):
|
||||
eslint-plugin-svelte@2.46.1(eslint@9.17.0(jiti@1.21.6))(svelte@4.2.19):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0)
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.6))
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
eslint: 9.17.0
|
||||
eslint-compat-utils: 0.5.1(eslint@9.17.0)
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
eslint-compat-utils: 0.5.1(eslint@9.17.0(jiti@1.21.6))
|
||||
esutils: 2.0.3
|
||||
known-css-properties: 0.35.0
|
||||
postcss: 8.4.49
|
||||
|
|
@ -3445,8 +3455,9 @@ snapshots:
|
|||
postcss-safe-parser: 6.0.0(postcss@8.4.49)
|
||||
postcss-selector-parser: 6.1.2
|
||||
semver: 7.6.3
|
||||
svelte: 4.2.19
|
||||
svelte-eslint-parser: 0.43.0(svelte@4.2.19)
|
||||
optionalDependencies:
|
||||
svelte: 4.2.19
|
||||
transitivePeerDependencies:
|
||||
- ts-node
|
||||
|
||||
|
|
@ -3464,9 +3475,9 @@ snapshots:
|
|||
|
||||
eslint-visitor-keys@4.2.0: {}
|
||||
|
||||
eslint@9.17.0:
|
||||
eslint@9.17.0(jiti@1.21.6):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0)
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.6))
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@eslint/config-array': 0.19.1
|
||||
'@eslint/core': 0.9.1
|
||||
|
|
@ -3500,6 +3511,8 @@ snapshots:
|
|||
minimatch: 3.1.2
|
||||
natural-compare: 1.4.0
|
||||
optionator: 0.9.4
|
||||
optionalDependencies:
|
||||
jiti: 1.21.6
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -3533,6 +3546,8 @@ snapshots:
|
|||
|
||||
esutils@2.0.3: {}
|
||||
|
||||
events@3.3.0: {}
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
fast-fifo@1.3.2: {}
|
||||
|
|
@ -3553,7 +3568,9 @@ snapshots:
|
|||
dependencies:
|
||||
reusify: 1.0.4
|
||||
|
||||
fdir@6.4.0: {}
|
||||
fdir@6.4.0(picomatch@4.0.2):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.2
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
dependencies:
|
||||
|
|
@ -3660,11 +3677,11 @@ snapshots:
|
|||
dependencies:
|
||||
get-intrinsic: 1.2.4
|
||||
|
||||
gql.tada@1.8.10(@gql.tada/svelte-support@1.0.1)(graphql@16.10.0)(typescript@5.7.2):
|
||||
gql.tada@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):
|
||||
dependencies:
|
||||
'@0no-co/graphql.web': 1.0.11(graphql@16.10.0)
|
||||
'@0no-co/graphqlsp': 1.12.16(graphql@16.10.0)(typescript@5.7.2)
|
||||
'@gql.tada/cli-utils': 1.6.3(@0no-co/graphqlsp@1.12.16)(@gql.tada/svelte-support@1.0.1)(graphql@16.10.0)(typescript@5.7.2)
|
||||
'@gql.tada/cli-utils': 1.6.3(@0no-co/graphqlsp@1.12.16(graphql@16.10.0)(typescript@5.7.2))(@gql.tada/svelte-support@1.0.1(svelte@4.2.19)(typescript@5.7.2))(graphql@16.10.0)(typescript@5.7.2)
|
||||
'@gql.tada/internal': 1.0.8(graphql@16.10.0)(typescript@5.7.2)
|
||||
typescript: 5.7.2
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -3932,7 +3949,8 @@ snapshots:
|
|||
lower-case: 2.0.2
|
||||
tslib: 2.7.0
|
||||
|
||||
node-gyp-build@4.8.2: {}
|
||||
node-gyp-build@4.8.2:
|
||||
optional: true
|
||||
|
||||
node-releases@2.0.18: {}
|
||||
|
||||
|
|
@ -4062,14 +4080,16 @@ snapshots:
|
|||
postcss-load-config@3.1.4(postcss@8.4.49):
|
||||
dependencies:
|
||||
lilconfig: 2.1.0
|
||||
postcss: 8.4.49
|
||||
yaml: 1.10.2
|
||||
optionalDependencies:
|
||||
postcss: 8.4.49
|
||||
|
||||
postcss-load-config@4.0.2(postcss@8.4.47):
|
||||
dependencies:
|
||||
lilconfig: 3.1.2
|
||||
postcss: 8.4.47
|
||||
yaml: 2.6.0
|
||||
optionalDependencies:
|
||||
postcss: 8.4.47
|
||||
|
||||
postcss-nested@6.2.0(postcss@8.4.47):
|
||||
dependencies:
|
||||
|
|
@ -4080,10 +4100,6 @@ snapshots:
|
|||
dependencies:
|
||||
postcss: 8.4.49
|
||||
|
||||
postcss-scss@4.0.9(postcss@8.4.47):
|
||||
dependencies:
|
||||
postcss: 8.4.47
|
||||
|
||||
postcss-scss@4.0.9(postcss@8.4.49):
|
||||
dependencies:
|
||||
postcss: 8.4.49
|
||||
|
|
@ -4378,11 +4394,11 @@ snapshots:
|
|||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
svelte-check@4.0.5(svelte@4.2.19)(typescript@5.7.2):
|
||||
svelte-check@4.0.5(picomatch@4.0.2)(svelte@4.2.19)(typescript@5.7.2):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
chokidar: 4.0.1
|
||||
fdir: 6.4.0
|
||||
fdir: 6.4.0(picomatch@4.0.2)
|
||||
picocolors: 1.1.0
|
||||
sade: 1.8.1
|
||||
svelte: 4.2.19
|
||||
|
|
@ -4390,15 +4406,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- picomatch
|
||||
|
||||
svelte-eslint-parser@0.41.1(svelte@4.2.19):
|
||||
dependencies:
|
||||
eslint-scope: 7.2.2
|
||||
eslint-visitor-keys: 3.4.3
|
||||
espree: 9.6.1
|
||||
postcss: 8.4.47
|
||||
postcss-scss: 4.0.9(postcss@8.4.47)
|
||||
svelte: 4.2.19
|
||||
|
||||
svelte-eslint-parser@0.43.0(svelte@4.2.19):
|
||||
dependencies:
|
||||
eslint-scope: 7.2.2
|
||||
|
|
@ -4406,6 +4413,7 @@ snapshots:
|
|||
espree: 9.6.1
|
||||
postcss: 8.4.49
|
||||
postcss-scss: 4.0.9(postcss@8.4.49)
|
||||
optionalDependencies:
|
||||
svelte: 4.2.19
|
||||
|
||||
svelte-hmr@0.16.0(svelte@4.2.19):
|
||||
|
|
@ -4437,7 +4445,7 @@ snapshots:
|
|||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
'@types/estree': 1.0.6
|
||||
acorn: 8.12.1
|
||||
acorn: 8.14.0
|
||||
aria-query: 5.3.2
|
||||
axobject-query: 4.1.0
|
||||
code-red: 1.0.4
|
||||
|
|
@ -4558,12 +4566,12 @@ snapshots:
|
|||
is-typed-array: 1.1.13
|
||||
possible-typed-array-names: 1.0.0
|
||||
|
||||
typescript-eslint@8.18.0(eslint@9.17.0)(typescript@5.7.2):
|
||||
typescript-eslint@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.18.0(@typescript-eslint/parser@8.18.0)(eslint@9.17.0)(typescript@5.7.2)
|
||||
'@typescript-eslint/parser': 8.18.0(eslint@9.17.0)(typescript@5.7.2)
|
||||
'@typescript-eslint/utils': 8.18.0(eslint@9.17.0)(typescript@5.7.2)
|
||||
eslint: 9.17.0
|
||||
'@typescript-eslint/eslint-plugin': 8.18.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))(typescript@5.7.2)
|
||||
'@typescript-eslint/parser': 8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
|
||||
'@typescript-eslint/utils': 8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
|
||||
eslint: 9.17.0(jiti@1.21.6)
|
||||
typescript: 5.7.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -4593,7 +4601,7 @@ snapshots:
|
|||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
urql@4.2.1(@urql/core@5.1.0)(react@19.0.0):
|
||||
urql@4.2.1(@urql/core@5.1.0(graphql@16.10.0))(react@19.0.0):
|
||||
dependencies:
|
||||
'@urql/core': 5.1.0(graphql@16.10.0)
|
||||
react: 19.0.0
|
||||
|
|
@ -4602,6 +4610,7 @@ snapshots:
|
|||
utf-8-validate@5.0.10:
|
||||
dependencies:
|
||||
node-gyp-build: 4.8.2
|
||||
optional: true
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
|
|
@ -4619,7 +4628,7 @@ snapshots:
|
|||
fsevents: 2.3.3
|
||||
|
||||
vitefu@0.2.5(vite@5.4.11):
|
||||
dependencies:
|
||||
optionalDependencies:
|
||||
vite: 5.4.11
|
||||
|
||||
vscode-languageserver-textdocument@1.0.12: {}
|
||||
|
|
@ -4671,7 +4680,7 @@ snapshots:
|
|||
optional: true
|
||||
|
||||
ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10):
|
||||
dependencies:
|
||||
optionalDependencies:
|
||||
bufferutil: 4.0.9
|
||||
utf-8-validate: 5.0.10
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
/* opt out of auto-dark-mode plugins, which break discord css, such as force-dark, dark-reader etc */
|
||||
color-scheme: only light;
|
||||
}
|
||||
|
||||
|
|
|
|||
32
src/app.d.ts
vendored
32
src/app.d.ts
vendored
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
|
||||
import type { SessionMetadata } from '$lib/components/ui/player/util'
|
||||
import type { Search } from '$lib/modules/anilist/queries'
|
||||
import type { VariablesOf } from 'gql.tada'
|
||||
|
||||
|
|
@ -11,6 +11,15 @@ export interface AuthResponse {
|
|||
token_type: 'Bearer'
|
||||
}
|
||||
|
||||
export interface Track {
|
||||
selected: boolean
|
||||
enabled: boolean
|
||||
id: string
|
||||
kind: string
|
||||
label: string
|
||||
language: string
|
||||
}
|
||||
|
||||
export interface Native {
|
||||
authAL: (url: string) => Promise<AuthResponse>
|
||||
restart: () => Promise<void>
|
||||
|
|
@ -23,11 +32,15 @@ export interface Native {
|
|||
selectDownload: () => Promise<string>
|
||||
setAngle: (angle: string) => Promise<void>
|
||||
getLogs: () => Promise<string>
|
||||
getDeviceInfo: () => Promise<any>
|
||||
getDeviceInfo: () => Promise<unknown>
|
||||
openUIDevtools: () => Promise<void>
|
||||
openTorrentDevtools: () => Promise<void>
|
||||
checkUpdate: () => Promise<void>
|
||||
toggleDiscordDetails: (enabled: boolean) => Promise<void>
|
||||
setMediaSession: (metadata: SessionMetadata) => Promise<void>
|
||||
setPositionState: (state?: MediaPositionState) => Promise<void>
|
||||
setPlayBackState: (paused: 'none' | 'paused' | 'playing') => Promise<void>
|
||||
setActionHandler: Navigator['mediaSession']['setActionHandler']
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
|
@ -51,13 +64,26 @@ declare global {
|
|||
function selectDownload (): Promise<string>
|
||||
function setAngle (angle: string): Promise<void>
|
||||
function getLogs (): Promise<string>
|
||||
function getDeviceInfo (): Promise<any>
|
||||
function getDeviceInfo (): Promise<unknown>
|
||||
function openUIDevtools (): Promise<void>
|
||||
function openTorrentDevtools (): Promise<void>
|
||||
function checkUpdate (): Promise<void>
|
||||
function toggleDiscordDetails (enabled: boolean): Promise<void>
|
||||
function setMediaSession (metadata: SessionMetadata): Promise<void>
|
||||
function setPositionState (state?: MediaPositionState): Promise<void>
|
||||
function setPlayBackState (paused: 'none' | 'paused' | 'playing'): Promise<void>
|
||||
function setActionHandler (...args: Parameters<Navigator['mediaSession']['setActionHandler']>): ReturnType<Navigator['mediaSession']['setActionHandler']>
|
||||
|
||||
function setTimeout (handler: TimerHandler, timeout?: number): number & { unref?: () => void }
|
||||
|
||||
interface HTMLMediaElement {
|
||||
videoTracks?: Track[]
|
||||
audioTracks?: Track[]
|
||||
}
|
||||
|
||||
interface ScreenOrientation {
|
||||
lock: (orientation: 'any' | 'natural' | 'landscape' | 'portrait' | 'portrait-primary' | 'portrait-secondary' | 'landscape-primary' | 'landscape-secondary') => Promise<void>
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
|
|
|
|||
9
src/lib/components/icons/PictureInPicture.svelte
Normal file
9
src/lib/components/icons/PictureInPicture.svelte
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<script lang='ts'>
|
||||
import { Icon, type Attrs } from 'lucide-svelte'
|
||||
import type { SvelteHTMLElements } from 'svelte/elements'
|
||||
const iconNode: Array<[elementName: keyof SvelteHTMLElements, attrs: Attrs]> = [['path', { d: 'M8 4.5v5H3m-1-6 6 6m13 0v-3c0-1.16-.84-2-2-2h-7m-9 9v2c0 1.05.95 2 2 2h3' }], ['rect', { width: '10', height: '7', x: '12', y: '13', rx: '2', fill: 'currentColor' }]]
|
||||
</script>
|
||||
|
||||
<Icon name='picture-in-picture' {...$$props} {iconNode}>
|
||||
<slot />
|
||||
</Icon>
|
||||
9
src/lib/components/icons/PictureInPictureExit.svelte
Normal file
9
src/lib/components/icons/PictureInPictureExit.svelte
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<script lang='ts'>
|
||||
import { Icon, type Attrs } from 'lucide-svelte'
|
||||
import type { SvelteHTMLElements } from 'svelte/elements'
|
||||
const iconNode: Array<[elementName: keyof SvelteHTMLElements, attrs: Attrs]> = [['path', { d: 'M21 9V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v10c0 1.1.9 2 2 2h4' }], ['rect', { width: '10', height: '7', x: '12', y: '13', rx: '2', fill: 'currentColor' }]]
|
||||
</script>
|
||||
|
||||
<Icon name='picture-in-picture-exit' {...$$props} {iconNode}>
|
||||
<slot />
|
||||
</Icon>
|
||||
12
src/lib/components/icons/Play.svelte
Normal file
12
src/lib/components/icons/Play.svelte
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<script lang='ts'>
|
||||
import { Icon, type Attrs } from 'lucide-svelte'
|
||||
import type { SvelteHTMLElements } from 'svelte/elements'
|
||||
|
||||
const iconNode: Array<[elementName: keyof SvelteHTMLElements, attrs: Attrs]> = [
|
||||
['path', { 'fill-rule': 'evenodd', 'clip-rule': 'evenodd', d: 'M22.99 11.7773C22.9219 10.8388 22.4205 9.92777 21.4859 9.39398L7.48782 1.39936C5.48785 0.25713 3 1.70127 3 4.00443V19.9937C3 22.2968 5.48785 23.741 7.48781 22.5988L21.4859 14.6041C22.4205 14.0703 22.9219 13.1593 22.99 12.2208C23.0226 12.0751 23.0226 11.9231 22.99 11.7773Z' }]
|
||||
]
|
||||
</script>
|
||||
|
||||
<Icon name='play' {...$$props} {iconNode} viewBox='0 0 24 24' strokeWidth='0'>
|
||||
<slot />
|
||||
</Icon>
|
||||
13
src/lib/components/icons/Subtitles.svelte
Normal file
13
src/lib/components/icons/Subtitles.svelte
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<script lang='ts'>
|
||||
import { Icon, type Attrs } from 'lucide-svelte'
|
||||
import type { SvelteHTMLElements } from 'svelte/elements'
|
||||
|
||||
const iconNode: Array<[elementName: keyof SvelteHTMLElements, attrs: Attrs]> = [
|
||||
['path', { fill: 'none', d: 'M0 0h24v24H0z' }],
|
||||
['path', { d: 'M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM4 12h4v2H4v-2zm10 6H4v-2h10v2zm6 0h-4v-2h4v2zm0-4H10v-2h10v2z' }]
|
||||
]
|
||||
</script>
|
||||
|
||||
<Icon name='subtitles' {...$$props} {iconNode} viewBox='0 0 24 24' strokeWidth='0'>
|
||||
<slot />
|
||||
</Icon>
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
{#await src then src}
|
||||
<div class={cn('object-cover w-screen absolute top-0 left-0 h-full overflow-hidden pointer-events-none bg-black', className)}>
|
||||
{#if src}
|
||||
<div class='min-w-[100vw] w-screen h-[30rem] bg-url bg-center bg-cover opacity-90 transition-opacity duration-500 border-gradient-to-t' style:--bg='url({src})' class:!opacity-15={$hideBanner} />
|
||||
<div class='min-w-[100vw] w-screen h-[30rem] bg-url bg-center bg-cover opacity-100 transition-opacity duration-500 border-gradient-to-t' style:--bg='url({src})' class:!opacity-45={$hideBanner} />
|
||||
{/if}
|
||||
</div>
|
||||
{/await}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const buttonVariants = tv({
|
|||
destructive: 'bg-destructive text-destructive-foreground select:bg-destructive/90 shadow-sm',
|
||||
outline: 'border-input bg-background select:bg-accent select:text-accent-foreground border shadow-sm',
|
||||
secondary: 'bg-secondary text-secondary-foreground select:bg-secondary/80 shadow-sm',
|
||||
ghost: 'select:bg-accent select:text-accent-foreground',
|
||||
ghost: 'select:bg-secondary-foreground/10 select:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 select:underline'
|
||||
},
|
||||
size: {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
<script lang="ts">
|
||||
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||
import Check from "svelte-radix/Check.svelte";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = ContextMenuPrimitive.CheckboxItemProps;
|
||||
type $$Events = ContextMenuPrimitive.CheckboxItemEvents;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
export let checked: $$Props["checked"] = undefined;
|
||||
</script>
|
||||
|
||||
<ContextMenuPrimitive.CheckboxItem
|
||||
bind:checked
|
||||
class={cn(
|
||||
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
on:focusin
|
||||
on:focusout
|
||||
on:pointerdown
|
||||
on:pointerleave
|
||||
on:pointermove
|
||||
>
|
||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<ContextMenuPrimitive.CheckboxIndicator>
|
||||
<Check class="h-4 w-4" />
|
||||
</ContextMenuPrimitive.CheckboxIndicator>
|
||||
</span>
|
||||
<slot />
|
||||
</ContextMenuPrimitive.CheckboxItem>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<script lang="ts">
|
||||
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||
import { cn, flyAndScale } from "$lib/utils.js";
|
||||
|
||||
type $$Props = ContextMenuPrimitive.ContentProps;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export let transition: $$Props["transition"] = flyAndScale;
|
||||
export let transitionConfig: $$Props["transitionConfig"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<ContextMenuPrimitive.Content
|
||||
{transition}
|
||||
{transitionConfig}
|
||||
class={cn(
|
||||
"bg-popover text-popover-foreground z-50 min-w-[8rem] rounded-md border p-1 shadow-md focus:outline-none",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:keydown
|
||||
>
|
||||
<slot />
|
||||
</ContextMenuPrimitive.Content>
|
||||
31
src/lib/components/ui/context-menu/context-menu-item.svelte
Normal file
31
src/lib/components/ui/context-menu/context-menu-item.svelte
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<script lang="ts">
|
||||
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = ContextMenuPrimitive.ItemProps & {
|
||||
inset?: boolean;
|
||||
};
|
||||
type $$Events = ContextMenuPrimitive.ItemEvents;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export let inset: $$Props["inset"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<ContextMenuPrimitive.Item
|
||||
class={cn(
|
||||
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
on:focusin
|
||||
on:focusout
|
||||
on:pointerdown
|
||||
on:pointerleave
|
||||
on:pointermove
|
||||
>
|
||||
<slot />
|
||||
</ContextMenuPrimitive.Item>
|
||||
19
src/lib/components/ui/context-menu/context-menu-label.svelte
Normal file
19
src/lib/components/ui/context-menu/context-menu-label.svelte
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<script lang="ts">
|
||||
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = ContextMenuPrimitive.LabelProps & {
|
||||
inset?: boolean;
|
||||
};
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export let inset: $$Props["inset"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<ContextMenuPrimitive.Label
|
||||
class={cn("text-foreground px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</ContextMenuPrimitive.Label>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||
|
||||
type $$Props = ContextMenuPrimitive.RadioGroupProps;
|
||||
|
||||
export let value: $$Props["value"] = undefined;
|
||||
</script>
|
||||
|
||||
<ContextMenuPrimitive.RadioGroup {...$$restProps} bind:value>
|
||||
<slot />
|
||||
</ContextMenuPrimitive.RadioGroup>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<script lang="ts">
|
||||
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||
import DotFilled from "svelte-radix/DotFilled.svelte";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = ContextMenuPrimitive.RadioItemProps;
|
||||
type $$Events = ContextMenuPrimitive.RadioItemEvents;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export let value: $$Props["value"];
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<ContextMenuPrimitive.RadioItem
|
||||
class={cn(
|
||||
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{value}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
on:focusin
|
||||
on:focusout
|
||||
on:pointerdown
|
||||
on:pointerleave
|
||||
on:pointermove
|
||||
>
|
||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<ContextMenuPrimitive.RadioIndicator>
|
||||
<DotFilled class="h-4 w-4 fill-current" />
|
||||
</ContextMenuPrimitive.RadioIndicator>
|
||||
</span>
|
||||
<slot />
|
||||
</ContextMenuPrimitive.RadioItem>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<script lang="ts">
|
||||
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = ContextMenuPrimitive.SeparatorProps;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<ContextMenuPrimitive.Separator
|
||||
class={cn("bg-border -mx-1 my-1 h-px", className)}
|
||||
{...$$restProps}
|
||||
/>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLSpanElement>;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<span
|
||||
class={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</span>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<script lang="ts">
|
||||
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||
import { cn, flyAndScale } from "$lib/utils.js";
|
||||
|
||||
type $$Props = ContextMenuPrimitive.SubContentProps;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export let transition: $$Props["transition"] = flyAndScale;
|
||||
export let transitionConfig: $$Props["transitionConfig"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<ContextMenuPrimitive.SubContent
|
||||
{transition}
|
||||
{transitionConfig}
|
||||
class={cn(
|
||||
"bg-popover text-popover-foreground z-50 min-w-[8rem] rounded-md border p-1 shadow-lg focus:outline-none",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:keydown
|
||||
on:focusout
|
||||
on:pointermove
|
||||
>
|
||||
<slot />
|
||||
</ContextMenuPrimitive.SubContent>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<script lang="ts">
|
||||
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||
import ChevronRight from "svelte-radix/ChevronRight.svelte";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = ContextMenuPrimitive.SubTriggerProps & {
|
||||
inset?: boolean;
|
||||
};
|
||||
type $$Events = ContextMenuPrimitive.SubTriggerEvents;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export let inset: $$Props["inset"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<ContextMenuPrimitive.SubTrigger
|
||||
class={cn(
|
||||
"data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
on:focusin
|
||||
on:focusout
|
||||
on:pointerleave
|
||||
on:pointermove
|
||||
>
|
||||
<slot />
|
||||
<ChevronRight class="ml-auto h-4 w-4" />
|
||||
</ContextMenuPrimitive.SubTrigger>
|
||||
49
src/lib/components/ui/context-menu/index.ts
Normal file
49
src/lib/components/ui/context-menu/index.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||
|
||||
import Item from "./context-menu-item.svelte";
|
||||
import Label from "./context-menu-label.svelte";
|
||||
import Content from "./context-menu-content.svelte";
|
||||
import Shortcut from "./context-menu-shortcut.svelte";
|
||||
import RadioItem from "./context-menu-radio-item.svelte";
|
||||
import Separator from "./context-menu-separator.svelte";
|
||||
import RadioGroup from "./context-menu-radio-group.svelte";
|
||||
import SubContent from "./context-menu-sub-content.svelte";
|
||||
import SubTrigger from "./context-menu-sub-trigger.svelte";
|
||||
import CheckboxItem from "./context-menu-checkbox-item.svelte";
|
||||
|
||||
const Sub = ContextMenuPrimitive.Sub;
|
||||
const Root = ContextMenuPrimitive.Root;
|
||||
const Trigger = ContextMenuPrimitive.Trigger;
|
||||
const Group = ContextMenuPrimitive.Group;
|
||||
|
||||
export {
|
||||
Sub,
|
||||
Root,
|
||||
Item,
|
||||
Label,
|
||||
Group,
|
||||
Trigger,
|
||||
Content,
|
||||
Shortcut,
|
||||
Separator,
|
||||
RadioItem,
|
||||
SubContent,
|
||||
SubTrigger,
|
||||
RadioGroup,
|
||||
CheckboxItem,
|
||||
//
|
||||
Root as ContextMenu,
|
||||
Sub as ContextMenuSub,
|
||||
Item as ContextMenuItem,
|
||||
Label as ContextMenuLabel,
|
||||
Group as ContextMenuGroup,
|
||||
Content as ContextMenuContent,
|
||||
Trigger as ContextMenuTrigger,
|
||||
Shortcut as ContextMenuShortcut,
|
||||
RadioItem as ContextMenuRadioItem,
|
||||
Separator as ContextMenuSeparator,
|
||||
RadioGroup as ContextMenuRadioGroup,
|
||||
SubContent as ContextMenuSubContent,
|
||||
SubTrigger as ContextMenuSubTrigger,
|
||||
CheckboxItem as ContextMenuCheckboxItem,
|
||||
};
|
||||
|
|
@ -16,6 +16,6 @@
|
|||
<DialogPrimitive.Overlay
|
||||
{transition}
|
||||
{transitionConfig}
|
||||
class={cn('custom-bg fixed inset-0 z-50 backdrop-blur-sm', className)}
|
||||
class={cn('custom-bg absolute inset-0 z-50 backdrop-blur-sm', className)}
|
||||
{...$$restProps}
|
||||
/>
|
||||
|
|
|
|||
1
src/lib/components/ui/player/index.ts
Normal file
1
src/lib/components/ui/player/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as Player } from './player.svelte'
|
||||
385
src/lib/components/ui/player/player.svelte
Normal file
385
src/lib/components/ui/player/player.svelte
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
<script lang='ts'>
|
||||
import * as Dialog from '$lib/components/ui/dialog'
|
||||
import PictureInPictureExit from '$lib/components/icons/PictureInPictureExit.svelte'
|
||||
import PictureInPicture from '$lib/components/icons/PictureInPicture.svelte'
|
||||
import Subtitles from '$lib/components/icons/Subtitles.svelte'
|
||||
import Play from '$lib/components/icons/Play.svelte'
|
||||
import { Button } from '$lib/components/ui/button'
|
||||
import { settings } from '$lib/modules/settings'
|
||||
import { bindPiP, toTS } from '$lib/utils'
|
||||
import { Cast, EllipsisVertical, FastForward, Maximize, Minimize, Pause, Rewind, SkipBack, SkipForward } from 'lucide-svelte'
|
||||
import { writable, type Writable } from 'simple-store-svelte'
|
||||
import { persisted } from 'svelte-persisted-store'
|
||||
import { toast } from 'svelte-sonner'
|
||||
import Seekbar from './seekbar.svelte'
|
||||
import type { SvelteMediaTimeRange } from 'svelte/elements'
|
||||
import { fade } from 'svelte/transition'
|
||||
import { getChapterTitle, normalizeTracks, sanitizeChapters, type MediaInfo } from './util'
|
||||
import Thumbnailer from './thumbnailer'
|
||||
import { onMount, tick } from 'svelte'
|
||||
import native from '$lib/modules/native'
|
||||
import { click } from '$lib/modules/navigate'
|
||||
import { goto } from '$app/navigation'
|
||||
import * as Tree from '$lib/components/ui/tree'
|
||||
|
||||
export let mediaInfo: MediaInfo
|
||||
// bindings
|
||||
// values
|
||||
let videoHeight = 9
|
||||
let videoWidth = 16
|
||||
let currentTime = 0
|
||||
let seekPercent = 0
|
||||
let duration = 1
|
||||
let playbackRate = 1
|
||||
let buffered: SvelteMediaTimeRange[] = []
|
||||
$: buffer = Math.max(...buffered.map(({ end }) => end))
|
||||
let readyState = 0
|
||||
$: safeduration = isFinite(duration) ? duration : currentTime
|
||||
const volume = persisted('volume', 0)
|
||||
|
||||
// state
|
||||
let seeking = false
|
||||
let ended = false
|
||||
let paused = true
|
||||
const cast = false
|
||||
|
||||
// elements
|
||||
let fullscreenElement: HTMLElement | null = null
|
||||
const pictureInPictureElement: Writable<HTMLVideoElement | null> = writable(null)
|
||||
let video: HTMLVideoElement
|
||||
let wrapper: HTMLDivElement
|
||||
|
||||
// functions
|
||||
function playPause () {
|
||||
playAnimation(paused ? 'play' : 'pause')
|
||||
return paused ? video.play() : video.pause()
|
||||
}
|
||||
function fullscreen () {
|
||||
return fullscreenElement ? document.exitFullscreen() : wrapper.requestFullscreen()
|
||||
}
|
||||
|
||||
function pip () {
|
||||
return $pictureInPictureElement ? document.exitPictureInPicture() : video.requestPictureInPicture()
|
||||
}
|
||||
|
||||
$: fullscreenElement ? screen.orientation.lock('landscape') : screen.orientation.unlock()
|
||||
|
||||
function checkAudio () {
|
||||
if ('audioTracks' in HTMLVideoElement.prototype) {
|
||||
if (!video.audioTracks!.length) {
|
||||
toast.error('Audio Codec Unsupported', {
|
||||
description: "This torrent's audio codec is not supported, try a different release by disabling Autoplay Torrents in RSS settings."
|
||||
})
|
||||
} else if (video.audioTracks!.length > 1) {
|
||||
const preferredTrack = [...video.audioTracks!].find(({ language }) => language === $settings.audioLanguage)
|
||||
if (preferredTrack) return selectAudio(preferredTrack.id)
|
||||
|
||||
const japaneseTrack = [...video.audioTracks!].find(({ language }) => language === 'jpn')
|
||||
if (japaneseTrack) return selectAudio(japaneseTrack.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
function selectAudio (id: string) {
|
||||
if (id) {
|
||||
for (const track of video.audioTracks ?? []) {
|
||||
track.enabled = track.id === id
|
||||
}
|
||||
seek(-0.2) // stupid fix because video freezes up when chaging tracks
|
||||
}
|
||||
open = false
|
||||
}
|
||||
function selectVideo (id: string) {
|
||||
if (id) {
|
||||
for (const track of video.videoTracks!) {
|
||||
track.selected = track.id === id
|
||||
}
|
||||
}
|
||||
open = false
|
||||
}
|
||||
function prev () {
|
||||
// TODO
|
||||
}
|
||||
function next () {
|
||||
// TODO
|
||||
}
|
||||
function seek (time: number) {
|
||||
video.currentTime = currentTime = currentTime + time
|
||||
playAnimation(time > 0 ? 'seekforw' : 'seekback')
|
||||
}
|
||||
function seekTo (time: number) {
|
||||
playAnimation(time > currentTime ? 'seekforw' : 'seekback')
|
||||
video.currentTime = currentTime = time
|
||||
}
|
||||
let wasPaused = false
|
||||
function startSeek () {
|
||||
wasPaused = paused
|
||||
if (!paused) video.pause()
|
||||
}
|
||||
|
||||
function finishSeek () {
|
||||
seekTo(seekPercent * safeduration / 100)
|
||||
if (!wasPaused) video.play()
|
||||
}
|
||||
|
||||
// animations
|
||||
|
||||
function playAnimation (type: 'play' | 'pause' | 'seekforw' | 'seekback') {
|
||||
animations.push({ type, id: crypto.randomUUID() })
|
||||
// eslint-disable-next-line no-self-assign
|
||||
animations = animations
|
||||
}
|
||||
function endAnimation (id: string) {
|
||||
const index = animations.findIndex(animation => animation.id === id)
|
||||
if (index !== -1) animations.splice(index, 1)
|
||||
// eslint-disable-next-line no-self-assign
|
||||
animations = animations
|
||||
}
|
||||
interface Animation {
|
||||
type: 'play' | 'pause' | 'seekforw' | 'seekback'
|
||||
id: string
|
||||
}
|
||||
let animations: Animation[] = []
|
||||
|
||||
const thumbnailer = new Thumbnailer(mediaInfo.url)
|
||||
|
||||
$: thumbnailer.updateSource(mediaInfo.url)
|
||||
|
||||
onMount(() => {
|
||||
thumbnailer.setVideo(video)
|
||||
})
|
||||
|
||||
// other
|
||||
|
||||
$: chapters = sanitizeChapters([
|
||||
{ start: 5, end: 15, text: 'Chapter 0' },
|
||||
{ start: 1.0 * 60, end: 1.2 * 60, text: 'Chapter 1' },
|
||||
{ start: 1.4 * 60, end: 88, text: 'Chapter 2 ' }
|
||||
], safeduration)
|
||||
|
||||
$: seekIndex = Math.max(0, Math.floor(seekPercent * safeduration / 100 / thumbnailer.interval))
|
||||
|
||||
$: native.setMediaSession(mediaInfo.session)
|
||||
$: native.setPositionState({ duration: safeduration, position: Math.max(0, currentTime), playbackRate })
|
||||
$: native.setPlayBackState(readyState === 0 ? 'none' : paused ? 'paused' : 'playing')
|
||||
native.setActionHandler('play', playPause)
|
||||
native.setActionHandler('pause', playPause)
|
||||
native.setActionHandler('seekto', ({ seekTime }) => seekTo(seekTime ?? 0))
|
||||
native.setActionHandler('seekbackward', () => seek(-2))
|
||||
native.setActionHandler('seekforward', () => seek(2))
|
||||
native.setActionHandler('previoustrack', prev)
|
||||
native.setActionHandler('nexttrack', next)
|
||||
|
||||
let open = false
|
||||
let treeState: Writable<string[]>
|
||||
|
||||
async function openSubs () {
|
||||
open = true
|
||||
await tick()
|
||||
treeState.set(['subs'])
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:document bind:fullscreenElement use:bindPiP={pictureInPictureElement} />
|
||||
|
||||
<div style:aspect-ratio='{videoWidth} / {videoHeight}' class='max-w-full max-h-full min-w-[clamp(0%,700px,100%)] relative content-center fullscreen:bg-black fullscreen:rounded-none rounded-xl overflow-clip' bind:this={wrapper}>
|
||||
<video
|
||||
class='w-full max-h-full grow bg-black'
|
||||
preload='auto'
|
||||
loop
|
||||
src={mediaInfo.url}
|
||||
bind:videoHeight
|
||||
bind:videoWidth
|
||||
bind:currentTime
|
||||
bind:duration
|
||||
bind:ended
|
||||
bind:paused
|
||||
bind:readyState
|
||||
bind:buffered
|
||||
bind:playbackRate
|
||||
bind:volume={$volume}
|
||||
bind:this={video}
|
||||
on:click={playPause}
|
||||
on:loadeddata={checkAudio}
|
||||
/>
|
||||
<div class='absolute w-full h-full flex items-center justify-center top-0 pointer-events-none'>
|
||||
{#if seeking}
|
||||
{#await thumbnailer.getThumbnail(seekIndex) then src}
|
||||
<img {src} alt='thumbnail' class='w-full h-full bg-black absolute top-0 right-0' />
|
||||
{/await}
|
||||
{/if}
|
||||
<Button class='mobile:inline-flex hidden p-3 w-12 h-12 absolute top-10 right-10 backdrop-blur-lg border-white/15 border bg-black/20 pointer-events-auto' variant='ghost'>
|
||||
<EllipsisVertical size='24px' class='p-[1px]' />
|
||||
</Button>
|
||||
<div class='mobile:flex hidden gap-4 absolute items-center'>
|
||||
<Button class='p-3 w-16 h-16 pointer-events-auto rounded-[50%] backdrop-blur-lg border-white/15 border bg-black/20' variant='ghost'>
|
||||
<SkipBack size='24px' fill='currentColor' strokeWidth='1' />
|
||||
</Button>
|
||||
<Button class='p-3 w-24 h-24 pointer-events-auto rounded-[50%] backdrop-blur-lg border-white/15 border bg-black/20' variant='ghost' on:click={playPause}>
|
||||
{#if paused}
|
||||
<Play size='42px' fill='currentColor' class='p-0.5' />
|
||||
{:else}
|
||||
<Pause size='42px' fill='currentColor' strokeWidth='1' />
|
||||
{/if}
|
||||
</Button>
|
||||
<Button class='p-3 w-16 h-16 pointer-events-auto rounded-[50%] backdrop-blur-lg border-white/15 border bg-black/20' variant='ghost'>
|
||||
<SkipForward size='24px' fill='currentColor' strokeWidth='1' />
|
||||
</Button>
|
||||
</div>
|
||||
{#if readyState < 3}
|
||||
<div in:fade={{ duration: 200, delay: 500 }} out:fade={{ duration: 200 }}>
|
||||
<div class='border-[3px] rounded-[50%] w-10 h-10 drop-shadow-lg border-transparent border-t-white animate-spin' />
|
||||
</div>
|
||||
{/if}
|
||||
{#each animations as { type, id } (id)}
|
||||
<div class='absolute animate-pulse-once' on:animationend={() => endAnimation(id)}>
|
||||
{#if type === 'play'}
|
||||
<Play size='64px' fill='white' />
|
||||
{:else if type === 'pause'}
|
||||
<Pause size='64px' fill='white' />
|
||||
{:else if type === 'seekforw'}
|
||||
<FastForward size='64px' fill='white' />
|
||||
{:else if type === 'seekback'}
|
||||
<Rewind size='64px' fill='white' />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class='absolute w-full bottom-0 flex flex-col gradient px-10 py-4'>
|
||||
<div class='flex justify-between gap-12'>
|
||||
<div class='flex flex-col gap-2 text-left group/mediainfo cursor-pointer' use:click={() => goto(`/anime/${mediaInfo.media.id}`)}>
|
||||
<div class='text-white text-lg font-normal leading-none line-clamp-1 group-hover/mediainfo:text-neutral-200'>{mediaInfo.session.title}</div>
|
||||
<div class='text-[rgba(217,217,217,0.6)] group-hover/mediainfo:text-neutral-400 text-sm leading-none font-light line-clamp-1'>{mediaInfo.session.description}</div>
|
||||
</div>
|
||||
<div class='flex flex-col gap-2 grow-0 items-end self-end'>
|
||||
<div class='text-[rgba(217,217,217,0.6)] text-sm leading-none font-light line-clamp-1'>{getChapterTitle(seeking ? seekPercent * safeduration / 100 : currentTime, chapters) || ''}</div>
|
||||
<div class='ml-auto self-end text-sm leading-none font-light text-nowrap'>{toTS(seeking ? seekPercent * safeduration / 100 : currentTime)} / {toTS(safeduration)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Seekbar {duration} {currentTime} buffer={buffer / duration * 100} {chapters} bind:seeking bind:seek={seekPercent} on:seeked={finishSeek} on:seeking={startSeek} {thumbnailer} />
|
||||
<div class='flex justify-between gap-2 mobile:hidden'>
|
||||
<div class='flex text-white gap-2'>
|
||||
<Button class='p-3 w-12 h-12' variant='ghost' on:click={playPause}>
|
||||
{#if paused}
|
||||
<Play size='24px' fill='currentColor' class='p-0.5' />
|
||||
{:else}
|
||||
<Pause size='24px' fill='currentColor' strokeWidth='1' />
|
||||
{/if}
|
||||
</Button>
|
||||
<Button class='p-3 w-12 h-12' variant='ghost' on:click={prev}>
|
||||
<SkipBack size='24px' fill='currentColor' strokeWidth='1' />
|
||||
</Button>
|
||||
<Button class='p-3 w-12 h-12' variant='ghost' on:click={next}>
|
||||
<SkipForward size='24px' fill='currentColor' strokeWidth='1' />
|
||||
</Button>
|
||||
</div>
|
||||
<div class='flex gap-2'>
|
||||
<Dialog.Root portal={wrapper} bind:open>
|
||||
<Dialog.Trigger asChild let:builder>
|
||||
<Button class='p-3 w-12 h-12' variant='ghost' builders={[builder]}>
|
||||
<EllipsisVertical size='24px' class='p-[1px]' />
|
||||
</Button>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Content class='absolute bg-transparent border-none p-0 shadow-none h-full w-full overflow-hidden'>
|
||||
<div on:pointerdown|self={() => { open = false }} class='h-full flex w-full justify-center items-center overflow-y-scroll'>
|
||||
<Tree.Root bind:state={treeState}>
|
||||
<Tree.Item>
|
||||
<span slot='trigger'>Audio</span>
|
||||
<Tree.Sub>
|
||||
{#each Object.entries(normalizeTracks(video.audioTracks ?? [])) as [lang, tracks] (lang)}
|
||||
<Tree.Item>
|
||||
<span slot='trigger' class='capitalize'>{lang}</span>
|
||||
<Tree.Sub>
|
||||
{#each tracks as track (track.id)}
|
||||
<Tree.Item active={track.enabled} on:click={() => selectAudio(track.id)}>
|
||||
<span>{track.label}</span>
|
||||
</Tree.Item>
|
||||
{/each}
|
||||
</Tree.Sub>
|
||||
</Tree.Item>
|
||||
{/each}
|
||||
</Tree.Sub>
|
||||
</Tree.Item>
|
||||
<Tree.Item>
|
||||
<span slot='trigger'>Video</span>
|
||||
<Tree.Sub>
|
||||
{#each Object.entries(normalizeTracks(video.videoTracks ?? [])) as [lang, tracks] (lang)}
|
||||
<Tree.Item>
|
||||
<span slot='trigger' class='capitalize'>{lang}</span>
|
||||
<Tree.Sub>
|
||||
{#each tracks as track (track.id)}
|
||||
<Tree.Item active={track.enabled} on:click={() => selectVideo(track.id)}>
|
||||
<span>{track.label}</span>
|
||||
</Tree.Item>
|
||||
{/each}
|
||||
</Tree.Sub>
|
||||
</Tree.Item>
|
||||
{/each}
|
||||
</Tree.Sub>
|
||||
</Tree.Item>
|
||||
<Tree.Item id='subs'>
|
||||
<span slot='trigger'>Subtitles</span>
|
||||
<Tree.Sub>
|
||||
<Tree.Item>
|
||||
<span>Consulting</span>
|
||||
</Tree.Item>
|
||||
<Tree.Item>
|
||||
<span>Support</span>
|
||||
</Tree.Item>
|
||||
</Tree.Sub>
|
||||
</Tree.Item>
|
||||
</Tree.Root>\
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
|
||||
<Button class='p-3 w-12 h-12' variant='ghost' on:click={openSubs}>
|
||||
<Subtitles size='24px' fill='currentColor' strokeWidth='0' />
|
||||
</Button>
|
||||
<Button class='p-3 w-12 h-12' variant='ghost' on:click={pip}>
|
||||
{#if $pictureInPictureElement}
|
||||
<PictureInPictureExit size='24px' strokeWidth='2' />
|
||||
{:else}
|
||||
<PictureInPicture size='24px' strokeWidth='2' />
|
||||
{/if}
|
||||
</Button>
|
||||
{#if false}
|
||||
<Button class='p-3 w-12 h-12' variant='ghost'>
|
||||
{#if cast}
|
||||
<Cast size='24px' fill='white' strokeWidth='2' />
|
||||
{:else}
|
||||
<Cast size='24px' strokeWidth='2' />
|
||||
{/if}
|
||||
</Button>
|
||||
{/if}
|
||||
<Button class='p-3 w-12 h-12' variant='ghost' on:click={fullscreen}>
|
||||
{#if fullscreenElement}
|
||||
<Minimize size='24px' class='p-0.5' strokeWidth='2.5' />
|
||||
{:else}
|
||||
<Maximize size='24px' class='p-0.5' strokeWidth='2.5' />
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.gradient {
|
||||
background: linear-gradient(to top, oklab(0 0 0 / 0.85) 0%, oklab(0 0 0 / 0.7) 35%, oklab(0 0 0 / 0) 100%);
|
||||
}
|
||||
|
||||
.animate-pulse-once {
|
||||
animation: pulse-once .4s linear;
|
||||
}
|
||||
|
||||
@keyframes pulse-once {
|
||||
0% {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
scale: 1.2;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
158
src/lib/components/ui/player/seekbar.svelte
Normal file
158
src/lib/components/ui/player/seekbar.svelte
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<script context='module' lang='ts'>
|
||||
type percent = number
|
||||
|
||||
interface BarSegment {
|
||||
size: percent
|
||||
text: string
|
||||
offset: percent
|
||||
scale: percent
|
||||
}
|
||||
|
||||
interface Chapter {
|
||||
start: number
|
||||
end: number
|
||||
text: string
|
||||
}
|
||||
|
||||
function clamp (value: percent) {
|
||||
return Math.min(Math.max(value, 0), 100)
|
||||
}
|
||||
|
||||
function skewclamp (value: percent) {
|
||||
const clamp = Math.min(Math.max(value, 0), 100)
|
||||
|
||||
return clamp === 0 ? -5 : clamp
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script lang='ts'>
|
||||
import { toTS } from '$lib/utils'
|
||||
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getChapterTitle } from './util'
|
||||
import type Thumbnailer from './thumbnailer'
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
seeking: null
|
||||
seeked: null
|
||||
}>()
|
||||
// state
|
||||
export let chapters: Chapter[] = []
|
||||
export let currentTime = 0
|
||||
export let duration: number
|
||||
export let buffer = 0
|
||||
|
||||
$: progress = clamp(currentTime / duration * 100)
|
||||
export let seek = 0
|
||||
|
||||
$: segments = makeSegments(chapters, duration)
|
||||
|
||||
function makeSegments (chapters: Chapter[], length: number): BarSegment[] {
|
||||
const barSegments: BarSegment[] = []
|
||||
let offset = 0
|
||||
|
||||
for (const chapter of chapters) {
|
||||
const chapterDuration = chapter.end - chapter.start
|
||||
if (chapterDuration > 0) { // Still necessary to handle empty "missing" segments.
|
||||
const size = (chapterDuration / length) * 100
|
||||
barSegments.push({
|
||||
size,
|
||||
text: chapter.text,
|
||||
offset,
|
||||
scale: 100 / size
|
||||
})
|
||||
offset += size
|
||||
}
|
||||
}
|
||||
|
||||
return barSegments
|
||||
}
|
||||
// seeking
|
||||
|
||||
let seekbar: HTMLDivElement
|
||||
|
||||
export let seeking = false
|
||||
|
||||
function calculatePositionProgress ({ pageX, currentTarget }: PointerEvent) {
|
||||
const target = currentTarget as HTMLDivElement
|
||||
const percent = clamp((pageX - target.getBoundingClientRect().left) / target.clientWidth * 100)
|
||||
if (seeking) {
|
||||
progress = percent
|
||||
}
|
||||
seek = percent
|
||||
}
|
||||
|
||||
function endHover () {
|
||||
seek = 0
|
||||
}
|
||||
|
||||
function startSeeking (e: PointerEvent) {
|
||||
seeking = true
|
||||
calculatePositionProgress(e)
|
||||
|
||||
if (e.pointerId) seekbar.setPointerCapture(e.pointerId)
|
||||
dispatch('seeking')
|
||||
}
|
||||
function endSeeking ({ pointerId }: PointerEvent) {
|
||||
seeking = false
|
||||
if (pointerId) seekbar.releasePointerCapture(pointerId)
|
||||
dispatch('seeked')
|
||||
}
|
||||
|
||||
// function checkThumbActive (progress: number, seek: number) {
|
||||
// for (const { offset, size } of segments) {
|
||||
// if (offset + size > progress) return offset + size > seek && offset < seek
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
|
||||
$: seekTime = seek * duration / 100
|
||||
|
||||
$: title = getChapterTitle(seekTime, chapters)
|
||||
|
||||
export let thumbnailer: Thumbnailer
|
||||
|
||||
$: seekIndex = Math.max(0, Math.floor(seekTime / thumbnailer.interval))
|
||||
</script>
|
||||
|
||||
<div class='w-full flex cursor-pointer relative group/seekbar touch-none' class:!cursor-grab={seeking}
|
||||
bind:this={seekbar}
|
||||
on:pointerdown={startSeeking}
|
||||
on:pointerup={endSeeking}
|
||||
on:pointermove={calculatePositionProgress}
|
||||
on:pointerleave={endHover}>
|
||||
{#each segments as chapter, i (chapter)}
|
||||
{@const { size, scale, offset } = chapter}
|
||||
{@const active = seek && seek > offset && seek < offset + size}
|
||||
<div class='w-full py-4 shrink-0 flex items-center justify-center' style:width='{size}%'>
|
||||
<div class='relative w-full h-1 flex items-center justify-center overflow-hidden rounded-[2px]' class:ml-0.5={!!i}>
|
||||
<div class='bg-[rgba(217,217,217,0.4)] absolute left-0 w-full h-0.5 transition-[height] duration-75' class:h-1={active} />
|
||||
<div class='bg-[rgba(217,217,217,0.4)] absolute left-0 w-full h-0.5 transition-[height] duration-75 transform-gpu' class:h-1={active} style:--tw-translate-x='{skewclamp(scale * (buffer - offset)) - 100}%' />
|
||||
<div class='bg-[rgba(217,217,217,0.4)] absolute left-0 w-full h-0.5 transition-[height] duration-75 transform-gpu' class:h-1={active} style:--tw-translate-x='{skewclamp(scale * (seek - offset)) - 100}%' />
|
||||
<div class='bg-white absolute w-full left-0 h-0.5 transition-[height] duration-75 transform-gpu' class:h-1={active} style:--tw-translate-x='{skewclamp(scale * (progress - offset)) - 100}%' />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{#if !seeking}
|
||||
<div class='absolute w-full transform-gpu flex pointer-events-none group-hover/seekbar:opacity-100 opacity-0 bottom-9' style:--tw-translate-x='clamp(64px, {clamp(seek)}%, calc(100% - 64px))'>
|
||||
<div class='-translate-x-1/2 text-sm leading-none text-nowrap flex flex-col justify-center items-center gap-1 rounded-lg bg-neutral-200 border-white border py-2 px-3 has-[img]:p-0 text-zinc-900 shadow-lg'>
|
||||
{#await thumbnailer.getThumbnail(seekIndex)}
|
||||
{#if title}
|
||||
<div class='max-w-24 text-ellipsis overflow-hidden'>{title}</div>
|
||||
{/if}
|
||||
<div>{toTS(seekTime)}</div>
|
||||
{:then src}
|
||||
<img {src} alt='thumbnail' class='w-40 rounded-lg min-h-10' />
|
||||
{#if title}
|
||||
<div class='max-w-24 text-ellipsis overflow-hidden absolute top-0 bg-white py-1 px-2 rounded-b-lg'>{title}</div>
|
||||
{/if}
|
||||
<div class='absolute bottom-0 bg-white py-1 px-2 rounded-t-lg'>{toTS(seekTime)}</div>
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- <div class='absolute w-full transform-gpu flex pointer-events-none top-[18px]' style:--tw-translate-x='{clamp(seeking ? seek : progress)}%'>
|
||||
<div class='transform-gpu -translate-x-1/2 -translate-y-1/2 rounded-[50%] w-3 h-3 bg-white scale-0 transition-transform group-hover/seekbar:scale-{checkThumbActive(progress, seek) ? 100 : 75}' />
|
||||
</div> -->
|
||||
</div>
|
||||
101
src/lib/components/ui/player/thumbnailer.ts
Normal file
101
src/lib/components/ui/player/thumbnailer.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
interface RenderItem {
|
||||
index: number
|
||||
run: () => void
|
||||
promise: Promise<string>
|
||||
}
|
||||
|
||||
export default class Thumbnailer {
|
||||
video = document.createElement('video')
|
||||
canvas = new OffscreenCanvas(0, 0)
|
||||
ctx = this.canvas.getContext('2d')!
|
||||
thumbnails: string[] = []
|
||||
size = 800
|
||||
interval = 10
|
||||
currentTask: RenderItem | undefined
|
||||
nextTask: RenderItem | undefined
|
||||
src
|
||||
|
||||
constructor (src: string) {
|
||||
this.video.preload = 'none'
|
||||
this.video.src = this.src = src
|
||||
this.video.load()
|
||||
this.video.playbackRate = 0
|
||||
this.video.muted = true
|
||||
}
|
||||
|
||||
setVideo (currentVideo: HTMLVideoElement) {
|
||||
currentVideo.addEventListener('timeupdate', () => {
|
||||
const index = Math.floor(currentVideo.currentTime / this.interval)
|
||||
const thumbnail = this.thumbnails[index]
|
||||
if (!thumbnail) this._paintThumbnail(currentVideo, index)
|
||||
})
|
||||
}
|
||||
|
||||
_createTask (index: number): RenderItem {
|
||||
const { promise, resolve } = Promise.withResolvers<string>()
|
||||
|
||||
const run = () => {
|
||||
this.video.requestVideoFrameCallback((_now, meta) => {
|
||||
resolve(this._paintThumbnail(this.video, index, meta.width, meta.height))
|
||||
this.video.currentTime = 0
|
||||
this.currentTask = undefined
|
||||
if (this.nextTask) {
|
||||
this.currentTask = this.nextTask
|
||||
this.nextTask = undefined
|
||||
this.currentTask.run()
|
||||
}
|
||||
})
|
||||
this.video.currentTime = index * this.interval
|
||||
}
|
||||
|
||||
return { index, run, promise }
|
||||
}
|
||||
|
||||
// get a task or create one to create a thumbnail
|
||||
// don't touch currently running task, overwrite next task
|
||||
_createThumbnail (index: number) {
|
||||
if (!this.currentTask) {
|
||||
this.currentTask = this._createTask(index)
|
||||
this.currentTask.run()
|
||||
return this.currentTask.promise
|
||||
}
|
||||
|
||||
if (index === this.currentTask.index) return this.currentTask.promise
|
||||
|
||||
if (!this.nextTask) {
|
||||
this.nextTask = this._createTask(index)
|
||||
return this.nextTask.promise
|
||||
}
|
||||
|
||||
if (index === this.nextTask.index) return this.nextTask.promise
|
||||
|
||||
this.nextTask = this._createTask(index)
|
||||
return this.nextTask.promise
|
||||
}
|
||||
|
||||
// generate and store the thumbnail
|
||||
async _paintThumbnail (video: HTMLVideoElement, index: number, width = video.videoWidth, height = video.videoHeight) {
|
||||
this.canvas.width = this.size
|
||||
this.canvas.height = height / width * this.size
|
||||
this.ctx.drawImage(video, 0, 0, this.canvas.width, this.canvas.height)
|
||||
this.thumbnails[index] = URL.createObjectURL(await this.canvas.convertToBlob({ type: 'image/webp', quality: 0.6 }))
|
||||
return this.thumbnails[index]
|
||||
}
|
||||
|
||||
async getThumbnail (index: number): Promise<string> {
|
||||
const thumbnail = this.thumbnails[index]
|
||||
if (thumbnail) return thumbnail
|
||||
|
||||
return await this._createThumbnail(index)
|
||||
}
|
||||
|
||||
updateSource (src: string) {
|
||||
if (src === this.src) return
|
||||
for (const thumbnail of this.thumbnails) URL.revokeObjectURL(thumbnail)
|
||||
this.thumbnails = []
|
||||
this.currentTask = undefined
|
||||
this.nextTask = undefined
|
||||
this.video.src = this.src = src
|
||||
this.video.load()
|
||||
}
|
||||
}
|
||||
167
src/lib/components/ui/player/util.ts
Normal file
167
src/lib/components/ui/player/util.ts
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import type { Media } from '$lib/modules/anilist'
|
||||
import type { Track } from '../../../../app'
|
||||
|
||||
export interface Chapter {
|
||||
start: number
|
||||
end: number
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface SessionMetadata {
|
||||
title: string
|
||||
description: string
|
||||
image: string
|
||||
}
|
||||
|
||||
export interface MediaInfo {
|
||||
url: string
|
||||
media: Media
|
||||
episode: number
|
||||
forced: boolean
|
||||
session: SessionMetadata
|
||||
}
|
||||
|
||||
export function getChapterTitle (time: number, chapters: Chapter[]): string | false {
|
||||
for (const { start, end, text } of chapters) {
|
||||
if (end > time) return start <= time && text
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
interface Interval {
|
||||
startTime: number
|
||||
endTime: number
|
||||
}
|
||||
|
||||
interface Result {
|
||||
interval: Interval
|
||||
skipType: string
|
||||
skipId: string
|
||||
episodeLength: number
|
||||
}
|
||||
|
||||
interface AniSkip {
|
||||
found: boolean
|
||||
results: Result[]
|
||||
message: string
|
||||
statusCode: number
|
||||
}
|
||||
|
||||
export async function getChaptersAniSkip (idMal: number, episode: number, duration: number) {
|
||||
const resAccurate = await fetch(`https://api.aniskip.com/v2/skip-times/${idMal}/${episode}/?episodeLength=${duration}&types=op&types=ed&types=recap`)
|
||||
const jsonAccurate = await resAccurate.json() as AniSkip
|
||||
|
||||
const resRough = await fetch(`https://api.aniskip.com/v2/skip-times/${idMal}/${episode}/?episodeLength=0&types=op&types=ed&types=recap`)
|
||||
const jsonRough = await resRough.json() as AniSkip
|
||||
|
||||
const map: Record<string, Result> = {}
|
||||
for (const result of [...jsonAccurate.results, ...jsonRough.results]) {
|
||||
if (!(result.skipType in map)) map[result.skipType] = result
|
||||
}
|
||||
|
||||
const results = Object.values(map)
|
||||
if (!results.length) return []
|
||||
|
||||
const chapters = results.map(result => {
|
||||
const diff = duration - result.episodeLength
|
||||
return {
|
||||
start: (result.interval.startTime + diff) * 1000,
|
||||
end: (result.interval.endTime + diff) * 1000,
|
||||
text: result.skipType.toUpperCase()
|
||||
}
|
||||
})
|
||||
const ed = chapters.find(({ text }) => text === 'ED')
|
||||
const recap = chapters.find(({ text }) => text === 'RECAP')
|
||||
if (recap) recap.text = 'Recap'
|
||||
|
||||
chapters.sort((a, b) => a.start - b.start)
|
||||
if ((chapters[0]!.start | 0) !== 0) {
|
||||
chapters.unshift({ start: 0, end: chapters[0]!.start, text: chapters[0]!.text === 'OP' ? 'Intro' : 'Episode' })
|
||||
}
|
||||
if (ed) {
|
||||
if ((ed.end | 0) + 5000 - duration * 1000 < 0) {
|
||||
chapters.push({ start: ed.end, end: duration * 1000, text: 'Preview' })
|
||||
}
|
||||
} else if ((chapters[chapters.length - 1]!.end | 0) + 5000 - duration * 1000 < 0) {
|
||||
chapters.push({
|
||||
start: chapters[chapters.length - 1]!.end,
|
||||
end: duration * 1000,
|
||||
text: 'Episode'
|
||||
})
|
||||
}
|
||||
|
||||
for (let i = 0, len = chapters.length - 2; i <= len; ++i) {
|
||||
const current = chapters[i]
|
||||
const next = chapters[i + 1]
|
||||
if ((current!.end | 0) !== (next!.start | 0)) {
|
||||
chapters.push({
|
||||
start: current!.end,
|
||||
end: next!.start,
|
||||
text: 'Episode'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
chapters.sort((a, b) => a.start - b.start)
|
||||
|
||||
return chapters
|
||||
}
|
||||
|
||||
export function sanitizeChapters (chapters: Chapter[], length: number): Chapter[] {
|
||||
if (length <= 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const sanitizedChapters: Chapter[] = []
|
||||
let currentTime = 0
|
||||
|
||||
const sortedChapters = chapters.map(chapter => {
|
||||
const end = Math.min(length, chapter.end)
|
||||
const start = Math.min(Math.max(0, chapter.start), end)
|
||||
return { start, end, text: chapter.text }
|
||||
}).sort((a, b) => a.start - b.start)
|
||||
|
||||
for (const chapter of sortedChapters) {
|
||||
// Handle Missing Segment Before Chapter
|
||||
if (chapter.start > currentTime) {
|
||||
sanitizedChapters.push({
|
||||
start: currentTime,
|
||||
end: chapter.start,
|
||||
text: sanitizedChapters.length === 0 ? '' : 'Episode'
|
||||
})
|
||||
}
|
||||
|
||||
sanitizedChapters.push(chapter)
|
||||
currentTime = chapter.end
|
||||
}
|
||||
|
||||
// Handle Missing Segment After Last Chapter
|
||||
if (currentTime < length) {
|
||||
sanitizedChapters.push({
|
||||
start: currentTime,
|
||||
end: length,
|
||||
text: ''
|
||||
})
|
||||
}
|
||||
|
||||
return sanitizedChapters
|
||||
}
|
||||
|
||||
export function normalizeTracks (_tracks: Track[]) {
|
||||
const tracks = [..._tracks]
|
||||
const hasEng = tracks.some(track => track.language === 'eng' || track.language === 'en')
|
||||
const lang = tracks.map(({ id, language, label, enabled, selected }) => {
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
enabled: enabled ?? selected,
|
||||
id,
|
||||
language: language || !hasEng ? 'eng' : 'unk',
|
||||
label
|
||||
}
|
||||
})
|
||||
return lang.reduce<Record<string, typeof lang>>((acc, track) => {
|
||||
if (!acc[track.language]) acc[track.language] = []
|
||||
acc[track.language]!.push(track)
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
28
src/lib/components/ui/tree/context.ts
Normal file
28
src/lib/components/ui/tree/context.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { getContext, setContext } from 'svelte'
|
||||
import { writable, get, type Writable } from 'svelte/store'
|
||||
|
||||
const MENU_CONTEXT = 'MENU_CONTEXT'
|
||||
const LEVEL_CONTEXT = 'LEVEL_CONTEXT'
|
||||
|
||||
export function createRootContext () {
|
||||
const state: Writable<string[]> = writable([])
|
||||
|
||||
setContext(LEVEL_CONTEXT, writable(-1))
|
||||
return setContext(MENU_CONTEXT, {
|
||||
state,
|
||||
setActive: (id: string, level: number) => state.update(s => s.slice(0, level).concat(id)),
|
||||
setInactive: (level: number) => state.update(s => s.slice(0, level))
|
||||
})
|
||||
}
|
||||
|
||||
export function getMenuContext () {
|
||||
return getContext<ReturnType<typeof createRootContext>>(MENU_CONTEXT)
|
||||
}
|
||||
|
||||
export function getLevelContext () {
|
||||
return getContext<Writable<number>>(LEVEL_CONTEXT)
|
||||
}
|
||||
|
||||
export function createChildLevel () {
|
||||
return setContext(LEVEL_CONTEXT, writable(get(getLevelContext()) + 1))
|
||||
}
|
||||
3
src/lib/components/ui/tree/index.ts
Normal file
3
src/lib/components/ui/tree/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export { default as Root } from './root.svelte'
|
||||
export { default as Sub } from './sub.svelte'
|
||||
export { default as Item } from './item.svelte'
|
||||
47
src/lib/components/ui/tree/item.svelte
Normal file
47
src/lib/components/ui/tree/item.svelte
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<script lang='ts'>
|
||||
import { ChevronRight } from 'svelte-radix'
|
||||
import { getMenuContext, getLevelContext } from './context.ts'
|
||||
|
||||
const { state, setActive, setInactive } = getMenuContext()
|
||||
const levelStore = getLevelContext()
|
||||
|
||||
export let id: string = crypto.randomUUID()
|
||||
|
||||
export let active = false
|
||||
$: level = $levelStore
|
||||
$: isActive = $state[level] === id
|
||||
$: showSubmenu = isActive
|
||||
|
||||
$: activeSibling = $state[level]
|
||||
|
||||
$: hasSub = $$slots.trigger
|
||||
|
||||
function handleClick () {
|
||||
if (hasSub) {
|
||||
isActive ? setInactive(level) : setActive(id, level)
|
||||
} else {
|
||||
setInactive(level)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class='relative'>
|
||||
<button class='w-full hover:bg-accent hover:text-accent-foreground flex select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none pl-8 cursor-pointer'
|
||||
on:click={handleClick}
|
||||
on:click
|
||||
class:bg-accent={isActive || active} class:text-accent-foreground={isActive || active}
|
||||
class:opacity-30={activeSibling}
|
||||
data-open={isActive}>
|
||||
{#if hasSub}
|
||||
<slot name='trigger' />
|
||||
<ChevronRight class='ml-auto h-4 w-4' />
|
||||
{:else}
|
||||
<slot />
|
||||
{/if}
|
||||
|
||||
</button>
|
||||
|
||||
{#if showSubmenu && hasSub}
|
||||
<slot />
|
||||
{/if}
|
||||
</div>
|
||||
13
src/lib/components/ui/tree/menu.svelte
Normal file
13
src/lib/components/ui/tree/menu.svelte
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<script lang='ts'>
|
||||
import { cn } from '$lib/utils'
|
||||
import type { HTMLAttributes } from 'svelte/elements'
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>
|
||||
|
||||
let className: $$Props['class'] = ''
|
||||
export { className as class }
|
||||
</script>
|
||||
|
||||
<div class={cn('relative w-64 bg-black text-popover-foreground rounded-md z-50 border p-1 shadow-md focus:outline-none has-[>[data-open=true]]:bg-black/30', className)} {...$$restProps}>
|
||||
<slot />
|
||||
</div>
|
||||
16
src/lib/components/ui/tree/root.svelte
Normal file
16
src/lib/components/ui/tree/root.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<script lang='ts'>
|
||||
import { createRootContext, createChildLevel } from './context.ts'
|
||||
import Menu from './menu.svelte'
|
||||
export const { state } = createRootContext()
|
||||
|
||||
createChildLevel()
|
||||
|
||||
function close (fn: () => void) {
|
||||
fn()
|
||||
return () => state.set([])
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu style='margin-left: -{$state.length * 528}px' class='transition-[margin-left]'>
|
||||
<slot {close} />
|
||||
</Menu>
|
||||
10
src/lib/components/ui/tree/sub.svelte
Normal file
10
src/lib/components/ui/tree/sub.svelte
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<script lang='ts'>
|
||||
import { createChildLevel } from './context.ts'
|
||||
import Menu from './menu.svelte'
|
||||
|
||||
createChildLevel()
|
||||
</script>
|
||||
|
||||
<Menu class='absolute right-[calc(-100%-1.25rem)] top-[calc(-1px-0.25rem)]'>
|
||||
<slot />
|
||||
</Menu>
|
||||
|
|
@ -11,7 +11,7 @@ export default expose({
|
|||
async load (code: string): Promise<TorrentSource> {
|
||||
// WARN: unsafe eval
|
||||
const url = URL.createObjectURL(new Blob([code], { type: 'application/javascript' }))
|
||||
const module = await import(url)
|
||||
const module = await import(/* @vite-ignore */url)
|
||||
URL.revokeObjectURL(url)
|
||||
return module.default
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,9 +20,7 @@ globalThis.authAL ??= (url: string) => {
|
|||
}
|
||||
|
||||
globalThis.restart ??= async () => location.reload()
|
||||
globalThis.openURL ??= async (url: string) => {
|
||||
open(url)
|
||||
}
|
||||
globalThis.openURL ??= async (url: string) => { open(url) }
|
||||
globalThis.selectPlayer ??= async () => 'mpv'
|
||||
globalThis.selectDownload ??= async () => '/tmp/webtorrent'
|
||||
globalThis.share ??= (...args) => navigator.share(...args)
|
||||
|
|
@ -36,6 +34,11 @@ globalThis.maximise ??= async () => undefined
|
|||
globalThis.close ??= async () => undefined
|
||||
globalThis.checkUpdate ??= async () => undefined
|
||||
globalThis.toggleDiscordDetails ??= async () => undefined
|
||||
// TODO: chapter info?
|
||||
globalThis.setMediaSession ??= async (metadata) => { navigator.mediaSession.metadata = new MediaMetadata({ title: metadata.title, artist: metadata.description, artwork: [{ src: metadata.image }] }) }
|
||||
globalThis.setPositionState ??= async e => navigator.mediaSession.setPositionState(e)
|
||||
globalThis.setPlayBackState ??= async e => { navigator.mediaSession.playbackState = e }
|
||||
globalThis.setActionHandler ??= async (...args) => navigator.mediaSession.setActionHandler(...args)
|
||||
|
||||
const native = globalThis as Native
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { type ClassValue, clsx } from 'clsx'
|
|||
import { twMerge } from 'tailwind-merge'
|
||||
import { cubicOut } from 'svelte/easing'
|
||||
import type { TransitionConfig } from 'svelte/transition'
|
||||
import { readable } from 'simple-store-svelte'
|
||||
import { readable, type Writable } from 'simple-store-svelte'
|
||||
|
||||
export function cn (...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
|
|
@ -122,3 +122,35 @@ export function fastPrettyBytes (num: number) {
|
|||
const exponent = Math.min(Math.floor(Math.log(num) / Math.log(1000)), units.length - 1)
|
||||
return Number((num / Math.pow(1000, exponent)).toFixed(2)) + units[exponent]!
|
||||
}
|
||||
|
||||
export function toTS (sec: number, full?: number) {
|
||||
if (isNaN(sec) || sec < 0) {
|
||||
switch (full) {
|
||||
case 1:
|
||||
return '0:00:00.00'
|
||||
case 2:
|
||||
return '0:00:00'
|
||||
case 3:
|
||||
return '00:00'
|
||||
default:
|
||||
return '0:00'
|
||||
}
|
||||
}
|
||||
const hours = Math.floor(sec / 3600)
|
||||
let minutes: string | number = Math.floor(sec / 60) - hours * 60
|
||||
let seconds: string | number = full === 1 ? (sec % 60).toFixed(2) : Math.floor(sec % 60)
|
||||
if (minutes < 10 && (hours > 0 || full)) minutes = '0' + minutes
|
||||
if (Number(seconds) < 10) seconds = '0' + seconds
|
||||
return (hours > 0 || full === 1 || full === 2) ? hours + ':' + minutes + ':' + seconds : minutes + ':' + seconds
|
||||
}
|
||||
|
||||
export function bindPiP (doc: Document, store: Writable<HTMLVideoElement | null>) {
|
||||
const signal = new AbortController()
|
||||
doc.addEventListener('enterpictureinpicture', (e) => {
|
||||
store.set(e.target as HTMLVideoElement)
|
||||
}, { signal: signal.signal })
|
||||
doc.addEventListener('leavepictureinpicture', () => {
|
||||
store.set(null)
|
||||
}, { signal: signal.signal })
|
||||
return { destroy: () => signal.abort() }
|
||||
}
|
||||
|
|
|
|||
46
src/routes/player/+page.svelte
Normal file
46
src/routes/player/+page.svelte
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<script lang='ts'>
|
||||
import { bannerSrc, hideBanner } from '$lib/components/ui/banner'
|
||||
import { Player } from '$lib/components/ui/player'
|
||||
import type { MediaInfo } from '$lib/components/ui/player/util'
|
||||
import { banner, client, title } from '$lib/modules/anilist'
|
||||
import { IDMedia } from '$lib/modules/anilist/queries'
|
||||
import { onMount, tick } from 'svelte'
|
||||
|
||||
onMount(async () => {
|
||||
await tick()
|
||||
hideBanner.value = true
|
||||
})
|
||||
|
||||
const mediaInfo: PromiseLike<MediaInfo> = client.client.query(IDMedia, { id: 176642 }, { requestPolicy: 'cache-first' }).then(v => {
|
||||
const media = v.data!.Media!
|
||||
bannerSrc.value = media
|
||||
|
||||
return {
|
||||
url: '/video.mkv',
|
||||
episode: 6,
|
||||
media,
|
||||
forced: false,
|
||||
session: {
|
||||
title: title(media),
|
||||
description: 'Episode 6 - Fierce Blazing Finale',
|
||||
image: banner(media) ?? ''
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div class='px-3 w-full h-full py-12 gap-4 content-center text-webkit-center'>
|
||||
{#await mediaInfo then mediaInfo}
|
||||
<Player {mediaInfo} />
|
||||
{/await}
|
||||
<!-- <div class='w-60 shrink-0'>
|
||||
Episode list
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.text-webkit-center {
|
||||
text-align: -webkit-center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -3,6 +3,10 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
|||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
onwarn: (warning, handler) => {
|
||||
if (warning.code === 'a11y-media-has-caption') return
|
||||
handler(warning)
|
||||
},
|
||||
preprocess: vitePreprocess({}),
|
||||
kit: {
|
||||
adapter: adapter({ fallback: 'index.html' }),
|
||||
|
|
|
|||
|
|
@ -4,9 +4,32 @@ import plugin from 'tailwindcss/plugin'
|
|||
|
||||
const config: Config = {
|
||||
plugins: [
|
||||
plugin (({ addVariant }) => {
|
||||
plugin(({ addVariant, matchVariant }) => {
|
||||
addVariant('select', ['&:hover', '&:focus-visible', '&:active'])
|
||||
addVariant('group-select', [':merge(.group):hover &', ':merge(.group):focus-visible &', ':merge(.group):active &'])
|
||||
addVariant('fullscreen', '&:fullscreen')
|
||||
addVariant('group-fullscreen', ':merge(.group):fullscreen &')
|
||||
matchVariant(
|
||||
'group-fullscreen',
|
||||
(value, { modifier }) => [
|
||||
':merge(.group):fullscreen &',
|
||||
`:merge(.group\\/${modifier}):fullscreen &`
|
||||
],
|
||||
{ values: { DEFAULT: undefined } }
|
||||
)
|
||||
matchVariant(
|
||||
'group-select',
|
||||
(value, { modifier }) => [
|
||||
':merge(.group):hover &',
|
||||
`:merge(.group\\/${modifier}):hover &`,
|
||||
':merge(.group):focus-visible &',
|
||||
`:merge(.group\\/${modifier}):focus-visible &`,
|
||||
':merge(.group):active &',
|
||||
`:merge(.group\\/${modifier}):active &`
|
||||
],
|
||||
{ values: { DEFAULT: undefined } }
|
||||
)
|
||||
addVariant('mobile', '@media (pointer: none), (pointer: coarse)')
|
||||
})
|
||||
],
|
||||
darkMode: ['class'],
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
]
|
||||
},
|
||||
"include": [
|
||||
"tailwind.config.ts",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.svelte"
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@ import { defineConfig } from 'vite'
|
|||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
server: { port: 7344 },
|
||||
build: {
|
||||
target: 'esnext'
|
||||
},
|
||||
ssr: {
|
||||
target: "webworker"
|
||||
target: 'webworker'
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: ['anitomyscript']
|
||||
|
|
|
|||
Loading…
Reference in a new issue