aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatarLarge Libravatar memdmp <memdmpestrogenzone>2025-07-31 22:48:01 +0200
committerLibravatarLarge Libravatar memdmp <memdmpestrogenzone>2025-07-31 22:48:01 +0200
commite55f4c2fe8a6e1d62a0b005777b46c80e360d37e (patch)
tree1f9ad00b914f04677ffaf8b395a4c5d4ff659756
downloadvideotool-e55f4c2fe8a6e1d62a0b005777b46c80e360d37e.tar.gz
videotool-e55f4c2fe8a6e1d62a0b005777b46c80e360d37e.tar.bz2
videotool-e55f4c2fe8a6e1d62a0b005777b46c80e360d37e.tar.lz
videotool-e55f4c2fe8a6e1d62a0b005777b46c80e360d37e.zip

feat: initial commit

-rw-r--r--.gitignore23
-rw-r--r--.npmrc1
-rw-r--r--.prettierignore9
-rw-r--r--.prettierrc16
-rw-r--r--README.md38
-rw-r--r--deno.json21
-rw-r--r--deno.lock1036
-rw-r--r--import_map.json9
-rw-r--r--package.json24
-rw-r--r--pnpm-lock.yaml1359
-rw-r--r--src/app.css1
-rw-r--r--src/app.d.ts13
-rw-r--r--src/app.html14
-rw-r--r--src/lib/Player/FrameSlider.svelte67
-rw-r--r--src/lib/Player/Keybinds.svelte47
-rw-r--r--src/lib/Player/Player.svelte161
-rw-r--r--src/lib/Player/Video.ts55
-rw-r--r--src/lib/Renderer/Renderer.svelte146
-rw-r--r--src/lib/assets/favicon.svg1
-rw-r--r--src/lib/index.ts1
-rw-r--r--src/lib/vendor/svelte-range-slider/README1
-rw-r--r--src/lib/vendor/svelte-range-slider/range-pips.svelte303
-rw-r--r--src/lib/vendor/svelte-range-slider/range-slider.svelte1026
-rw-r--r--src/routes/+layout.svelte12
-rw-r--r--src/routes/+page.svelte6
-rw-r--r--src/routes/ffmpeg-test/+page.svelte45
-rw-r--r--src/routes/render/+page.svelte6
-rw-r--r--src/user/Sneky Snitch.mp4bin0 -> 1620945 bytes
-rw-r--r--src/user/index.ts21
-rw-r--r--static/robots.txt3
-rw-r--r--svelte.config.js30
-rw-r--r--tsconfig.json19
-rw-r--r--vite.config.ts23
33 files changed, 4537 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3b462cb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+node_modules
+
+# Output
+.output
+.vercel
+.netlify
+.wrangler
+/.svelte-kit
+/build
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Env
+.env
+.env.*
+!.env.example
+!.env.test
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..b6f27f1
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..7d74fe2
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,9 @@
+# Package Managers
+package-lock.json
+pnpm-lock.yaml
+yarn.lock
+bun.lock
+bun.lockb
+
+# Miscellaneous
+/static/
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..8103a0b
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,16 @@
+{
+ "useTabs": true,
+ "singleQuote": true,
+ "trailingComma": "none",
+ "printWidth": 100,
+ "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
+ "overrides": [
+ {
+ "files": "*.svelte",
+ "options": {
+ "parser": "svelte"
+ }
+ }
+ ],
+ "tailwindStylesheet": "./src/app.css"
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..75842c4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,38 @@
+# sv
+
+Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
+
+## Creating a project
+
+If you're seeing this, you've probably already done this step. Congrats!
+
+```sh
+# create a new project in the current directory
+npx sv create
+
+# create a new project in my-app
+npx sv create my-app
+```
+
+## Developing
+
+Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
+
+```sh
+npm run dev
+
+# or start the server and open the app in a new browser tab
+npm run dev -- --open
+```
+
+## Building
+
+To create a production version of your app:
+
+```sh
+npm run build
+```
+
+You can preview the production build with `npm run preview`.
+
+> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
diff --git a/deno.json b/deno.json
new file mode 100644
index 0000000..e4ac711
--- /dev/null
+++ b/deno.json
@@ -0,0 +1,21 @@
+{
+ "version": "0.0.1",
+ "tasks": {
+ "dev": "deno run --allow-net --allow-read --allow-write=. --deny-read=/proc/version --allow-env --allow-run --allow-sys=homedir,cpus,networkInterfaces,hostname,osRelease,uid --allow-ffi --unstable-cron --unstable-kv --unstable-bare-node-builtins --unstable-byonm npm:vite",
+ "build": "deno run --allow-net --allow-read --allow-write=. --deny-read=/proc/version --allow-env --allow-run --allow-sys=homedir,cpus,networkInterfaces,hostname,osRelease,uid --allow-ffi --unstable-cron --unstable-kv --unstable-bare-node-builtins --unstable-byonm npm:vite build",
+ "preview": "deno run --allow-net --allow-read=. --deny-read=/proc/version --allow-env --allow-sys=homedir,cpus,networkInterfaces,hostname,osRelease,uid --allow-ffi --unstable-cron --unstable-kv --unstable-bare-node-builtins --unstable-byonm npm:vite preview",
+ "serve": "deno run --allow-net --allow-read=. --deny-read=/proc/version main.ts"
+ },
+ "importMap": "./import_map.json",
+ "license": "AGPL-3.0-or-later",
+ "fmt": {
+ "singleQuote": true
+ },
+ "compilerOptions": {
+ "checkJs": true,
+ "strict": true
+ },
+ "unstable": [
+ "sloppy-imports"
+ ]
+} \ No newline at end of file
diff --git a/deno.lock b/deno.lock
new file mode 100644
index 0000000..df9d81e
--- /dev/null
+++ b/deno.lock
@@ -0,0 +1,1036 @@
+{
+ "version": "5",
+ "specifiers": {
+ "jsr:@memdmp/keyframegen@*": "0.2.1",
+ "jsr:@memdmp/timelinecalc@0.1": "0.1.2",
+ "npm:@sveltejs/adapter-static@3.0.8": "3.0.8_@sveltejs+kit@2.19.0__@sveltejs+vite-plugin-svelte@5.0.3___svelte@5.23.0____acorn@8.15.0___vite@6.2.1__svelte@5.23.0___acorn@8.15.0__vite@6.2.1_@sveltejs+vite-plugin-svelte@5.0.3__svelte@5.23.0___acorn@8.15.0__vite@6.2.1_svelte@5.23.0__acorn@8.15.0_vite@6.2.1",
+ "npm:@sveltejs/kit@2.19.0": "2.19.0_@sveltejs+vite-plugin-svelte@5.0.3__svelte@5.23.0___acorn@8.15.0__vite@6.2.1_svelte@5.23.0__acorn@8.15.0_vite@6.2.1",
+ "npm:@sveltejs/vite-plugin-svelte@5.0.3": "5.0.3_svelte@5.23.0__acorn@8.15.0_vite@6.2.1",
+ "npm:@tailwindcss/vite@*": "4.1.11_vite@6.2.1",
+ "npm:@tailwindcss/vite@4.1.11": "4.1.11_vite@6.2.1",
+ "npm:esbuild@0.25.1": "0.25.1",
+ "npm:prettier-plugin-svelte@3.3.3": "3.3.3_prettier@3.5.3_svelte@5.23.0__acorn@8.15.0",
+ "npm:prettier@3.5.3": "3.5.3",
+ "npm:svelte-check@4.1.5": "4.1.5_svelte@5.23.0__acorn@8.15.0_typescript@5.8.2",
+ "npm:svelte@5.23.0": "5.23.0_acorn@8.15.0",
+ "npm:tailwindcss@4.0.13": "4.0.13",
+ "npm:typescript@5.8.2": "5.8.2",
+ "npm:vite@6.2.1": "6.2.1"
+ },
+ "jsr": {
+ "@memdmp/keyframegen@0.2.1": {
+ "integrity": "d0a49ed597422507acb1429f36bec463861b7b48846b7a89564a5b553aa368b7",
+ "dependencies": [
+ "jsr:@memdmp/timelinecalc"
+ ]
+ },
+ "@memdmp/timelinecalc@0.1.2": {
+ "integrity": "edd60ee8d1bbf42d8dad10bbea3a7f7c4e72481bdbc15a1d32e05065fdf3af39"
+ }
+ },
+ "npm": {
+ "@ampproject/remapping@2.3.0": {
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dependencies": [
+ "@jridgewell/gen-mapping",
+ "@jridgewell/trace-mapping"
+ ]
+ },
+ "@emnapi/core@1.4.5": {
+ "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==",
+ "dependencies": [
+ "@emnapi/wasi-threads",
+ "tslib"
+ ]
+ },
+ "@emnapi/runtime@1.4.5": {
+ "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
+ "dependencies": [
+ "tslib"
+ ]
+ },
+ "@emnapi/wasi-threads@1.0.4": {
+ "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==",
+ "dependencies": [
+ "tslib"
+ ]
+ },
+ "@esbuild/aix-ppc64@0.25.1": {
+ "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==",
+ "os": ["aix"],
+ "cpu": ["ppc64"]
+ },
+ "@esbuild/aix-ppc64@0.25.8": {
+ "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
+ "os": ["aix"],
+ "cpu": ["ppc64"]
+ },
+ "@esbuild/android-arm64@0.25.1": {
+ "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==",
+ "os": ["android"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/android-arm64@0.25.8": {
+ "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
+ "os": ["android"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/android-arm@0.25.1": {
+ "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==",
+ "os": ["android"],
+ "cpu": ["arm"]
+ },
+ "@esbuild/android-arm@0.25.8": {
+ "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
+ "os": ["android"],
+ "cpu": ["arm"]
+ },
+ "@esbuild/android-x64@0.25.1": {
+ "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==",
+ "os": ["android"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/android-x64@0.25.8": {
+ "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
+ "os": ["android"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/darwin-arm64@0.25.1": {
+ "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==",
+ "os": ["darwin"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/darwin-arm64@0.25.8": {
+ "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
+ "os": ["darwin"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/darwin-x64@0.25.1": {
+ "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==",
+ "os": ["darwin"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/darwin-x64@0.25.8": {
+ "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
+ "os": ["darwin"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/freebsd-arm64@0.25.1": {
+ "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==",
+ "os": ["freebsd"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/freebsd-arm64@0.25.8": {
+ "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
+ "os": ["freebsd"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/freebsd-x64@0.25.1": {
+ "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==",
+ "os": ["freebsd"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/freebsd-x64@0.25.8": {
+ "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
+ "os": ["freebsd"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/linux-arm64@0.25.1": {
+ "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==",
+ "os": ["linux"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/linux-arm64@0.25.8": {
+ "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
+ "os": ["linux"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/linux-arm@0.25.1": {
+ "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==",
+ "os": ["linux"],
+ "cpu": ["arm"]
+ },
+ "@esbuild/linux-arm@0.25.8": {
+ "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
+ "os": ["linux"],
+ "cpu": ["arm"]
+ },
+ "@esbuild/linux-ia32@0.25.1": {
+ "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==",
+ "os": ["linux"],
+ "cpu": ["ia32"]
+ },
+ "@esbuild/linux-ia32@0.25.8": {
+ "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
+ "os": ["linux"],
+ "cpu": ["ia32"]
+ },
+ "@esbuild/linux-loong64@0.25.1": {
+ "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==",
+ "os": ["linux"],
+ "cpu": ["loong64"]
+ },
+ "@esbuild/linux-loong64@0.25.8": {
+ "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
+ "os": ["linux"],
+ "cpu": ["loong64"]
+ },
+ "@esbuild/linux-mips64el@0.25.1": {
+ "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==",
+ "os": ["linux"],
+ "cpu": ["mips64el"]
+ },
+ "@esbuild/linux-mips64el@0.25.8": {
+ "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
+ "os": ["linux"],
+ "cpu": ["mips64el"]
+ },
+ "@esbuild/linux-ppc64@0.25.1": {
+ "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==",
+ "os": ["linux"],
+ "cpu": ["ppc64"]
+ },
+ "@esbuild/linux-ppc64@0.25.8": {
+ "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
+ "os": ["linux"],
+ "cpu": ["ppc64"]
+ },
+ "@esbuild/linux-riscv64@0.25.1": {
+ "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==",
+ "os": ["linux"],
+ "cpu": ["riscv64"]
+ },
+ "@esbuild/linux-riscv64@0.25.8": {
+ "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
+ "os": ["linux"],
+ "cpu": ["riscv64"]
+ },
+ "@esbuild/linux-s390x@0.25.1": {
+ "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==",
+ "os": ["linux"],
+ "cpu": ["s390x"]
+ },
+ "@esbuild/linux-s390x@0.25.8": {
+ "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
+ "os": ["linux"],
+ "cpu": ["s390x"]
+ },
+ "@esbuild/linux-x64@0.25.1": {
+ "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==",
+ "os": ["linux"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/linux-x64@0.25.8": {
+ "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
+ "os": ["linux"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/netbsd-arm64@0.25.1": {
+ "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==",
+ "os": ["netbsd"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/netbsd-arm64@0.25.8": {
+ "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
+ "os": ["netbsd"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/netbsd-x64@0.25.1": {
+ "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==",
+ "os": ["netbsd"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/netbsd-x64@0.25.8": {
+ "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
+ "os": ["netbsd"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/openbsd-arm64@0.25.1": {
+ "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==",
+ "os": ["openbsd"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/openbsd-arm64@0.25.8": {
+ "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
+ "os": ["openbsd"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/openbsd-x64@0.25.1": {
+ "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==",
+ "os": ["openbsd"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/openbsd-x64@0.25.8": {
+ "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
+ "os": ["openbsd"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/openharmony-arm64@0.25.8": {
+ "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
+ "os": ["openharmony"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/sunos-x64@0.25.1": {
+ "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==",
+ "os": ["sunos"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/sunos-x64@0.25.8": {
+ "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
+ "os": ["sunos"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/win32-arm64@0.25.1": {
+ "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==",
+ "os": ["win32"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/win32-arm64@0.25.8": {
+ "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
+ "os": ["win32"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/win32-ia32@0.25.1": {
+ "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==",
+ "os": ["win32"],
+ "cpu": ["ia32"]
+ },
+ "@esbuild/win32-ia32@0.25.8": {
+ "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
+ "os": ["win32"],
+ "cpu": ["ia32"]
+ },
+ "@esbuild/win32-x64@0.25.1": {
+ "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==",
+ "os": ["win32"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/win32-x64@0.25.8": {
+ "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
+ "os": ["win32"],
+ "cpu": ["x64"]
+ },
+ "@isaacs/fs-minipass@4.0.1": {
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "dependencies": [
+ "minipass"
+ ]
+ },
+ "@jridgewell/gen-mapping@0.3.12": {
+ "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==",
+ "dependencies": [
+ "@jridgewell/sourcemap-codec",
+ "@jridgewell/trace-mapping"
+ ]
+ },
+ "@jridgewell/resolve-uri@3.1.2": {
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
+ },
+ "@jridgewell/sourcemap-codec@1.5.4": {
+ "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="
+ },
+ "@jridgewell/trace-mapping@0.3.29": {
+ "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==",
+ "dependencies": [
+ "@jridgewell/resolve-uri",
+ "@jridgewell/sourcemap-codec"
+ ]
+ },
+ "@napi-rs/wasm-runtime@0.2.12": {
+ "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
+ "dependencies": [
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util@0.10.0"
+ ]
+ },
+ "@polka/url@1.0.0-next.29": {
+ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="
+ },
+ "@rollup/rollup-android-arm-eabi@4.46.2": {
+ "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
+ "os": ["android"],
+ "cpu": ["arm"]
+ },
+ "@rollup/rollup-android-arm64@4.46.2": {
+ "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
+ "os": ["android"],
+ "cpu": ["arm64"]
+ },
+ "@rollup/rollup-darwin-arm64@4.46.2": {
+ "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
+ "os": ["darwin"],
+ "cpu": ["arm64"]
+ },
+ "@rollup/rollup-darwin-x64@4.46.2": {
+ "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
+ "os": ["darwin"],
+ "cpu": ["x64"]
+ },
+ "@rollup/rollup-freebsd-arm64@4.46.2": {
+ "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
+ "os": ["freebsd"],
+ "cpu": ["arm64"]
+ },
+ "@rollup/rollup-freebsd-x64@4.46.2": {
+ "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
+ "os": ["freebsd"],
+ "cpu": ["x64"]
+ },
+ "@rollup/rollup-linux-arm-gnueabihf@4.46.2": {
+ "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
+ "os": ["linux"],
+ "cpu": ["arm"]
+ },
+ "@rollup/rollup-linux-arm-musleabihf@4.46.2": {
+ "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
+ "os": ["linux"],
+ "cpu": ["arm"]
+ },
+ "@rollup/rollup-linux-arm64-gnu@4.46.2": {
+ "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
+ "os": ["linux"],
+ "cpu": ["arm64"]
+ },
+ "@rollup/rollup-linux-arm64-musl@4.46.2": {
+ "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
+ "os": ["linux"],
+ "cpu": ["arm64"]
+ },
+ "@rollup/rollup-linux-loongarch64-gnu@4.46.2": {
+ "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
+ "os": ["linux"],
+ "cpu": ["loong64"]
+ },
+ "@rollup/rollup-linux-ppc64-gnu@4.46.2": {
+ "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
+ "os": ["linux"],
+ "cpu": ["ppc64"]
+ },
+ "@rollup/rollup-linux-riscv64-gnu@4.46.2": {
+ "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
+ "os": ["linux"],
+ "cpu": ["riscv64"]
+ },
+ "@rollup/rollup-linux-riscv64-musl@4.46.2": {
+ "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
+ "os": ["linux"],
+ "cpu": ["riscv64"]
+ },
+ "@rollup/rollup-linux-s390x-gnu@4.46.2": {
+ "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
+ "os": ["linux"],
+ "cpu": ["s390x"]
+ },
+ "@rollup/rollup-linux-x64-gnu@4.46.2": {
+ "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
+ "os": ["linux"],
+ "cpu": ["x64"]
+ },
+ "@rollup/rollup-linux-x64-musl@4.46.2": {
+ "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
+ "os": ["linux"],
+ "cpu": ["x64"]
+ },
+ "@rollup/rollup-win32-arm64-msvc@4.46.2": {
+ "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
+ "os": ["win32"],
+ "cpu": ["arm64"]
+ },
+ "@rollup/rollup-win32-ia32-msvc@4.46.2": {
+ "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
+ "os": ["win32"],
+ "cpu": ["ia32"]
+ },
+ "@rollup/rollup-win32-x64-msvc@4.46.2": {
+ "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
+ "os": ["win32"],
+ "cpu": ["x64"]
+ },
+ "@sveltejs/acorn-typescript@1.0.5_acorn@8.15.0": {
+ "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
+ "dependencies": [
+ "acorn"
+ ]
+ },
+ "@sveltejs/adapter-static@3.0.8_@sveltejs+kit@2.19.0__@sveltejs+vite-plugin-svelte@5.0.3___svelte@5.23.0____acorn@8.15.0___vite@6.2.1__svelte@5.23.0___acorn@8.15.0__vite@6.2.1_@sveltejs+vite-plugin-svelte@5.0.3__svelte@5.23.0___acorn@8.15.0__vite@6.2.1_svelte@5.23.0__acorn@8.15.0_vite@6.2.1": {
+ "integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==",
+ "dependencies": [
+ "@sveltejs/kit"
+ ]
+ },
+ "@sveltejs/kit@2.19.0_@sveltejs+vite-plugin-svelte@5.0.3__svelte@5.23.0___acorn@8.15.0__vite@6.2.1_svelte@5.23.0__acorn@8.15.0_vite@6.2.1": {
+ "integrity": "sha512-UTx28Ad4sYsLU//gqkEo5aFOPFBRT2uXCmXTsURqhurDCvzkVwXruJgBcHDaMiK6RKKpYRteDUaXYqZyGPgCXQ==",
+ "dependencies": [
+ "@sveltejs/vite-plugin-svelte",
+ "@types/cookie",
+ "cookie",
+ "devalue",
+ "esm-env",
+ "import-meta-resolve",
+ "kleur",
+ "magic-string",
+ "mrmime",
+ "sade",
+ "set-cookie-parser",
+ "sirv",
+ "svelte",
+ "vite"
+ ],
+ "bin": true
+ },
+ "@sveltejs/vite-plugin-svelte-inspector@4.0.1_@sveltejs+vite-plugin-svelte@5.0.3__svelte@5.23.0___acorn@8.15.0__vite@6.2.1_svelte@5.23.0__acorn@8.15.0_vite@6.2.1": {
+ "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==",
+ "dependencies": [
+ "@sveltejs/vite-plugin-svelte",
+ "debug",
+ "svelte",
+ "vite"
+ ]
+ },
+ "@sveltejs/vite-plugin-svelte@5.0.3_svelte@5.23.0__acorn@8.15.0_vite@6.2.1": {
+ "integrity": "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==",
+ "dependencies": [
+ "@sveltejs/vite-plugin-svelte-inspector",
+ "debug",
+ "deepmerge",
+ "kleur",
+ "magic-string",
+ "svelte",
+ "vite",
+ "vitefu"
+ ]
+ },
+ "@tailwindcss/node@4.1.11": {
+ "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==",
+ "dependencies": [
+ "@ampproject/remapping",
+ "enhanced-resolve",
+ "jiti",
+ "lightningcss",
+ "magic-string",
+ "source-map-js",
+ "tailwindcss@4.1.11"
+ ]
+ },
+ "@tailwindcss/oxide-android-arm64@4.1.11": {
+ "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==",
+ "os": ["android"],
+ "cpu": ["arm64"]
+ },
+ "@tailwindcss/oxide-darwin-arm64@4.1.11": {
+ "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==",
+ "os": ["darwin"],
+ "cpu": ["arm64"]
+ },
+ "@tailwindcss/oxide-darwin-x64@4.1.11": {
+ "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==",
+ "os": ["darwin"],
+ "cpu": ["x64"]
+ },
+ "@tailwindcss/oxide-freebsd-x64@4.1.11": {
+ "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==",
+ "os": ["freebsd"],
+ "cpu": ["x64"]
+ },
+ "@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11": {
+ "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==",
+ "os": ["linux"],
+ "cpu": ["arm"]
+ },
+ "@tailwindcss/oxide-linux-arm64-gnu@4.1.11": {
+ "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==",
+ "os": ["linux"],
+ "cpu": ["arm64"]
+ },
+ "@tailwindcss/oxide-linux-arm64-musl@4.1.11": {
+ "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==",
+ "os": ["linux"],
+ "cpu": ["arm64"]
+ },
+ "@tailwindcss/oxide-linux-x64-gnu@4.1.11": {
+ "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==",
+ "os": ["linux"],
+ "cpu": ["x64"]
+ },
+ "@tailwindcss/oxide-linux-x64-musl@4.1.11": {
+ "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==",
+ "os": ["linux"],
+ "cpu": ["x64"]
+ },
+ "@tailwindcss/oxide-wasm32-wasi@4.1.11": {
+ "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==",
+ "dependencies": [
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@emnapi/wasi-threads",
+ "@napi-rs/wasm-runtime",
+ "@tybys/wasm-util@0.9.0",
+ "tslib"
+ ],
+ "cpu": ["wasm32"]
+ },
+ "@tailwindcss/oxide-win32-arm64-msvc@4.1.11": {
+ "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==",
+ "os": ["win32"],
+ "cpu": ["arm64"]
+ },
+ "@tailwindcss/oxide-win32-x64-msvc@4.1.11": {
+ "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==",
+ "os": ["win32"],
+ "cpu": ["x64"]
+ },
+ "@tailwindcss/oxide@4.1.11": {
+ "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==",
+ "dependencies": [
+ "detect-libc",
+ "tar"
+ ],
+ "optionalDependencies": [
+ "@tailwindcss/oxide-android-arm64",
+ "@tailwindcss/oxide-darwin-arm64",
+ "@tailwindcss/oxide-darwin-x64",
+ "@tailwindcss/oxide-freebsd-x64",
+ "@tailwindcss/oxide-linux-arm-gnueabihf",
+ "@tailwindcss/oxide-linux-arm64-gnu",
+ "@tailwindcss/oxide-linux-arm64-musl",
+ "@tailwindcss/oxide-linux-x64-gnu",
+ "@tailwindcss/oxide-linux-x64-musl",
+ "@tailwindcss/oxide-wasm32-wasi",
+ "@tailwindcss/oxide-win32-arm64-msvc",
+ "@tailwindcss/oxide-win32-x64-msvc"
+ ],
+ "scripts": true
+ },
+ "@tailwindcss/vite@4.1.11_vite@6.2.1": {
+ "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==",
+ "dependencies": [
+ "@tailwindcss/node",
+ "@tailwindcss/oxide",
+ "tailwindcss@4.1.11",
+ "vite"
+ ]
+ },
+ "@tybys/wasm-util@0.10.0": {
+ "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==",
+ "dependencies": [
+ "tslib"
+ ]
+ },
+ "@tybys/wasm-util@0.9.0": {
+ "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
+ "dependencies": [
+ "tslib"
+ ]
+ },
+ "@types/cookie@0.6.0": {
+ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
+ },
+ "@types/estree@1.0.8": {
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
+ },
+ "acorn@8.15.0": {
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "bin": true
+ },
+ "aria-query@5.3.2": {
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="
+ },
+ "axobject-query@4.1.0": {
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="
+ },
+ "chokidar@4.0.3": {
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dependencies": [
+ "readdirp"
+ ]
+ },
+ "chownr@3.0.0": {
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="
+ },
+ "clsx@2.1.1": {
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
+ },
+ "cookie@0.6.0": {
+ "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="
+ },
+ "debug@4.4.1": {
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "dependencies": [
+ "ms"
+ ]
+ },
+ "deepmerge@4.3.1": {
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
+ },
+ "detect-libc@2.0.4": {
+ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="
+ },
+ "devalue@5.1.1": {
+ "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw=="
+ },
+ "enhanced-resolve@5.18.2": {
+ "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==",
+ "dependencies": [
+ "graceful-fs",
+ "tapable"
+ ]
+ },
+ "esbuild@0.25.1": {
+ "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==",
+ "optionalDependencies": [
+ "@esbuild/aix-ppc64@0.25.1",
+ "@esbuild/android-arm@0.25.1",
+ "@esbuild/android-arm64@0.25.1",
+ "@esbuild/android-x64@0.25.1",
+ "@esbuild/darwin-arm64@0.25.1",
+ "@esbuild/darwin-x64@0.25.1",
+ "@esbuild/freebsd-arm64@0.25.1",
+ "@esbuild/freebsd-x64@0.25.1",
+ "@esbuild/linux-arm@0.25.1",
+ "@esbuild/linux-arm64@0.25.1",
+ "@esbuild/linux-ia32@0.25.1",
+ "@esbuild/linux-loong64@0.25.1",
+ "@esbuild/linux-mips64el@0.25.1",
+ "@esbuild/linux-ppc64@0.25.1",
+ "@esbuild/linux-riscv64@0.25.1",
+ "@esbuild/linux-s390x@0.25.1",
+ "@esbuild/linux-x64@0.25.1",
+ "@esbuild/netbsd-arm64@0.25.1",
+ "@esbuild/netbsd-x64@0.25.1",
+ "@esbuild/openbsd-arm64@0.25.1",
+ "@esbuild/openbsd-x64@0.25.1",
+ "@esbuild/sunos-x64@0.25.1",
+ "@esbuild/win32-arm64@0.25.1",
+ "@esbuild/win32-ia32@0.25.1",
+ "@esbuild/win32-x64@0.25.1"
+ ],
+ "scripts": true,
+ "bin": true
+ },
+ "esm-env@1.2.2": {
+ "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="
+ },
+ "esrap@1.4.9": {
+ "integrity": "sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g==",
+ "dependencies": [
+ "@jridgewell/sourcemap-codec"
+ ]
+ },
+ "fdir@6.4.6": {
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="
+ },
+ "fsevents@2.3.3": {
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "os": ["darwin"],
+ "scripts": true
+ },
+ "graceful-fs@4.2.11": {
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+ },
+ "import-meta-resolve@4.1.0": {
+ "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw=="
+ },
+ "is-reference@3.0.3": {
+ "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
+ "dependencies": [
+ "@types/estree"
+ ]
+ },
+ "jiti@2.5.1": {
+ "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
+ "bin": true
+ },
+ "kleur@4.1.5": {
+ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="
+ },
+ "lightningcss-darwin-arm64@1.30.1": {
+ "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
+ "os": ["darwin"],
+ "cpu": ["arm64"]
+ },
+ "lightningcss-darwin-x64@1.30.1": {
+ "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
+ "os": ["darwin"],
+ "cpu": ["x64"]
+ },
+ "lightningcss-freebsd-x64@1.30.1": {
+ "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
+ "os": ["freebsd"],
+ "cpu": ["x64"]
+ },
+ "lightningcss-linux-arm-gnueabihf@1.30.1": {
+ "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
+ "os": ["linux"],
+ "cpu": ["arm"]
+ },
+ "lightningcss-linux-arm64-gnu@1.30.1": {
+ "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
+ "os": ["linux"],
+ "cpu": ["arm64"]
+ },
+ "lightningcss-linux-arm64-musl@1.30.1": {
+ "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
+ "os": ["linux"],
+ "cpu": ["arm64"]
+ },
+ "lightningcss-linux-x64-gnu@1.30.1": {
+ "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
+ "os": ["linux"],
+ "cpu": ["x64"]
+ },
+ "lightningcss-linux-x64-musl@1.30.1": {
+ "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
+ "os": ["linux"],
+ "cpu": ["x64"]
+ },
+ "lightningcss-win32-arm64-msvc@1.30.1": {
+ "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
+ "os": ["win32"],
+ "cpu": ["arm64"]
+ },
+ "lightningcss-win32-x64-msvc@1.30.1": {
+ "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
+ "os": ["win32"],
+ "cpu": ["x64"]
+ },
+ "lightningcss@1.30.1": {
+ "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
+ "dependencies": [
+ "detect-libc"
+ ],
+ "optionalDependencies": [
+ "lightningcss-darwin-arm64",
+ "lightningcss-darwin-x64",
+ "lightningcss-freebsd-x64",
+ "lightningcss-linux-arm-gnueabihf",
+ "lightningcss-linux-arm64-gnu",
+ "lightningcss-linux-arm64-musl",
+ "lightningcss-linux-x64-gnu",
+ "lightningcss-linux-x64-musl",
+ "lightningcss-win32-arm64-msvc",
+ "lightningcss-win32-x64-msvc"
+ ]
+ },
+ "locate-character@3.0.0": {
+ "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
+ },
+ "magic-string@0.30.17": {
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "dependencies": [
+ "@jridgewell/sourcemap-codec"
+ ]
+ },
+ "minipass@7.1.2": {
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="
+ },
+ "minizlib@3.0.2": {
+ "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
+ "dependencies": [
+ "minipass"
+ ]
+ },
+ "mkdirp@3.0.1": {
+ "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "bin": true
+ },
+ "mri@1.2.0": {
+ "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="
+ },
+ "mrmime@2.0.1": {
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="
+ },
+ "ms@2.1.3": {
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "nanoid@3.3.11": {
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "bin": true
+ },
+ "picocolors@1.1.1": {
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
+ },
+ "postcss@8.5.6": {
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dependencies": [
+ "nanoid",
+ "picocolors",
+ "source-map-js"
+ ]
+ },
+ "prettier-plugin-svelte@3.3.3_prettier@3.5.3_svelte@5.23.0__acorn@8.15.0": {
+ "integrity": "sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==",
+ "dependencies": [
+ "prettier",
+ "svelte"
+ ]
+ },
+ "prettier@3.5.3": {
+ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
+ "bin": true
+ },
+ "readdirp@4.1.2": {
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="
+ },
+ "rollup@4.46.2": {
+ "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
+ "dependencies": [
+ "@types/estree"
+ ],
+ "optionalDependencies": [
+ "@rollup/rollup-android-arm-eabi",
+ "@rollup/rollup-android-arm64",
+ "@rollup/rollup-darwin-arm64",
+ "@rollup/rollup-darwin-x64",
+ "@rollup/rollup-freebsd-arm64",
+ "@rollup/rollup-freebsd-x64",
+ "@rollup/rollup-linux-arm-gnueabihf",
+ "@rollup/rollup-linux-arm-musleabihf",
+ "@rollup/rollup-linux-arm64-gnu",
+ "@rollup/rollup-linux-arm64-musl",
+ "@rollup/rollup-linux-loongarch64-gnu",
+ "@rollup/rollup-linux-ppc64-gnu",
+ "@rollup/rollup-linux-riscv64-gnu",
+ "@rollup/rollup-linux-riscv64-musl",
+ "@rollup/rollup-linux-s390x-gnu",
+ "@rollup/rollup-linux-x64-gnu",
+ "@rollup/rollup-linux-x64-musl",
+ "@rollup/rollup-win32-arm64-msvc",
+ "@rollup/rollup-win32-ia32-msvc",
+ "@rollup/rollup-win32-x64-msvc",
+ "fsevents"
+ ],
+ "bin": true
+ },
+ "sade@1.8.1": {
+ "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
+ "dependencies": [
+ "mri"
+ ]
+ },
+ "set-cookie-parser@2.7.1": {
+ "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="
+ },
+ "sirv@3.0.1": {
+ "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==",
+ "dependencies": [
+ "@polka/url",
+ "mrmime",
+ "totalist"
+ ]
+ },
+ "source-map-js@1.2.1": {
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
+ },
+ "svelte-check@4.1.5_svelte@5.23.0__acorn@8.15.0_typescript@5.8.2": {
+ "integrity": "sha512-Gb0T2IqBNe1tLB9EB1Qh+LOe+JB8wt2/rNBDGvkxQVvk8vNeAoG+vZgFB/3P5+zC7RWlyBlzm9dVjZFph/maIg==",
+ "dependencies": [
+ "@jridgewell/trace-mapping",
+ "chokidar",
+ "fdir",
+ "picocolors",
+ "sade",
+ "svelte",
+ "typescript"
+ ],
+ "bin": true
+ },
+ "svelte@5.23.0_acorn@8.15.0": {
+ "integrity": "sha512-v0lL3NuKontiCxholEiAXCB+BYbndlKbwlDMK0DS86WgGELMJSpyqCSbJeMEMBDwOglnS7Ar2Rq0wwa/z2L8Vg==",
+ "dependencies": [
+ "@ampproject/remapping",
+ "@jridgewell/sourcemap-codec",
+ "@sveltejs/acorn-typescript",
+ "@types/estree",
+ "acorn",
+ "aria-query",
+ "axobject-query",
+ "clsx",
+ "esm-env",
+ "esrap",
+ "is-reference",
+ "locate-character",
+ "magic-string",
+ "zimmerframe"
+ ]
+ },
+ "tailwindcss@4.0.13": {
+ "integrity": "sha512-gbvFrB0fOsTv/OugXWi2PtflJ4S6/ctu6Mmn3bCftmLY/6xRsQVEJPgIIpABwpZ52DpONkCA3bEj5b54MHxF2Q=="
+ },
+ "tailwindcss@4.1.11": {
+ "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="
+ },
+ "tapable@2.2.2": {
+ "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="
+ },
+ "tar@7.4.3": {
+ "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
+ "dependencies": [
+ "@isaacs/fs-minipass",
+ "chownr",
+ "minipass",
+ "minizlib",
+ "mkdirp",
+ "yallist"
+ ]
+ },
+ "totalist@3.0.1": {
+ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="
+ },
+ "tslib@2.8.1": {
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
+ },
+ "typescript@5.8.2": {
+ "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
+ "bin": true
+ },
+ "vite@6.2.1": {
+ "integrity": "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==",
+ "dependencies": [
+ "esbuild",
+ "postcss",
+ "rollup"
+ ],
+ "optionalDependencies": [
+ "fsevents"
+ ],
+ "bin": true
+ },
+ "vitefu@1.1.1_vite@6.2.1": {
+ "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
+ "dependencies": [
+ "vite"
+ ],
+ "optionalPeers": [
+ "vite"
+ ]
+ },
+ "yallist@5.0.0": {
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="
+ },
+ "zimmerframe@1.1.2": {
+ "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="
+ }
+ },
+ "workspace": {
+ "dependencies": [
+ "npm:@tailwindcss/vite@*"
+ ],
+ "packageJson": {
+ "dependencies": [
+ "npm:@sveltejs/adapter-static@3.0.8",
+ "npm:@sveltejs/kit@2.19.0",
+ "npm:@sveltejs/vite-plugin-svelte@5.0.3",
+ "npm:@tailwindcss/vite@4.1.11",
+ "npm:esbuild@0.25.1",
+ "npm:prettier-plugin-svelte@3.3.3",
+ "npm:prettier@3.5.3",
+ "npm:svelte-check@4.1.5",
+ "npm:svelte@5.23.0",
+ "npm:tailwindcss@4.0.13",
+ "npm:typescript@5.8.2",
+ "npm:vite@6.2.1"
+ ]
+ }
+ }
+}
diff --git a/import_map.json b/import_map.json
new file mode 100644
index 0000000..5a5ed87
--- /dev/null
+++ b/import_map.json
@@ -0,0 +1,9 @@
+{
+ "imports": {
+ "$": "./src",
+ "$/": "./src/",
+ "$lib": "./src/lib",
+ "$lib/": "./src/lib/",
+ "@tailwindcss/vite": "npm:@tailwindcss/vite"
+ }
+} \ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..a6c9f48
--- /dev/null
+++ b/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "videotool",
+ "private": true,
+ "version": "0.0.1",
+ "type": "module",
+ "devDependencies": {
+ "@sveltejs/adapter-static": "3.0.8",
+ "@sveltejs/kit": "2.19.0",
+ "@sveltejs/vite-plugin-svelte": "5.0.3",
+ "@tailwindcss/vite": "4.1.11",
+ "esbuild": "0.25.1",
+ "prettier": "3.5.3",
+ "prettier-plugin-svelte": "3.3.3",
+ "svelte": "5.23.0",
+ "svelte-check": "4.1.5",
+ "tailwindcss": "4.0.13",
+ "typescript": "5.8.2",
+ "vite": "6.2.1"
+ },
+ "dependencies": {
+ "@ffmpeg/ffmpeg": "^0.12.15",
+ "@ffmpeg/util": "^0.12.2"
+ }
+} \ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..eba1dcc
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,1359 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@ffmpeg/ffmpeg':
+ specifier: ^0.12.15
+ version: 0.12.15
+ '@ffmpeg/util':
+ specifier: ^0.12.2
+ version: 0.12.2
+ devDependencies:
+ '@sveltejs/adapter-static':
+ specifier: 3.0.8
+ version: 3.0.8(@sveltejs/kit@2.19.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1)))
+ '@sveltejs/kit':
+ specifier: 2.19.0
+ version: 2.19.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))
+ '@sveltejs/vite-plugin-svelte':
+ specifier: 5.0.3
+ version: 5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))
+ '@tailwindcss/vite':
+ specifier: 4.1.11
+ version: 4.1.11(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))
+ esbuild:
+ specifier: 0.25.1
+ version: 0.25.1
+ prettier:
+ specifier: 3.5.3
+ version: 3.5.3
+ prettier-plugin-svelte:
+ specifier: 3.3.3
+ version: 3.3.3(prettier@3.5.3)(svelte@5.23.0)
+ svelte:
+ specifier: 5.23.0
+ version: 5.23.0
+ svelte-check:
+ specifier: 4.1.5
+ version: 4.1.5(svelte@5.23.0)(typescript@5.8.2)
+ tailwindcss:
+ specifier: 4.0.13
+ version: 4.0.13
+ typescript:
+ specifier: 5.8.2
+ version: 5.8.2
+ vite:
+ specifier: 6.2.1
+ version: 6.2.1(jiti@2.4.2)(lightningcss@1.30.1)
+
+packages:
+
+ '@ampproject/remapping@2.3.0':
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+
+ '@esbuild/aix-ppc64@0.25.1':
+ resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.1':
+ resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.1':
+ resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.1':
+ resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.1':
+ resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.1':
+ resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.1':
+ resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.1':
+ resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.1':
+ resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.1':
+ resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.1':
+ resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.1':
+ resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.1':
+ resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.1':
+ resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.1':
+ resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.1':
+ resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.1':
+ resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.1':
+ resolution: {integrity: sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.1':
+ resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.1':
+ resolution: {integrity: sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.1':
+ resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/sunos-x64@0.25.1':
+ resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.1':
+ resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.1':
+ resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.1':
+ resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@ffmpeg/ffmpeg@0.12.15':
+ resolution: {integrity: sha512-1C8Obr4GsN3xw+/1Ww6PFM84wSQAGsdoTuTWPOj2OizsRDLT4CXTaVjPhkw6ARyDus1B9X/L2LiXHqYYsGnRFw==}
+ engines: {node: '>=18.x'}
+
+ '@ffmpeg/types@0.12.4':
+ resolution: {integrity: sha512-k9vJQNBGTxE5AhYDtOYR5rO5fKsspbg51gbcwtbkw2lCdoIILzklulcjJfIDwrtn7XhDeF2M+THwJ2FGrLeV6A==}
+ engines: {node: '>=16.x'}
+
+ '@ffmpeg/util@0.12.2':
+ resolution: {integrity: sha512-ouyoW+4JB7WxjeZ2y6KpRvB+dLp7Cp4ro8z0HIVpZVCM7AwFlHa0c4R8Y/a4M3wMqATpYKhC7lSFHQ0T11MEDw==}
+ engines: {node: '>=18.x'}
+
+ '@isaacs/fs-minipass@4.0.1':
+ resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+ engines: {node: '>=18.0.0'}
+
+ '@jridgewell/gen-mapping@0.3.8':
+ resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/set-array@1.2.1':
+ resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.0':
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+ '@polka/url@1.0.0-next.28':
+ resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==}
+
+ '@rollup/rollup-android-arm-eabi@4.35.0':
+ resolution: {integrity: sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.35.0':
+ resolution: {integrity: sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.35.0':
+ resolution: {integrity: sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.35.0':
+ resolution: {integrity: sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.35.0':
+ resolution: {integrity: sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.35.0':
+ resolution: {integrity: sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.35.0':
+ resolution: {integrity: sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.35.0':
+ resolution: {integrity: sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.35.0':
+ resolution: {integrity: sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.35.0':
+ resolution: {integrity: sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.35.0':
+ resolution: {integrity: sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.35.0':
+ resolution: {integrity: sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.35.0':
+ resolution: {integrity: sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.35.0':
+ resolution: {integrity: sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.35.0':
+ resolution: {integrity: sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.35.0':
+ resolution: {integrity: sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-win32-arm64-msvc@4.35.0':
+ resolution: {integrity: sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.35.0':
+ resolution: {integrity: sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.35.0':
+ resolution: {integrity: sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@sveltejs/acorn-typescript@1.0.5':
+ resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==}
+ peerDependencies:
+ acorn: ^8.9.0
+
+ '@sveltejs/adapter-static@3.0.8':
+ resolution: {integrity: sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==}
+ peerDependencies:
+ '@sveltejs/kit': ^2.0.0
+
+ '@sveltejs/kit@2.19.0':
+ resolution: {integrity: sha512-UTx28Ad4sYsLU//gqkEo5aFOPFBRT2uXCmXTsURqhurDCvzkVwXruJgBcHDaMiK6RKKpYRteDUaXYqZyGPgCXQ==}
+ engines: {node: '>=18.13'}
+ hasBin: true
+ peerDependencies:
+ '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0
+ svelte: ^4.0.0 || ^5.0.0-next.0
+ vite: ^5.0.3 || ^6.0.0
+
+ '@sveltejs/vite-plugin-svelte-inspector@4.0.1':
+ resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22}
+ peerDependencies:
+ '@sveltejs/vite-plugin-svelte': ^5.0.0
+ svelte: ^5.0.0
+ vite: ^6.0.0
+
+ '@sveltejs/vite-plugin-svelte@5.0.3':
+ resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22}
+ peerDependencies:
+ svelte: ^5.0.0
+ vite: ^6.0.0
+
+ '@tailwindcss/node@4.1.11':
+ resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==}
+
+ '@tailwindcss/oxide-android-arm64@4.1.11':
+ resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.11':
+ resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.1.11':
+ resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.11':
+ resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
+ resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
+ resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.11':
+ resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.11':
+ resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.11':
+ resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.11':
+ resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
+ resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.11':
+ resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.1.11':
+ resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==}
+ engines: {node: '>= 10'}
+
+ '@tailwindcss/vite@4.1.11':
+ resolution: {integrity: sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7
+
+ '@types/cookie@0.6.0':
+ resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+
+ '@types/estree@1.0.6':
+ resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
+ acorn@8.14.1:
+ resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
+ axobject-query@4.1.0:
+ resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
+ engines: {node: '>= 0.4'}
+
+ chokidar@4.0.3:
+ resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+ engines: {node: '>= 14.16.0'}
+
+ chownr@3.0.0:
+ resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+ engines: {node: '>=18'}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ cookie@0.6.0:
+ resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
+ engines: {node: '>= 0.6'}
+
+ debug@4.4.0:
+ resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
+ detect-libc@2.0.4:
+ resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
+ engines: {node: '>=8'}
+
+ devalue@5.1.1:
+ resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==}
+
+ enhanced-resolve@5.18.1:
+ resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
+ engines: {node: '>=10.13.0'}
+
+ esbuild@0.25.1:
+ resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ esm-env@1.2.2:
+ resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
+
+ esrap@1.4.5:
+ resolution: {integrity: sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g==}
+
+ fdir@6.4.3:
+ resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ import-meta-resolve@4.1.0:
+ resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==}
+
+ is-reference@3.0.3:
+ resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
+
+ jiti@2.4.2:
+ resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
+ hasBin: true
+
+ kleur@4.1.5:
+ resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
+ engines: {node: '>=6'}
+
+ lightningcss-darwin-arm64@1.30.1:
+ resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.30.1:
+ resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.30.1:
+ resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-linux-x64-musl@1.30.1:
+ resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.30.1:
+ resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
+ engines: {node: '>= 12.0.0'}
+
+ locate-character@3.0.0:
+ resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
+
+ magic-string@0.30.17:
+ resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minizlib@3.0.2:
+ resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==}
+ engines: {node: '>= 18'}
+
+ mkdirp@3.0.1:
+ resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ mri@1.2.0:
+ resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+ engines: {node: '>=4'}
+
+ mrmime@2.0.1:
+ resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+ engines: {node: '>=10'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.9:
+ resolution: {integrity: sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ postcss@8.5.3:
+ resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prettier-plugin-svelte@3.3.3:
+ resolution: {integrity: sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==}
+ peerDependencies:
+ prettier: ^3.0.0
+ svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0
+
+ prettier@3.5.3:
+ resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ readdirp@4.1.2:
+ resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+ engines: {node: '>= 14.18.0'}
+
+ rollup@4.35.0:
+ resolution: {integrity: sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ sade@1.8.1:
+ resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
+ engines: {node: '>=6'}
+
+ set-cookie-parser@2.7.1:
+ resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
+
+ sirv@3.0.1:
+ resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
+ engines: {node: '>=18'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ svelte-check@4.1.5:
+ resolution: {integrity: sha512-Gb0T2IqBNe1tLB9EB1Qh+LOe+JB8wt2/rNBDGvkxQVvk8vNeAoG+vZgFB/3P5+zC7RWlyBlzm9dVjZFph/maIg==}
+ engines: {node: '>= 18.0.0'}
+ hasBin: true
+ peerDependencies:
+ svelte: ^4.0.0 || ^5.0.0-next.0
+ typescript: '>=5.0.0'
+
+ svelte@5.23.0:
+ resolution: {integrity: sha512-v0lL3NuKontiCxholEiAXCB+BYbndlKbwlDMK0DS86WgGELMJSpyqCSbJeMEMBDwOglnS7Ar2Rq0wwa/z2L8Vg==}
+ engines: {node: '>=18'}
+
+ tailwindcss@4.0.13:
+ resolution: {integrity: sha512-gbvFrB0fOsTv/OugXWi2PtflJ4S6/ctu6Mmn3bCftmLY/6xRsQVEJPgIIpABwpZ52DpONkCA3bEj5b54MHxF2Q==}
+
+ tailwindcss@4.1.11:
+ resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==}
+
+ tapable@2.2.1:
+ resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ engines: {node: '>=6'}
+
+ tar@7.4.3:
+ resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
+ engines: {node: '>=18'}
+
+ totalist@3.0.1:
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+ engines: {node: '>=6'}
+
+ typescript@5.8.2:
+ resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ vite@6.2.1:
+ resolution: {integrity: sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ jiti: '>=1.21.0'
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vitefu@1.0.6:
+ resolution: {integrity: sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==}
+ peerDependencies:
+ vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0
+ peerDependenciesMeta:
+ vite:
+ optional: true
+
+ yallist@5.0.0:
+ resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+ engines: {node: '>=18'}
+
+ zimmerframe@1.1.2:
+ resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
+
+snapshots:
+
+ '@ampproject/remapping@2.3.0':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.8
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@esbuild/aix-ppc64@0.25.1':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.1':
+ optional: true
+
+ '@esbuild/android-arm@0.25.1':
+ optional: true
+
+ '@esbuild/android-x64@0.25.1':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.1':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.1':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.1':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.1':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.1':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.1':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.1':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.1':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.1':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.1':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.1':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.1':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.1':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.1':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.1':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.1':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.1':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.1':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.1':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.1':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.1':
+ optional: true
+
+ '@ffmpeg/ffmpeg@0.12.15':
+ dependencies:
+ '@ffmpeg/types': 0.12.4
+
+ '@ffmpeg/types@0.12.4': {}
+
+ '@ffmpeg/util@0.12.2': {}
+
+ '@isaacs/fs-minipass@4.0.1':
+ dependencies:
+ minipass: 7.1.2
+
+ '@jridgewell/gen-mapping@0.3.8':
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/set-array@1.2.1': {}
+
+ '@jridgewell/sourcemap-codec@1.5.0': {}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ '@polka/url@1.0.0-next.28': {}
+
+ '@rollup/rollup-android-arm-eabi@4.35.0':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.35.0':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.35.0':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.35.0':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.35.0':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.35.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.35.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.35.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.35.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.35.0':
+ optional: true
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.35.0':
+ optional: true
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.35.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.35.0':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.35.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.35.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.35.0':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.35.0':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.35.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.35.0':
+ optional: true
+
+ '@sveltejs/acorn-typescript@1.0.5(acorn@8.14.1)':
+ dependencies:
+ acorn: 8.14.1
+
+ '@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.19.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1)))':
+ dependencies:
+ '@sveltejs/kit': 2.19.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))
+
+ '@sveltejs/kit@2.19.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))':
+ dependencies:
+ '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))
+ '@types/cookie': 0.6.0
+ cookie: 0.6.0
+ devalue: 5.1.1
+ esm-env: 1.2.2
+ import-meta-resolve: 4.1.0
+ kleur: 4.1.5
+ magic-string: 0.30.17
+ mrmime: 2.0.1
+ sade: 1.8.1
+ set-cookie-parser: 2.7.1
+ sirv: 3.0.1
+ svelte: 5.23.0
+ vite: 6.2.1(jiti@2.4.2)(lightningcss@1.30.1)
+
+ '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))':
+ dependencies:
+ '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))
+ debug: 4.4.0
+ svelte: 5.23.0
+ vite: 6.2.1(jiti@2.4.2)(lightningcss@1.30.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))':
+ dependencies:
+ '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.23.0)(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))
+ debug: 4.4.0
+ deepmerge: 4.3.1
+ kleur: 4.1.5
+ magic-string: 0.30.17
+ svelte: 5.23.0
+ vite: 6.2.1(jiti@2.4.2)(lightningcss@1.30.1)
+ vitefu: 1.0.6(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))
+ transitivePeerDependencies:
+ - supports-color
+
+ '@tailwindcss/node@4.1.11':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ enhanced-resolve: 5.18.1
+ jiti: 2.4.2
+ lightningcss: 1.30.1
+ magic-string: 0.30.17
+ source-map-js: 1.2.1
+ tailwindcss: 4.1.11
+
+ '@tailwindcss/oxide-android-arm64@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.11':
+ dependencies:
+ detect-libc: 2.0.4
+ tar: 7.4.3
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.1.11
+ '@tailwindcss/oxide-darwin-arm64': 4.1.11
+ '@tailwindcss/oxide-darwin-x64': 4.1.11
+ '@tailwindcss/oxide-freebsd-x64': 4.1.11
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.11
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.11
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.11
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.11
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.11
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.11
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.11
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.11
+
+ '@tailwindcss/vite@4.1.11(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1))':
+ dependencies:
+ '@tailwindcss/node': 4.1.11
+ '@tailwindcss/oxide': 4.1.11
+ tailwindcss: 4.1.11
+ vite: 6.2.1(jiti@2.4.2)(lightningcss@1.30.1)
+
+ '@types/cookie@0.6.0': {}
+
+ '@types/estree@1.0.6': {}
+
+ acorn@8.14.1: {}
+
+ aria-query@5.3.2: {}
+
+ axobject-query@4.1.0: {}
+
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.2
+
+ chownr@3.0.0: {}
+
+ clsx@2.1.1: {}
+
+ cookie@0.6.0: {}
+
+ debug@4.4.0:
+ dependencies:
+ ms: 2.1.3
+
+ deepmerge@4.3.1: {}
+
+ detect-libc@2.0.4: {}
+
+ devalue@5.1.1: {}
+
+ enhanced-resolve@5.18.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.1
+
+ esbuild@0.25.1:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.1
+ '@esbuild/android-arm': 0.25.1
+ '@esbuild/android-arm64': 0.25.1
+ '@esbuild/android-x64': 0.25.1
+ '@esbuild/darwin-arm64': 0.25.1
+ '@esbuild/darwin-x64': 0.25.1
+ '@esbuild/freebsd-arm64': 0.25.1
+ '@esbuild/freebsd-x64': 0.25.1
+ '@esbuild/linux-arm': 0.25.1
+ '@esbuild/linux-arm64': 0.25.1
+ '@esbuild/linux-ia32': 0.25.1
+ '@esbuild/linux-loong64': 0.25.1
+ '@esbuild/linux-mips64el': 0.25.1
+ '@esbuild/linux-ppc64': 0.25.1
+ '@esbuild/linux-riscv64': 0.25.1
+ '@esbuild/linux-s390x': 0.25.1
+ '@esbuild/linux-x64': 0.25.1
+ '@esbuild/netbsd-arm64': 0.25.1
+ '@esbuild/netbsd-x64': 0.25.1
+ '@esbuild/openbsd-arm64': 0.25.1
+ '@esbuild/openbsd-x64': 0.25.1
+ '@esbuild/sunos-x64': 0.25.1
+ '@esbuild/win32-arm64': 0.25.1
+ '@esbuild/win32-ia32': 0.25.1
+ '@esbuild/win32-x64': 0.25.1
+
+ esm-env@1.2.2: {}
+
+ esrap@1.4.5:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ fdir@6.4.3: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ graceful-fs@4.2.11: {}
+
+ import-meta-resolve@4.1.0: {}
+
+ is-reference@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.6
+
+ jiti@2.4.2: {}
+
+ kleur@4.1.5: {}
+
+ lightningcss-darwin-arm64@1.30.1:
+ optional: true
+
+ lightningcss-darwin-x64@1.30.1:
+ optional: true
+
+ lightningcss-freebsd-x64@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.30.1:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ optional: true
+
+ lightningcss@1.30.1:
+ dependencies:
+ detect-libc: 2.0.4
+ optionalDependencies:
+ lightningcss-darwin-arm64: 1.30.1
+ lightningcss-darwin-x64: 1.30.1
+ lightningcss-freebsd-x64: 1.30.1
+ lightningcss-linux-arm-gnueabihf: 1.30.1
+ lightningcss-linux-arm64-gnu: 1.30.1
+ lightningcss-linux-arm64-musl: 1.30.1
+ lightningcss-linux-x64-gnu: 1.30.1
+ lightningcss-linux-x64-musl: 1.30.1
+ lightningcss-win32-arm64-msvc: 1.30.1
+ lightningcss-win32-x64-msvc: 1.30.1
+
+ locate-character@3.0.0: {}
+
+ magic-string@0.30.17:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ minipass@7.1.2: {}
+
+ minizlib@3.0.2:
+ dependencies:
+ minipass: 7.1.2
+
+ mkdirp@3.0.1: {}
+
+ mri@1.2.0: {}
+
+ mrmime@2.0.1: {}
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.9: {}
+
+ picocolors@1.1.1: {}
+
+ postcss@8.5.3:
+ dependencies:
+ nanoid: 3.3.9
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.23.0):
+ dependencies:
+ prettier: 3.5.3
+ svelte: 5.23.0
+
+ prettier@3.5.3: {}
+
+ readdirp@4.1.2: {}
+
+ rollup@4.35.0:
+ dependencies:
+ '@types/estree': 1.0.6
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.35.0
+ '@rollup/rollup-android-arm64': 4.35.0
+ '@rollup/rollup-darwin-arm64': 4.35.0
+ '@rollup/rollup-darwin-x64': 4.35.0
+ '@rollup/rollup-freebsd-arm64': 4.35.0
+ '@rollup/rollup-freebsd-x64': 4.35.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.35.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.35.0
+ '@rollup/rollup-linux-arm64-gnu': 4.35.0
+ '@rollup/rollup-linux-arm64-musl': 4.35.0
+ '@rollup/rollup-linux-loongarch64-gnu': 4.35.0
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.35.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.35.0
+ '@rollup/rollup-linux-s390x-gnu': 4.35.0
+ '@rollup/rollup-linux-x64-gnu': 4.35.0
+ '@rollup/rollup-linux-x64-musl': 4.35.0
+ '@rollup/rollup-win32-arm64-msvc': 4.35.0
+ '@rollup/rollup-win32-ia32-msvc': 4.35.0
+ '@rollup/rollup-win32-x64-msvc': 4.35.0
+ fsevents: 2.3.3
+
+ sade@1.8.1:
+ dependencies:
+ mri: 1.2.0
+
+ set-cookie-parser@2.7.1: {}
+
+ sirv@3.0.1:
+ dependencies:
+ '@polka/url': 1.0.0-next.28
+ mrmime: 2.0.1
+ totalist: 3.0.1
+
+ source-map-js@1.2.1: {}
+
+ svelte-check@4.1.5(svelte@5.23.0)(typescript@5.8.2):
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.25
+ chokidar: 4.0.3
+ fdir: 6.4.3
+ picocolors: 1.1.1
+ sade: 1.8.1
+ svelte: 5.23.0
+ typescript: 5.8.2
+ transitivePeerDependencies:
+ - picomatch
+
+ svelte@5.23.0:
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@sveltejs/acorn-typescript': 1.0.5(acorn@8.14.1)
+ '@types/estree': 1.0.6
+ acorn: 8.14.1
+ aria-query: 5.3.2
+ axobject-query: 4.1.0
+ clsx: 2.1.1
+ esm-env: 1.2.2
+ esrap: 1.4.5
+ is-reference: 3.0.3
+ locate-character: 3.0.0
+ magic-string: 0.30.17
+ zimmerframe: 1.1.2
+
+ tailwindcss@4.0.13: {}
+
+ tailwindcss@4.1.11: {}
+
+ tapable@2.2.1: {}
+
+ tar@7.4.3:
+ dependencies:
+ '@isaacs/fs-minipass': 4.0.1
+ chownr: 3.0.0
+ minipass: 7.1.2
+ minizlib: 3.0.2
+ mkdirp: 3.0.1
+ yallist: 5.0.0
+
+ totalist@3.0.1: {}
+
+ typescript@5.8.2: {}
+
+ vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1):
+ dependencies:
+ esbuild: 0.25.1
+ postcss: 8.5.3
+ rollup: 4.35.0
+ optionalDependencies:
+ fsevents: 2.3.3
+ jiti: 2.4.2
+ lightningcss: 1.30.1
+
+ vitefu@1.0.6(vite@6.2.1(jiti@2.4.2)(lightningcss@1.30.1)):
+ optionalDependencies:
+ vite: 6.2.1(jiti@2.4.2)(lightningcss@1.30.1)
+
+ yallist@5.0.0: {}
+
+ zimmerframe@1.1.2: {}
diff --git a/src/app.css b/src/app.css
new file mode 100644
index 0000000..d4b5078
--- /dev/null
+++ b/src/app.css
@@ -0,0 +1 @@
+@import 'tailwindcss';
diff --git a/src/app.d.ts b/src/app.d.ts
new file mode 100644
index 0000000..da08e6d
--- /dev/null
+++ b/src/app.d.ts
@@ -0,0 +1,13 @@
+// See https://svelte.dev/docs/kit/types#app.d.ts
+// for information about these interfaces
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface PageState {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/src/app.html b/src/app.html
new file mode 100644
index 0000000..d1e03cb
--- /dev/null
+++ b/src/app.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html lang="en" class='bg-[#0a0a0a] text-white'>
+
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ %sveltekit.head%
+</head>
+
+<body data-sveltekit-preload-data="hover">
+ <div style="display: contents">%sveltekit.body%</div>
+</body>
+
+</html>
diff --git a/src/lib/Player/FrameSlider.svelte b/src/lib/Player/FrameSlider.svelte
new file mode 100644
index 0000000..a632aa9
--- /dev/null
+++ b/src/lib/Player/FrameSlider.svelte
@@ -0,0 +1,67 @@
+<script lang="ts">
+ import RangeSlider from '../vendor/svelte-range-slider/range-slider.svelte';
+ let {
+ frame = $bindable(),
+ frameCount,
+ playing = $bindable(),
+ playbackStarted = $bindable()
+ }: {
+ frame: number;
+ frameCount: number;
+ playing: boolean;
+ playbackStarted: number;
+ } = $props();
+</script>
+
+<div class="w-full flex items-center justify-center pt-2">
+ <div class="p-2 pr-1">
+ <button
+ onclick={() => {
+ playing = !playing;
+ }}
+ aria-label={playing ? 'pause' : 'play'}
+ class="flex items-center justify-center"
+ ><svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke-width="1.5"
+ stroke="currentColor"
+ class="size-6"
+ >
+ <path
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ d={!playing
+ ? 'M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 0 1 0 1.972l-11.54 6.347a1.125 1.125 0 0 1-1.667-.986V5.653Z'
+ : 'M15.75 5.25v13.5m-7.5-13.5v13.5'}
+ />
+ </svg></button
+ >
+ </div>
+ <div class="flex-1">
+ <RangeSlider bind:value={frame} min={0} max={frameCount} />
+ </div>
+ <div class="label p-2 pl-1">
+ <input
+ type="number"
+ bind:value={frame}
+ class="w-16 appearance-none text-right"
+ style="-moz-appearance:textfield;"
+ max={frameCount}
+ onkeypress={(e) => {
+ e.stopPropagation();
+ }}
+ onkeydown={(e) => {
+ e.stopPropagation();
+ }}
+ />
+ of {frameCount}
+ <!-- <input
+ type="number"
+ bind:value={frameCount}
+ class="w-8 appearance-none"
+ style="-moz-appearance:textfield;"
+ /> -->
+ </div>
+</div>
diff --git a/src/lib/Player/Keybinds.svelte b/src/lib/Player/Keybinds.svelte
new file mode 100644
index 0000000..756a865
--- /dev/null
+++ b/src/lib/Player/Keybinds.svelte
@@ -0,0 +1,47 @@
+<script lang="ts">
+ let {
+ frame = $bindable(),
+ frameCount,
+ fps,
+ playing = $bindable(),
+ playbackStarted = $bindable()
+ }: {
+ frame: number;
+ frameCount: number;
+ fps: number | undefined;
+ playing: boolean;
+ playbackStarted: number;
+ } = $props();
+</script>
+
+<svelte:window
+ onkeypress={(e) => {
+ switch (e.key) {
+ case ' ':
+ e.preventDefault();
+ playing = !playing;
+ if (playing) playbackStarted = performance.now();
+ break;
+
+ // default:
+ // if (dev) console.debug('Keypress:', e.key);
+ // break;
+ }
+ }}
+ onkeydown={(e) => {
+ switch (e.key) {
+ case 'ArrowLeft':
+ e.preventDefault();
+ frame = Math.max(frame - (e.ctrlKey ? (fps ?? 60) : 1), 0);
+ break;
+ case 'ArrowRight':
+ e.preventDefault();
+ frame = Math.min(frame + (e.ctrlKey ? (fps ?? 60) : 1), frameCount);
+ break;
+
+ // default:
+ // if (dev) console.debug('Keydown:', e.key);
+ // break;
+ }
+ }}
+/>
diff --git a/src/lib/Player/Player.svelte b/src/lib/Player/Player.svelte
new file mode 100644
index 0000000..f6df121
--- /dev/null
+++ b/src/lib/Player/Player.svelte
@@ -0,0 +1,161 @@
+<script lang="ts">
+ import FrameSlider from './FrameSlider.svelte';
+ import { type Video, type VideoConstructor } from '$/lib/Player/Video';
+ import Keybinds from './Keybinds.svelte';
+ import { onMount } from 'svelte';
+
+ let frame = $state(0);
+ let canvas = $state(null as HTMLCanvasElement | null);
+ let audio = $state(null as HTMLAudioElement | null);
+ let lastCanvas = null as typeof canvas;
+ let video = $state(undefined as Video | undefined);
+ let frameCount = $state(0);
+ let playing = $state(false);
+ let playbackStarted = $state(0);
+ let playbackFrameOffset = 0;
+ let renderPromise: Promise<void> | void = void 0;
+ let renderId = 0;
+ let audioSource = $state(null as null | string);
+ let {
+ Video: VideoImplementation
+ }: {
+ Video: VideoConstructor;
+ } = $props();
+ const newCanvas = async (canvas: HTMLCanvasElement, videoImplementation: VideoConstructor) => {
+ if (video) video.cleanup();
+ lastCanvas = canvas;
+ video = new VideoImplementation(canvas);
+ const audioSourcePromise = (async () => {
+ if (video?.audioUrl?.[1]) {
+ audioSource = await fetch(video.audioUrl[1])
+ .then(async (v) => {
+ if (v.status.toString().startsWith('2')) return URL.createObjectURL(await v.blob());
+ else throw new Error('non-2xx audio');
+ })
+ .catch((e) => {
+ console.warn('Failed to get audio', e);
+ return video?.audioUrl?.[1] ?? null;
+ });
+ }
+ })();
+ video['_isInit'] = true;
+ renderPromise = video.init();
+ await renderPromise;
+ video['_isInit'] = false;
+ frameCount = video.length;
+ await audioSourcePromise;
+ };
+ const renderPreviewFrame = async (video: Video, frame: number) => {
+ const ourId = ++renderId;
+ if (renderPromise) await renderPromise;
+ if (renderId !== ourId) return;
+ renderPromise =
+ video.renderFrame({
+ frames: frame,
+ milliseconds: (frame / video.fps) * 1000,
+ seconds: frame / video.fps
+ }) ?? Promise.resolve();
+ return renderPromise;
+ };
+ $effect(() => {
+ if (canvas && canvas !== lastCanvas) newCanvas(canvas, VideoImplementation);
+ });
+ $effect(() => {
+ if (video) renderPreviewFrame(video, frame);
+ });
+ let playbackLoopId = 0;
+ const startPlaybackLoop = (id = ++playbackLoopId) => {
+ if (video && id === playbackLoopId) {
+ const ms = performance.now() - playbackStarted;
+ let f = Math.floor((ms / 1000) * video.fps) + playbackFrameOffset;
+
+ if (f > frameCount) {
+ f = frameCount;
+ playing = false;
+ }
+ frame = f;
+ renderPreviewFrame(video, frame).then(() =>
+ requestAnimationFrame(() => (playing ? startPlaybackLoop(id) : void 0))
+ );
+ }
+ };
+ $effect(() => {
+ if (playing) {
+ playbackStarted = performance.now();
+ playbackFrameOffset = frame;
+ startPlaybackLoop();
+ }
+ });
+ let loadedFrameTimestamp = false;
+ onMount(() => {
+ const t = sessionStorage.getItem('timestamp');
+ const tI = t ? parseInt(t, 36) : null;
+ if (tI && !isNaN(tI)) {
+ frame = tI;
+ requestAnimationFrame(() => (frame = tI));
+ }
+ loadedFrameTimestamp = true;
+ });
+ // TODO: implement waitin a few seconds before saving
+ $effect(() => {
+ if (loadedFrameTimestamp)
+ try {
+ sessionStorage.setItem('timestamp', frame.toString(36));
+ } catch (_) {}
+ });
+ $effect(() => {
+ if (audio && video && !playing) {
+ try {
+ const f = frame;
+ audio.currentTime = frame / video.fps;
+ audio.play();
+ (async () => {
+ const targetTime = (frame + 1) / video.fps;
+ while (audio.currentTime <= targetTime) {
+ await new Promise((rs) => requestAnimationFrame(rs));
+ if (playing || frame !== f) return;
+ }
+ audio.pause();
+ audio.currentTime = frame / video.fps;
+ })();
+ } catch (error) {
+ console.warn(error);
+ }
+ }
+ });
+ $effect(() => {
+ if (audio) {
+ if (playing) audio.play();
+ else audio.pause();
+ }
+ });
+</script>
+
+<svelte:window
+ onresize={() => {
+ if (canvas && video) {
+ (async () => {
+ video['_isInit'] = true;
+ renderPromise = await video.init();
+ video['_isInit'] = false;
+ renderPreviewFrame(video, frame);
+ })();
+ }
+ }}
+/>
+
+<Keybinds bind:frame {frameCount} fps={video?.fps} bind:playing bind:playbackStarted />
+
+<div class="p-2 w-screen h-screen relative flex flex-col">
+ <div class="flex-1 relative">
+ <div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
+ <canvas class="pointer-events-none bg-black" bind:this={canvas}>
+ Your browser doesn't support the canvas API.
+ </canvas>
+ {#if audioSource}
+ <audio src={audioSource} bind:this={audio}></audio>
+ {/if}
+ </div>
+ </div>
+ <FrameSlider bind:frame {frameCount} bind:playing bind:playbackStarted />
+</div>
diff --git a/src/lib/Player/Video.ts b/src/lib/Player/Video.ts
new file mode 100644
index 0000000..78b3b8f
--- /dev/null
+++ b/src/lib/Player/Video.ts
@@ -0,0 +1,55 @@
+export type FrameTime = {
+ milliseconds: number,
+ seconds: number,
+ frames: number
+}
+export abstract class Video {
+ public constructor(public canvas: HTMLCanvasElement) { };
+ public abstract renderFrame(time: FrameTime): Promise<void> | void;
+ /** (re-)Initializes the Video object. Also called on window resizes. */
+ public abstract init(): void | Promise<void>;
+ private _isInit = false;
+ /** The frames per second to render at */
+ public abstract get fps(): number;
+ /** Length in frames */
+ public abstract get length(): number;
+ /** A URL (and matching filename) to an ffmpeg-compatible audio file */
+ public audioUrl?: readonly [filename: string, fileUrl: string];
+ /** Resizes the canvas to a predetermined render resolution - must only be called in init() - do not overwrite */
+ public resize(x: number, y: number) {
+ if (!this._isInit) throw new Error('Must only call resize() in init.')
+ this.canvas.width = x;
+ this.canvas.height = y;
+ const parentW = this.canvas.parentElement!.clientWidth,
+ parentH = this.canvas.parentElement!.clientHeight
+ if (x <= parentW && y <= parentH) {
+ this.canvas.style.width = `${x}px`;
+ this.canvas.style.height = `${y}px`;
+ } else if (x <= parentW && y > parentH) {
+ this.canvas.style.width = `${x / y * parentH}px`;
+ this.canvas.style.height = `${parentH}px`;
+ } else if (y <= parentH && x > parentW) {
+ this.canvas.style.width = `${parentW}px`;
+ this.canvas.style.height = `${y / x * parentW}px`;
+ } else {
+ if ((parentW / x) * y > parentH) {
+ this.canvas.style.width = `${(parentH / y) * x}px`
+ this.canvas.style.height = `${parentH}px`
+ } else {
+ this.canvas.style.width = `${parentW}px`
+ this.canvas.style.height = `${(parentW / x) * y}px`
+ }
+ }
+ }
+ /** The width of the video, in pixels */
+ public get w() {
+ return this.canvas.width;
+ }
+ /** The height of the video, in pixels */
+ public get h() {
+ return this.canvas.height;
+ }
+ /** Use to cleanup any mess you made - do not remove the canvas, it may be reused. */
+ public cleanup() { };
+}
+export type VideoConstructor = new (...params: ConstructorParameters<typeof Video>) => Video
diff --git a/src/lib/Renderer/Renderer.svelte b/src/lib/Renderer/Renderer.svelte
new file mode 100644
index 0000000..72c270b
--- /dev/null
+++ b/src/lib/Renderer/Renderer.svelte
@@ -0,0 +1,146 @@
+<script lang="ts">
+ import { onDestroy } from 'svelte';
+ import type { VideoConstructor } from '../Player/Video';
+ import { FFmpeg, type LogEvent } from '@ffmpeg/ffmpeg';
+ import { fetchFile, toBlobURL } from '@ffmpeg/util';
+
+ let {
+ Video: VideoImplementation
+ }: {
+ Video: VideoConstructor;
+ } = $props();
+ let canvas = $state(null as null | HTMLCanvasElement);
+
+ let frame = $state(0);
+ let frameCount = $state(0);
+ let active = false;
+
+ let lastAnimationFrame = 0;
+ let startedAt = 0;
+ let message = $state('Waiting');
+ let videoUrl = $state(null as null | string);
+
+ const ffmpegBaseURL = 'https://unpkg.com/@ffmpeg/core-mt@0.12.6/dist/esm';
+
+ const start = async (format = 'mp4') => {
+ active = true;
+ startedAt = performance.now();
+
+ const ffmpeg = new FFmpeg();
+ message = 'Loading ffmpeg-core.js';
+ ffmpeg.on('log', ({ message: msg }: LogEvent) => {
+ message = msg;
+ console.log(message);
+ });
+ await ffmpeg.load({
+ coreURL: await toBlobURL(`${ffmpegBaseURL}/ffmpeg-core.js`, 'text/javascript'),
+ wasmURL: await toBlobURL(`${ffmpegBaseURL}/ffmpeg-core.wasm`, 'application/wasm'),
+ workerURL: await toBlobURL(`${ffmpegBaseURL}/ffmpeg-core.worker.js`, 'text/javascript')
+ });
+
+ message = 'Making directory';
+ await ffmpeg.createDir('frames');
+ message = 'Preparing Canvas';
+ const c = canvas!;
+ const video = new VideoImplementation(c);
+ video['_isInit'] = true;
+ await video.init();
+ video['_isInit'] = false;
+ frameCount = video.length;
+ message = 'Rendering first few frames...';
+ for (frame = 0; frame <= frameCount; frame++) {
+ if (!active) return;
+ await video.renderFrame({
+ frames: frame,
+ milliseconds: (frame / video.fps) * 1000,
+ seconds: frame / video.fps
+ });
+ // TODO: see if we can pipe this into an active ffmpeg instead of writing a file then running a command after
+ const file = await fetchFile(c.toDataURL());
+ await ffmpeg.writeFile('frames/f' + frame.toString().padStart(10, '0') + '.png', file);
+ if (performance.now() > lastAnimationFrame + 33) {
+ await new Promise((rs) => requestAnimationFrame(rs));
+ lastAnimationFrame = performance.now();
+ message = `Rendering to pngs - ${frame}/${frameCount} frames | running for ${(
+ Math.floor(lastAnimationFrame - startedAt) / 1000
+ ).toFixed(3)} seconds`;
+ }
+ }
+
+ message = 'Start transcoding';
+ await ffmpeg.exec([
+ '-framerate',
+ video.fps.toString(),
+ '-pattern_type',
+ 'sequence',
+ '-start_number',
+ '0',
+ '-pattern_type',
+ 'glob',
+ '-i',
+ 'frames/f*.png',
+ // 'frames/f%04d.png',
+ 'middle.' + format
+ ]);
+ message = 'Disposing pngs';
+ for (const i of await ffmpeg.listDir('frames'))
+ if (!i.isDir) await ffmpeg.deleteFile('frames/' + i.name);
+ await ffmpeg.deleteDir('frames/');
+ if (video.audioUrl) {
+ message = 'Fetching Audio';
+ await ffmpeg.writeFile('audio.' + video.audioUrl[0], await fetchFile(video.audioUrl[1]));
+ message = 'Merging Audio';
+ await ffmpeg.exec([
+ '-i',
+ 'middle.' + format,
+ '-r',
+ video.fps.toString(),
+ '-i',
+ 'audio.' + video.audioUrl[0],
+ '-c:v',
+ 'copy',
+ '-map',
+ '0:v:0',
+ '-map',
+ '1:a:0',
+ '-frames:v',
+ video.length.toString(),
+ 'output.' + format
+ ]);
+ message = 'Removing videoless file';
+ await ffmpeg.deleteFile('middle.' + format);
+ } else await ffmpeg.rename('middle.' + format, 'output.' + format);
+ message = 'Reading File';
+ const data = await ffmpeg.readFile('output.' + format);
+ console.log('done');
+ videoUrl = URL.createObjectURL(
+ // @ts-ignore bufferlike is good enuf
+ new Blob([(data as Uint8Array).buffer], { type: 'video/' + format })
+ );
+ message = 'Disposing ffmpeg state';
+ await ffmpeg.deleteFile('output.' + format);
+ location.href = videoUrl;
+ };
+ $effect(() => {
+ if (canvas && VideoImplementation) start();
+ });
+ onDestroy(() => (active = false));
+</script>
+
+<div class="flex flex-col h-screen w-screen">
+ {#if videoUrl}
+ <!-- svelte-ignore a11y_media_has_caption -->
+ <video src={videoUrl} controls class="max-w-screen max-h-screen flex-1"></video>
+ {:else}
+ <div class="flex-1 relative">
+ <div class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
+ <canvas class="pointer-events-none bg-black" bind:this={canvas}>
+ Your browser doesn't support the canvas API.
+ </canvas>
+ </div>
+ </div>
+ {/if}
+ <p class="p-4">
+ {message}
+ </p>
+</div>
diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg
new file mode 100644
index 0000000..cc5dc66
--- /dev/null
+++ b/src/lib/assets/favicon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg> \ No newline at end of file
diff --git a/src/lib/index.ts b/src/lib/index.ts
new file mode 100644
index 0000000..856f2b6
--- /dev/null
+++ b/src/lib/index.ts
@@ -0,0 +1 @@
+// place files you want to import through the `$lib` alias in this folder.
diff --git a/src/lib/vendor/svelte-range-slider/README b/src/lib/vendor/svelte-range-slider/README
new file mode 100644
index 0000000..b17797b
--- /dev/null
+++ b/src/lib/vendor/svelte-range-slider/README
@@ -0,0 +1 @@
+https://github.com/roycrippen4/svelte-range-slider/tree/master
diff --git a/src/lib/vendor/svelte-range-slider/range-pips.svelte b/src/lib/vendor/svelte-range-slider/range-pips.svelte
new file mode 100644
index 0000000..418fc7e
--- /dev/null
+++ b/src/lib/vendor/svelte-range-slider/range-pips.svelte
@@ -0,0 +1,303 @@
+<script lang="ts" module>
+ export interface PipsProps {
+ min?: number;
+ max?: number;
+ step?: number;
+ values?: number[];
+ vertical?: boolean;
+ reversed?: boolean;
+ hoverable?: boolean;
+ disabled?: boolean;
+ pipstep?: number;
+ prefix?: string;
+ suffix?: string;
+ focus?: boolean;
+ range?: undefined | boolean | 'min' | 'max';
+ all?: undefined | boolean | 'pip' | 'label';
+ first?: boolean | 'pip' | 'label';
+ last?: boolean | 'pip' | 'label';
+ rest?: boolean | 'pip' | 'label';
+ percentOf: (v: number) => number;
+ fixFloat: (v: number) => number;
+ orientationStart?: 'top' | 'bottom' | 'left' | 'right';
+ orientationEnd?: 'top' | 'bottom' | 'left' | 'right';
+ formatter?: (v: number, i: number, p: number) => string;
+ moveHandle: undefined | ((index: number | undefined, value: number) => number);
+ normalisedClient: (e: MouseEvent | TouchEvent) => { x: number; y: number };
+ }
+</script>
+
+<script lang="ts">
+ let {
+ range = false,
+ min = 0,
+ max = 100,
+ step = 1,
+ values = [(max + min) / 2],
+ vertical = false,
+ reversed = false,
+ hoverable = true,
+ disabled = false,
+ pipstep,
+ all = true,
+ first,
+ last,
+ rest,
+ prefix = '',
+ suffix = '',
+ focus,
+ orientationStart,
+ // eslint-disable-next-line no-unused-vars
+ formatter = (v, i, p) => v.toString(),
+ percentOf,
+ moveHandle,
+ fixFloat,
+ normalisedClient
+ }: PipsProps = $props();
+
+ let clientStart = $state({ x: 0, y: 0 });
+ let pipStep = $derived(
+ pipstep ||
+ ((max - min) / step >= (vertical ? 50 : 100) ? (max - min) / (vertical ? 10 : 20) : 1)
+ );
+ let pipCount = $derived(parseInt(((max - min) / (step * pipStep)).toString(), 10));
+ let pipVal = $derived((val: number) => fixFloat(min + val * step * pipStep));
+ let isSelected = $derived((val: number) => values.some((v) => fixFloat(v) === fixFloat(val)));
+ let inRange = $derived((val: number) => {
+ if (range === 'min') {
+ return values[0] > val;
+ }
+ if (range === 'max') {
+ return values[0] < val;
+ }
+ if (range) {
+ return values[0] < val && values[1] > val;
+ }
+ });
+
+ /**
+ * function to run when the user clicks on a label
+ * we store the original client position so we can check if the user has moved the mouse/finger
+ * @param {MouseEvent} e the event from browser
+ **/
+ const labelDown = (e: MouseEvent) => {
+ clientStart = { x: e.clientX, y: e.clientY };
+ };
+
+ /**
+ * function to run when the user releases the mouse/finger
+ * we check if the user has moved the mouse/finger, if not we "click" the label
+ * and move the handle it to the label position
+ * @param {number} val the value of the label
+ * @param {MouseEvent|TouchEvent} e the event from browser
+ */
+ function labelUp(val: number, e: MouseEvent | TouchEvent) {
+ if (disabled) {
+ return;
+ }
+
+ const clientPos = normalisedClient(e);
+ const distanceMoved = Math.sqrt(
+ Math.pow(clientStart.x - clientPos.x, 2) + Math.pow(clientStart.y - clientPos.y, 2)
+ );
+
+ if (clientStart && distanceMoved <= 5) {
+ moveHandle?.(undefined, val);
+ }
+ }
+</script>
+
+<div
+ class="rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6"
+ class:disabled
+ class:hoverable
+ class:vertical
+ class:reversed
+ class:focus
+>
+ {#if (all && first !== false) || first}
+ <span
+ class="pip-680f0f01-664b-43b5-9e1c-789449c63c62 first"
+ class:selected={isSelected(min)}
+ class:in-range={inRange(min)}
+ style="{orientationStart}: 0%;"
+ onpointerdown={labelDown}
+ onpointerup={(e) => labelUp(min, e)}
+ >
+ {#if all === 'label' || first === 'label'}
+ <span class="pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b">
+ {prefix}{formatter(fixFloat(min), 0, 0)}{suffix}
+ </span>
+ {/if}
+ </span>
+ {/if}
+
+ {#if (all && rest !== false) || rest}
+ <!-- eslint-disable-next-line no-unused-vars -->
+ {#each Array(pipCount + 1) as _, i}
+ {#if pipVal(i) !== min && pipVal(i) !== max}
+ <span
+ class="pip-680f0f01-664b-43b5-9e1c-789449c63c62"
+ class:selected={isSelected(pipVal(i))}
+ class:in-range={inRange(pipVal(i))}
+ style="{orientationStart}: {percentOf(pipVal(i))}%;"
+ onpointerdown={labelDown}
+ onpointerup={(e) => labelUp(pipVal(i), e)}
+ >
+ {#if all === 'label' || rest === 'label'}
+ <span class="pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b">
+ {prefix}{formatter(pipVal(i), i, percentOf(pipVal(i)))}{suffix}
+ </span>
+ {/if}
+ </span>
+ {/if}
+ {/each}
+ {/if}
+
+ {#if (all && last !== false) || last}
+ <span
+ class="pip last"
+ class:selected={isSelected(max)}
+ class:in-range={inRange(max)}
+ style="{orientationStart}: 100%;"
+ onpointerdown={labelDown}
+ onpointerup={(e) => labelUp(max, e)}
+ >
+ {#if all === 'label' || last === 'label'}
+ <span class="pipVal">
+ {prefix}{formatter(fixFloat(max), pipCount, 100)}{suffix}
+ </span>
+ {/if}
+ </span>
+ {/if}
+</div>
+
+<style>
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28) {
+ --pip: var(--range-pip, lightslategray);
+ --pip-text: var(--range-pip-text, var(--pip));
+ --pip-active: var(--range-pip-active, darkslategrey);
+ --pip-active-text: var(--range-pip-active-text, var(--pip-active));
+ --pip-hover: var(--range-pip-hover, darkslategrey);
+ --pip-hover-text: var(--range-pip-hover-text, var(--pip-hover));
+ --pip-in-range: var(--range-pip-in-range, var(--pip-active));
+ --pip-in-range-text: var(--range-pip-in-range-text, var(--pip-active-text));
+ }
+ :global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6) {
+ position: absolute;
+ height: 1em;
+ left: 0;
+ right: 0;
+ bottom: -1em;
+ }
+ :global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.vertical) {
+ height: auto;
+ width: 1em;
+ left: 100%;
+ right: auto;
+ top: 0;
+ bottom: 0;
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip-680f0f01-664b-43b5-9e1c-789449c63c62
+ ) {
+ height: 0.4em;
+ position: absolute;
+ top: 0.25em;
+ width: 1px;
+ white-space: nowrap;
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.vertical
+ .pip-680f0f01-664b-43b5-9e1c-789449c63c62
+ ) {
+ height: 1px;
+ width: 0.4em;
+ left: 0.25em;
+ top: auto;
+ bottom: auto;
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
+ ) {
+ position: absolute;
+ top: 0.4em;
+ transform: translate(-50%, 25%);
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.vertical
+ .pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
+ ) {
+ position: absolute;
+ top: 0;
+ left: 0.4em;
+ transform: translate(25%, -50%);
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip-680f0f01-664b-43b5-9e1c-789449c63c62
+ ) {
+ transition: all 0.15s ease;
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
+ ) {
+ transition:
+ all 0.15s ease,
+ font-weight 0s linear;
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip-680f0f01-664b-43b5-9e1c-789449c63c62
+ ) {
+ color: var(--pip-text, lightslategray);
+ background-color: var(--pip, lightslategray);
+ }
+ :global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip.selected) {
+ color: var(--pip-active-text, darkslategrey);
+ background-color: var(--pip-active, darkslategrey);
+ }
+ :global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.hoverable:not(.disabled) .pip:hover) {
+ color: var(--pip-hover-text, darkslategrey);
+ background-color: var(--pip-hover, darkslategrey);
+ }
+ :global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip.in-range) {
+ color: var(--pip-in-range-text, darkslategrey);
+ background-color: var(--pip-in-range, darkslategrey);
+ }
+ :global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6 .pip.selected) {
+ height: 0.75em;
+ }
+ :global(.rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.vertical .pip.selected) {
+ height: 1px;
+ width: 0.75em;
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6
+ .pip.selected
+ .pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
+ ) {
+ font-weight: bold;
+ top: 0.75em;
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.vertical
+ .pip.selected
+ .pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
+ ) {
+ top: 0;
+ left: 0.75em;
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.hoverable:not(.disabled)
+ .pip:not(.selected):hover
+ ) {
+ transition: none;
+ }
+ :global(
+ .rangePips-f75c52e3-b799-4c81-8238-035d862cc2e6.hoverable:not(.disabled)
+ .pip:not(.selected):hover
+ .pipVal-c41e7185-de59-40a7-90f2-e3d98b1e844b
+ ) {
+ transition: none;
+ font-weight: bold;
+ }
+</style>
diff --git a/src/lib/vendor/svelte-range-slider/range-slider.svelte b/src/lib/vendor/svelte-range-slider/range-slider.svelte
new file mode 100644
index 0000000..7f522e2
--- /dev/null
+++ b/src/lib/vendor/svelte-range-slider/range-slider.svelte
@@ -0,0 +1,1026 @@
+<script lang="ts" module>
+ export type ChangeEvent = {
+ activeHandle: number;
+ startValue: number;
+ previousValue: number;
+ value: number;
+ values: number[];
+ };
+
+ export type StartEvent = { activeHandle: number; value: number; values: number[] };
+
+ export type StopEvent = {
+ activeHandle: number;
+ startValue: number;
+ value: number;
+ values: number[];
+ };
+
+ export interface RangeSliderProps {
+ range?: boolean | 'min' | 'max';
+ onchange?: (event: ChangeEvent) => void;
+ onstart?: (event: StartEvent) => void;
+ onstop?: (event: StopEvent) => void;
+ pushy?: boolean;
+ min?: number;
+ max?: number;
+ ariaLabels?: string[];
+ precision?: number;
+ springOptions?: { stiffness: number; damping: number };
+ id?: string;
+ prefix?: string;
+ suffix?: string;
+ pips?: boolean;
+ pipstep?: number;
+ all?: boolean | 'pip' | 'label';
+ first?: boolean | 'pip' | 'label';
+ last?: boolean | 'pip' | 'label';
+ rest?: boolean | 'pip' | 'label';
+ step?: number;
+ value?: number;
+ values?: number[];
+ vertical?: boolean;
+ float?: boolean;
+ reversed?: boolean;
+ hoverable?: boolean;
+ disabled?: boolean;
+ formatter?: (value: number, index: number, percent: number) => string;
+ handleFormatter?: (value: number, index: number, percent: number) => string;
+ }
+</script>
+
+<script lang="ts">
+ import { spring } from 'svelte/motion';
+ import RangePips from './range-pips.svelte';
+
+ let {
+ range = false,
+ pushy = false,
+ min = 0,
+ max = 100,
+ ariaLabels = [],
+ precision = 2,
+ springOptions = { stiffness: 0.15, damping: 0.4 },
+ id = '',
+ prefix = '',
+ suffix = '',
+ pips = false,
+ pipstep,
+ all,
+ first,
+ last,
+ rest,
+ step = 1,
+ value = $bindable(0),
+ values = $bindable([(max + min) / 2]),
+ vertical = false,
+ float = false,
+ reversed = false,
+ hoverable = true,
+ disabled = false,
+ onchange,
+ onstart,
+ onstop,
+ formatter = (value: { toString: () => string }) => value.toString(),
+ handleFormatter = formatter
+ }: RangeSliderProps = $props();
+
+ if (value) {
+ values = [value];
+ }
+
+ let slider: Element | undefined = $state(undefined);
+ let valueLength = $state(0);
+ let focus = $state(false);
+ let handleActivated = $state(false);
+ let handlePressed = $state(false);
+ let keyboardActive = $state(false);
+ let activeHandle = $state(values.length - 1);
+
+ let startValue: number | undefined = $state();
+
+ let previousValue: number | undefined = $state();
+
+ /**
+ * make sure the value is coerced to a float value
+ * @param {number} v the value to fix
+ * @return {number} a float version of the input
+ **/
+ const fixFloat = (v: number): number => parseFloat((+v).toFixed(precision));
+
+ $effect(() => {
+ // check that "values" is an array, or set it as array to prevent any errors in springs, or range trimming
+ if (!Array.isArray(values)) {
+ values = [(max + min) / 2];
+ console.error(
+ "'values' prop should be an Array (https://github.com/simeydotme/svelte-range-slider-pips#slider-props)"
+ );
+ }
+
+ // trim the range so it remains as a min/max (only 2 handles)
+ // and also align the handles to the steps
+ const trimmedAlignedValues = trimRange(values.map((v) => alignValueToStep(v)));
+ if (
+ !(values.length === trimmedAlignedValues.length) ||
+ !values.every((element, index) => fixFloat(element) === trimmedAlignedValues[index])
+ ) {
+ values = trimmedAlignedValues;
+ }
+
+ // check if the valueLength (length of values[]) has changed,
+ // because if so we need to re-seed the spring function with the new values array.
+ if (valueLength !== values.length) {
+ // set the initial spring values when the slider initialises, or when values array length has changed
+ springPositions = spring(
+ values.map((v) => percentOf(v)),
+ springOptions
+ );
+ } else {
+ // update the value of the spring function for animated handles whenever the values has updated
+ springPositions.set(values.map((v) => percentOf(v)));
+ }
+ // set the valueLength for the next check
+ valueLength = values.length;
+
+ if (values.length > 1 && !Array.isArray(ariaLabels)) {
+ console.warn(
+ `'ariaLabels' prop should be an Array (https://github.com/simeydotme/svelte-range-slider-pips#slider-props)`
+ );
+ }
+ });
+
+ /**
+ * take in a value, and then calculate that value's percentage
+ * of the overall range (min-max);
+ * @param {number} val the value we're getting percent for
+ * @return {number} the percentage value
+ **/
+ const percentOf = (/** @type {number} */ val: number): number => {
+ let percent = ((val - min) / (max - min)) * 100;
+
+ if (isNaN(percent) || percent <= 0) {
+ return 0;
+ }
+
+ if (percent >= 100) {
+ return 100;
+ }
+
+ return fixFloat(percent);
+ };
+
+ /**
+ * clamp a value from the range so that it always
+ * falls within the min/max values
+ * @param {number} val the value to clamp
+ * @return {number} the value after it's been clamped
+ **/
+ const clampValue = (/** @type {number} */ val: number): number => {
+ // return the min/max if outside of that range
+ return val <= min ? min : val >= max ? max : val;
+ };
+
+ /**
+ * align the value with the steps so that it
+ * always sits on the closest (above/below) step
+ * @param {number} val the value to align
+ * @return {number} the value after it's been aligned
+ **/
+ const alignValueToStep = (/** @type {number} */ val: number): number => {
+ // sanity check for performance
+ if (val <= min) {
+ return fixFloat(min);
+ }
+
+ if (val >= max) {
+ return fixFloat(max);
+ }
+
+ val = fixFloat(val);
+
+ // find the middle-point between steps and see if the value is closer to the next step, or previous step
+ let remainder = (val - min) % step;
+ let aligned = val - remainder;
+
+ if (Math.abs(remainder) * 2 >= step) {
+ aligned += remainder > 0 ? step : -step;
+ }
+
+ aligned = clampValue(aligned); // make sure the value is within acceptable limits
+
+ // make sure the returned value is set to the precision desired
+ // this is also because javascript often returns weird floats
+ // when dealing with odd numbers and percentages
+ return fixFloat(aligned);
+ };
+
+ /**
+ * the orientation of the handles/pips based on the
+ * input values of vertical and reversed
+ * @type {"top"|"bottom"|"left"|"right"} orientationStart
+ **/
+ let orientationStart: 'top' | 'bottom' | 'left' | 'right' = $derived(
+ vertical ? (reversed ? 'top' : 'bottom') : reversed ? 'right' : 'left'
+ );
+ let orientationEnd = $derived(
+ vertical ? (reversed ? 'bottom' : 'top') : reversed ? 'left' : 'right'
+ );
+
+ /**
+ * helper function to get the index of an element in it's DOM container
+ * @param {Element|null} el dom object reference we want the index of
+ * @returns {number} the index of the input element
+ **/
+ function index(el: Element | null): number {
+ if (!el) {
+ return -1;
+ }
+
+ let i = 0;
+ while ((el = el.previousElementSibling)) {
+ i++;
+ }
+ return i;
+ }
+
+ /**
+ * normalise a mouse or touch event to return the
+ * client (x/y) object for that event
+ * @param {MouseEvent|TouchEvent} e a mouse/touch event to normalise
+ * @returns {{ x: number, y: number }} normalised event client object (x,y)
+ **/
+ function normalisedClient(e: MouseEvent | TouchEvent): { x: number; y: number } {
+ if (e.type.includes('touch')) {
+ const touchEvent = e as TouchEvent;
+ const touch = touchEvent.touches[0] || touchEvent.changedTouches[0];
+ return { x: touch.clientX, y: touch.clientY };
+ } else {
+ const mouseEvent = e as MouseEvent;
+ return { x: mouseEvent.clientX, y: mouseEvent.clientY };
+ }
+ }
+
+ /**
+ * check if an element is a handle on the slider
+ * @param {Element} el dom object reference we want to check
+ * @returns {boolean}
+ **/
+ function targetIsHandle(el: Element): boolean {
+ if (!slider) return false;
+ const handles = [...slider.querySelectorAll('.handle')];
+ const isHandle = handles.includes(el);
+ const isChild = handles.some((handle) => handle.contains(el));
+ return isHandle || isChild;
+ }
+
+ /**
+ * trim the values array based on whether the property
+ * for 'range' is 'min', 'max', or truthy. This is because we
+ * do not want more than one handle for a min/max range, and we do
+ * not want more than two handles for a true range.
+ * @param {number[]} values the input values for the rangeSlider
+ * @return {number[]} the range array for creating a rangeSlider
+ **/
+ function trimRange(values: number[]): number[] {
+ if (range === 'min' || range === 'max') {
+ return values.slice(0, 1);
+ }
+ if (range) {
+ return values.slice(0, 2);
+ }
+
+ return values;
+ }
+
+ /**
+ * helper to return the slider dimensions for finding
+ * the closest handle to user interaction
+ * @return {DOMRect} the range slider DOM client rect
+ **/
+ function getSliderDimensions(): DOMRect | undefined {
+ return slider?.getBoundingClientRect();
+ }
+
+ /**
+ * helper to return closest handle to user interaction
+ * @param {{ x: number, y: number }} clientPos the client{x,y} positions to check against
+ * @return {number} the index of the closest handle to clientPos
+ **/
+ function getClosestHandle(clientPos: { x: number; y: number }): number {
+ // first make sure we have the latest dimensions
+ // of the slider, as it may have changed size
+ const dims = getSliderDimensions();
+ if (!dims) throw new Error('No Slider Dimensions yet.');
+ // calculate the interaction position, percent and value
+ let handlePos = 0;
+ let handlePercent = 0;
+ let handleVal = 0;
+ if (vertical) {
+ handlePos = clientPos.y - dims.top;
+ handlePercent = (handlePos / dims.height) * 100;
+ handlePercent = reversed ? handlePercent : 100 - handlePercent;
+ } else {
+ handlePos = clientPos.x - dims.left;
+ handlePercent = (handlePos / dims.width) * 100;
+ handlePercent = reversed ? 100 - handlePercent : handlePercent;
+ }
+ handleVal = ((max - min) / 100) * handlePercent + min;
+
+ // if we have a range, and the handles are at the same
+ // position, we want a simple check if the interaction
+ // value is greater than return the second handle
+ if (range === true && values[0] === values[1]) {
+ if (handleVal > values[1]) {
+ return 1;
+ }
+
+ return 0;
+
+ // if there are multiple handles, and not a range, then
+ // we sort the handles values, and return the first one closest
+ // to the interaction value
+ }
+
+ return values.indexOf(
+ [...values].sort((a, b) => Math.abs(handleVal - a) - Math.abs(handleVal - b))[0]
+ );
+ }
+
+ /**
+ * take the interaction position on the slider, convert
+ * it to a value on the range, and then send that value
+ * through to the moveHandle() method to set the active
+ * handle's position
+ * @param {{ x: number, y: number }} clientPos the client{x,y} of the interaction
+ **/
+ function handleInteract(clientPos: { x: number; y: number }) {
+ // first make sure we have the latest dimensions
+ // of the slider, as it may have changed size
+ const dims = getSliderDimensions();
+ if (!dims) throw new Error('No Slider Dimensions yet.');
+ // calculate the interaction position, percent and value
+ let handlePos = 0;
+ let handlePercent = 0;
+ let handleVal = 0;
+ if (vertical) {
+ handlePos = clientPos.y - dims.top;
+ handlePercent = (handlePos / dims.height) * 100;
+ handlePercent = reversed ? handlePercent : 100 - handlePercent;
+ } else {
+ handlePos = clientPos.x - dims.left;
+ handlePercent = (handlePos / dims.width) * 100;
+ handlePercent = reversed ? 100 - handlePercent : handlePercent;
+ }
+ handleVal = ((max - min) / 100) * handlePercent + min;
+ // move handle to the value
+ moveHandle(activeHandle, handleVal);
+ }
+
+ let lastSetValue = NaN;
+ /**
+ * move a handle to a specific value, respecting the clamp/align rules
+ * @param {number} index the index of the handle we want to move
+ * @param {number} handleValue the value to move the handle to
+ * @return {number} the value that was moved to (after alignment/clamping)
+ **/
+ function moveHandle(index: number | undefined, handleValue: number): number {
+ // align & clamp the value so we're not doing extra
+ // calculation on an out-of-range value down below
+ handleValue = alignValueToStep(handleValue);
+ // use the active handle if handle index is not provided
+ if (typeof index === 'undefined') {
+ index = activeHandle;
+ }
+ // if this is a range slider perform special checks
+ if (range) {
+ // restrict the handles of a range-slider from
+ // going past one-another unless "pushy" is true
+ if (index === 0 && handleValue > values[1]) {
+ if (pushy) {
+ values[1] = handleValue;
+ } else {
+ handleValue = values[1];
+ }
+ } else if (index === 1 && handleValue < values[0]) {
+ if (pushy) {
+ values[0] = handleValue;
+ } else {
+ handleValue = values[0];
+ }
+ }
+ }
+
+ // if the value has changed, update it
+ if (values[index] !== handleValue) {
+ values[index] = handleValue;
+ }
+
+ // fire the change event when the handle moves,
+ // and store the previous value for the next time
+ if (previousValue !== handleValue) {
+ handleOnChange();
+ previousValue = handleValue;
+ }
+ lastSetValue = handleValue;
+ value = handleValue;
+ return handleValue;
+ }
+ $effect(() => {
+ if (value !== lastSetValue) moveHandle(undefined, value);
+ });
+
+ /**
+ * helper to find the beginning range value for use with css style
+ * @param {number[]} values the input values for the rangeSlider
+ * @return {number} the beginning of the range
+ **/
+ function rangeStart(values: number[]): number {
+ if (range === 'min') {
+ return 0;
+ }
+
+ return values[0];
+ }
+
+ /**
+ * helper to find the ending range value for use with css style
+ * @param {array} values the input values for the rangeSlider
+ * @return {number} the end of the range
+ **/
+ function rangeEnd(values: Array<any>): number {
+ if (range === 'max') {
+ return 0;
+ }
+
+ if (range === 'min') {
+ return 100 - values[0];
+ }
+
+ return 100 - values[1];
+ }
+
+ /**
+ * helper to take a string of html and return only the text
+ * @param {string} possibleHtml the string that may contain html
+ * @return {string} the text from the input
+ */
+ function pureText(possibleHtml: string): string {
+ return `${possibleHtml}`.replace(/<[^>]*>/g, '');
+ }
+
+ /**
+ * when the user has unfocussed (blurred) from the
+ * slider, deactivate all handles
+ **/
+ function sliderBlurHandle() {
+ if (!keyboardActive) {
+ return;
+ }
+
+ focus = false;
+ handleActivated = false;
+ handlePressed = false;
+ }
+
+ /**
+ * when the user focusses the handle of a slider
+ * set it to be active
+ * @param {Event} e the event from browser
+ **/
+ function sliderFocusHandle(e: Event) {
+ if (disabled) {
+ return;
+ }
+
+ const target = e.target as HTMLElement;
+ activeHandle = index(target);
+ focus = true;
+ }
+
+ /**
+ * handle the keyboard accessible features by checking the
+ * input type, and modfier key then moving handle by appropriate amount
+ * @param {KeyboardEvent} e the event from browser
+ **/
+ function sliderKeydown(e: KeyboardEvent) {
+ if (disabled) {
+ return;
+ }
+
+ const target = e.target as HTMLElement;
+ const handle = index(target);
+ let jump = e.ctrlKey || e.metaKey || e.shiftKey ? step * 10 : step;
+ let prevent = false;
+
+ if (e.key === 'PageDown' || e.key === 'PageUp') {
+ jump *= 10;
+ }
+
+ if (e.key === 'ArrowRight' || e.key === 'ArrowUp') {
+ moveHandle(handle, values[handle] + jump);
+ prevent = true;
+ }
+
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') {
+ moveHandle(handle, values[handle] - jump);
+ prevent = true;
+ }
+
+ if (e.key === 'Home') {
+ moveHandle(handle, min);
+ prevent = true;
+ }
+
+ if (e.key === 'End') {
+ moveHandle(handle, max);
+ prevent = true;
+ }
+
+ if (prevent) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ }
+
+ /**
+ * function to run when the user touches the slider element anywhere
+ * @param {MouseEvent|TouchEvent} e the event from browser
+ **/
+ function sliderInteractStart(e: MouseEvent | TouchEvent) {
+ if (disabled) {
+ return;
+ }
+
+ const element = e.target as HTMLElement;
+ const clientPos = normalisedClient(e);
+ // set the closest handle as active
+ focus = true;
+ handleActivated = true;
+ handlePressed = true;
+ activeHandle = getClosestHandle(clientPos);
+
+ // fire the start event
+ startValue = previousValue = alignValueToStep(values[activeHandle]);
+ handleOnStart();
+
+ // for touch devices we want the handle to instantly
+ // move to the position touched for more responsive feeling
+ if (e.type === 'touchstart' && !element.matches('.pipVal')) {
+ handleInteract(clientPos);
+ }
+ }
+
+ /**
+ * function to run when the user stops touching
+ * down on the slider element anywhere
+ * @param {Event} e the event from browser
+ **/
+ function sliderInteractEnd(e: Event) {
+ // fire the stop event for touch devices
+ if (e.type === 'touchend') {
+ handleOnStop();
+ }
+ handlePressed = false;
+ }
+
+ /**
+ * unfocus the slider if the user clicked off of
+ * it, somewhere else on the screen
+ * @param {MouseEvent|TouchEvent} e the event from browser
+ **/
+ function bodyInteractStart(e: MouseEvent | TouchEvent) {
+ keyboardActive = false;
+ const target = e.target as HTMLElement;
+ if (slider && focus && e.target !== slider && !slider.contains(target)) {
+ focus = false;
+ }
+ }
+
+ /**
+ * send the clientX through to handle the interaction
+ * whenever the user moves across screen while active
+ * @param {MouseEvent|TouchEvent} e the event from browser
+ **/
+ function bodyInteract(e: MouseEvent | TouchEvent) {
+ if (!disabled) {
+ if (handleActivated) {
+ handleInteract(normalisedClient(e));
+ }
+ }
+ }
+
+ /**
+ * if user triggers mouseup on the body while
+ * a handle is active (without moving) then we
+ * trigger an interact event there
+ * @param {MouseEvent|TouchEvent} e the event from browser
+ **/
+ function bodyMouseUp(e: MouseEvent | TouchEvent) {
+ if (!disabled) {
+ const el = e.target as HTMLElement;
+ // this only works if a handle is active, which can
+ // only happen if there was sliderInteractStart triggered
+ // on the slider, already
+ if (handleActivated) {
+ if (slider && (el === slider || slider.contains(el))) {
+ focus = true;
+ // don't trigger interact if the target is a handle (no need) or
+ // if the target is a label (we want to move to that value from rangePips)
+ if (!targetIsHandle(el) && !el.matches('.pipVal')) {
+ handleInteract(normalisedClient(e));
+ }
+ }
+ // fire the stop event for mouse device
+ // when the body is triggered with an active handle
+ handleOnStop();
+ }
+ }
+ handleActivated = false;
+ handlePressed = false;
+ }
+
+ /**
+ * @param {KeyboardEvent} e
+ */
+ function bodyKeyDown(e: KeyboardEvent) {
+ if (disabled) {
+ return;
+ }
+
+ const target = e.target as HTMLElement;
+
+ if (slider && (e.target === slider || slider.contains(target))) {
+ keyboardActive = true;
+ }
+ }
+
+ function handleOnStop() {
+ if (disabled || !onstop || typeof onstop !== 'function') {
+ return;
+ }
+
+ onstop({
+ activeHandle,
+ startValue: startValue ?? 0,
+ value: values[activeHandle],
+ values: values.map((v) => alignValueToStep(v))
+ });
+ }
+
+ function handleOnStart() {
+ if (disabled || !onstart || typeof onstart !== 'function') {
+ return;
+ }
+
+ onstart({
+ activeHandle,
+ value: startValue ?? 0,
+ values: values.map((v) => alignValueToStep(v))
+ });
+ }
+
+ function handleOnChange() {
+ if (disabled || !onchange || typeof onchange !== 'function') {
+ return;
+ }
+
+ onchange({
+ activeHandle,
+ startValue: startValue ?? 0,
+ previousValue: typeof previousValue === 'undefined' ? (startValue ?? 0) : previousValue,
+ value: values[activeHandle],
+ values: values.map((v) => alignValueToStep(v))
+ });
+ }
+
+ /** @type {import('svelte/motion').Spring<number[]>} */
+ let springPositions: import('svelte/motion').Spring<number[]> = spring(
+ values.map((v) => percentOf(v)),
+ springOptions
+ );
+</script>
+
+<div
+ {id}
+ bind:this={slider}
+ role="none"
+ class="_rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28"
+ class:range
+ class:disabled
+ class:hoverable
+ class:vertical
+ class:reversed
+ class:focus
+ class:min={range === 'min'}
+ class:max={range === 'max'}
+ class:pips
+ class:pip-labels={all === 'label' || first === 'label' || last === 'label' || rest === 'label'}
+ onmousedown={sliderInteractStart}
+ onmouseup={sliderInteractEnd}
+>
+ {#each values as value, index}
+ <span
+ role="slider"
+ class="rangeHandle"
+ class:active={focus && activeHandle === index}
+ class:press={handlePressed && activeHandle === index}
+ data-handle={index}
+ onblur={sliderBlurHandle}
+ onfocus={sliderFocusHandle}
+ onkeydown={sliderKeydown}
+ style="{orientationStart}: {$springPositions[index]}%; z-index: {activeHandle === index
+ ? 3
+ : 2};"
+ aria-label={ariaLabels[index]}
+ aria-valuemin={range === true && index === 1 ? values[0] : min}
+ aria-valuemax={range === true && index === 0 ? values[1] : max}
+ aria-valuenow={value}
+ aria-valuetext="{prefix}{pureText(handleFormatter(value, index, percentOf(value)))}{suffix}"
+ aria-orientation={vertical ? 'vertical' : 'horizontal'}
+ aria-disabled={disabled}
+ tabindex={disabled ? -1 : 0}
+ >
+ <span class="rangeNub"></span>
+ {#if float}
+ <span class="rangeFloat">
+ {prefix}{handleFormatter(value, index, percentOf(value))}{suffix}
+ </span>
+ {/if}
+ </span>
+ {/each}
+
+ {#if range}
+ <span
+ class="rangeBar"
+ style="{orientationStart}: {rangeStart($springPositions)}%;
+ {orientationEnd}: {rangeEnd($springPositions)}%;"
+ ></span>
+ {/if}
+
+ {#if pips}
+ <RangePips
+ {values}
+ {min}
+ {max}
+ {step}
+ {range}
+ {vertical}
+ {reversed}
+ {orientationStart}
+ {hoverable}
+ {disabled}
+ {all}
+ {first}
+ {last}
+ {rest}
+ {pipstep}
+ {prefix}
+ {suffix}
+ {formatter}
+ {focus}
+ {percentOf}
+ {moveHandle}
+ {fixFloat}
+ {normalisedClient}
+ />
+ {/if}
+</div>
+
+<svelte:window
+ onmousedown={bodyInteractStart}
+ onmousemove={bodyInteract}
+ onmouseup={bodyMouseUp}
+ onkeydown={bodyKeyDown}
+/>
+
+<style>
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28) {
+ --slider: var(--range-slider, #d7dada);
+ --handle-inactive: var(--range-handle-inactive, #99a2a2);
+ --handle: var(--range-handle, #838de7);
+ --handle-focus: var(--range-handle-focus, #4a40d4);
+ --handle-border: var(--range-handle-border, var(--handle));
+ --range-inactive: var(--range-range-inactive, var(--handle-inactive));
+ --range: var(--range-range, var(--handle-focus));
+ --float-inactive: var(--range-float-inactive, var(--handle-inactive));
+ --float: var(--range-float, var(--handle-focus));
+ --float-text: var(--range-float-text, white);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28) {
+ position: relative;
+ border-radius: 100px;
+ height: 0.5em;
+ margin: 1em;
+ transition: opacity 0.2s ease;
+ user-select: none;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 *) {
+ user-select: none;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.pips) {
+ margin-bottom: 1.8em;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.pip-labels) {
+ margin-bottom: 2.8em;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.vertical) {
+ display: inline-block;
+ border-radius: 100px;
+ width: 0.5em;
+ min-height: 200px;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.vertical.pips) {
+ margin-right: 1.8em;
+ margin-bottom: 1em;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.vertical.pip-labels) {
+ margin-right: 2.8em;
+ margin-bottom: 1em;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeHandle) {
+ position: absolute;
+ display: block;
+ height: 1.4em;
+ width: 1.4em;
+ top: 0.25em;
+ bottom: auto;
+ transform: translateY(-50%) translateX(-50%);
+ z-index: 2;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.reversed .rangeHandle) {
+ transform: translateY(-50%) translateX(50%);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.vertical .rangeHandle) {
+ left: 0.25em;
+ top: auto;
+ transform: translateY(50%) translateX(-50%);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.vertical.reversed .rangeHandle) {
+ transform: translateY(-50%) translateX(-50%);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeNub),
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeHandle:before) {
+ position: absolute;
+ left: 0;
+ top: 0;
+ display: block;
+ border-radius: 10em;
+ height: 100%;
+ width: 100%;
+ transition: box-shadow 0.2s ease;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeHandle:before) {
+ content: '';
+ left: 1px;
+ top: 1px;
+ bottom: 1px;
+ right: 1px;
+ height: auto;
+ width: auto;
+ box-shadow: 0 0 0 0px var(--handle-border);
+ opacity: 0;
+ }
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.hoverable:not(.disabled)
+ .rangeHandle:hover:before
+ ) {
+ box-shadow: 0 0 0 8px var(--handle-border);
+ opacity: 0.2;
+ }
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.hoverable:not(.disabled)
+ .rangeHandle.press:before
+ ),
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.hoverable:not(.disabled)
+ .rangeHandle.press:hover:before
+ ) {
+ box-shadow: 0 0 0 12px var(--handle-border);
+ opacity: 0.4;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.range:not(.min):not(.max) .rangeNub) {
+ border-radius: 10em 10em 10em 1.6em;
+ }
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.range .rangeHandle:nth-of-type(1) .rangeNub
+ ) {
+ transform: rotate(-135deg);
+ }
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.range .rangeHandle:nth-of-type(2) .rangeNub
+ ) {
+ transform: rotate(45deg);
+ }
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.range.reversed
+ .rangeHandle:nth-of-type(1)
+ .rangeNub
+ ) {
+ transform: rotate(45deg);
+ }
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.range.reversed
+ .rangeHandle:nth-of-type(2)
+ .rangeNub
+ ) {
+ transform: rotate(-135deg);
+ }
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.range.vertical
+ .rangeHandle:nth-of-type(1)
+ .rangeNub
+ ) {
+ transform: rotate(135deg);
+ }
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.range.vertical
+ .rangeHandle:nth-of-type(2)
+ .rangeNub
+ ) {
+ transform: rotate(-45deg);
+ }
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.range.vertical.reversed
+ .rangeHandle:nth-of-type(1)
+ .rangeNub
+ ) {
+ transform: rotate(-45deg);
+ }
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.range.vertical.reversed
+ .rangeHandle:nth-of-type(2)
+ .rangeNub
+ ) {
+ transform: rotate(135deg);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeFloat) {
+ display: block;
+ position: absolute;
+ left: 50%;
+ top: -0.5em;
+ transform: translate(-50%, -100%);
+ font-size: 1em;
+ text-align: center;
+ opacity: 0;
+ pointer-events: none;
+ white-space: nowrap;
+ transition: all 0.2s ease;
+ font-size: 0.9em;
+ padding: 0.2em 0.4em;
+ border-radius: 0.2em;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeHandle.active .rangeFloat),
+ :global(
+ ._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.hoverable .rangeHandle:hover .rangeFloat
+ ) {
+ opacity: 1;
+ top: -0.2em;
+ transform: translate(-50%, -100%);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeBar) {
+ position: absolute;
+ display: block;
+ transition: background 0.2s ease;
+ border-radius: 1em;
+ height: 0.5em;
+ top: 0;
+ user-select: none;
+ z-index: 1;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.vertical .rangeBar) {
+ width: 0.5em;
+ height: auto;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28) {
+ background-color: var(--slider, #d7dada);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeBar) {
+ background-color: var(--range-inactive, #99a2a2);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.focus .rangeBar) {
+ background-color: var(--range, #838de7);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeNub) {
+ background-color: var(--handle-inactive, #99a2a2);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.focus .rangeNub) {
+ background-color: var(--handle, #838de7);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeHandle.active .rangeNub) {
+ background-color: var(--handle-focus, #4a40d4);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28 .rangeFloat) {
+ color: white;
+ color: var(--float-text);
+ background-color: var(--float-inactive, #99a2a2);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.focus .rangeFloat) {
+ background-color: var(--float, #4a40d4);
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.disabled) {
+ opacity: 0.5;
+ }
+ :global(._rangeslider-0f6d4a99-47b0-4108-8415-b2aefa867e28.disabled .rangeNub) {
+ background-color: var(--slider, #d7dada);
+ }
+</style>
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
new file mode 100644
index 0000000..8c56a3c
--- /dev/null
+++ b/src/routes/+layout.svelte
@@ -0,0 +1,12 @@
+<script lang="ts">
+ import '../app.css';
+ import favicon from '$lib/assets/favicon.svg';
+
+ let { children } = $props();
+</script>
+
+<svelte:head>
+ <link rel="icon" href={favicon} />
+</svelte:head>
+
+{@render children?.()}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
new file mode 100644
index 0000000..8d09da5
--- /dev/null
+++ b/src/routes/+page.svelte
@@ -0,0 +1,6 @@
+<script lang="ts">
+ import Editor from '$/lib/Player/Player.svelte';
+ import Video from '$/user';
+</script>
+
+<Editor {Video} />
diff --git a/src/routes/ffmpeg-test/+page.svelte b/src/routes/ffmpeg-test/+page.svelte
new file mode 100644
index 0000000..226fc3c
--- /dev/null
+++ b/src/routes/ffmpeg-test/+page.svelte
@@ -0,0 +1,45 @@
+<script lang="ts">
+ import { FFmpeg } from '@ffmpeg/ffmpeg';
+ // @ts-ignore
+ import type { LogEvent } from '@ffmpeg/ffmpeg/dist/esm/types';
+ import { fetchFile, toBlobURL } from '@ffmpeg/util';
+
+ let videoEl: HTMLVideoElement;
+
+ const baseURL = 'https://unpkg.com/@ffmpeg/core-mt@0.12.6/dist/esm';
+ const videoURL = 'https://raw.githubusercontent.com/ffmpegwasm/testdata/master/video-15s.avi';
+
+ let message = $state('Click Start to Transcode');
+
+ const transcode = async () => {
+ const ffmpeg = new FFmpeg();
+ message = 'Loading ffmpeg-core.js';
+ ffmpeg.on('log', ({ message: msg }: LogEvent) => {
+ message = msg;
+ console.log(message);
+ });
+ await ffmpeg.load({
+ coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
+ wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
+ workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, 'text/javascript')
+ });
+ message = 'Start transcoding';
+ await ffmpeg.writeFile('test.avi', await fetchFile(videoURL));
+ await ffmpeg.exec(['-i', 'test.avi', 'test.mp4']);
+ message = 'Complete transcoding';
+ const data = await ffmpeg.readFile('test.mp4');
+ console.log('done');
+ videoEl.src = URL.createObjectURL(
+ // @ts-ignore bufferlike is good enuf
+ new Blob([(data as Uint8Array).buffer], { type: 'video/mp4' })
+ );
+ };
+</script>
+
+<div>
+ <!-- svelte-ignore a11y_media_has_caption -->
+ <video bind:this={videoEl} controls></video>
+ <br />
+ <button onclick={transcode}>Start</button>
+ <p>{message}</p>
+</div>
diff --git a/src/routes/render/+page.svelte b/src/routes/render/+page.svelte
new file mode 100644
index 0000000..e9ecd91
--- /dev/null
+++ b/src/routes/render/+page.svelte
@@ -0,0 +1,6 @@
+<script lang="ts">
+ import Renderer from '$/lib/Renderer/Renderer.svelte';
+ import Video from '$/user';
+</script>
+
+<Renderer {Video} />
diff --git a/src/user/Sneky Snitch.mp4 b/src/user/Sneky Snitch.mp4
new file mode 100644
index 0000000..b9d53b5
--- /dev/null
+++ b/src/user/Sneky Snitch.mp4
Binary files differ
diff --git a/src/user/index.ts b/src/user/index.ts
new file mode 100644
index 0000000..2caf88c
--- /dev/null
+++ b/src/user/index.ts
@@ -0,0 +1,21 @@
+import { Video as BaseVideo, type FrameTime } from '$/lib/Player/Video';
+import SneakySnitchUrl from './Sneky Snitch.mp4?url'
+
+export default class Video extends BaseVideo {
+ public ctx!: CanvasRenderingContext2D
+ public init(): void | Promise<void> {
+ // this.resize(this.canvas.clientWidth,this.canvas.clientHeight)
+ this.resize(1920, 1080)
+ this.ctx = this.canvas.getContext('2d')!
+ }
+ public renderFrame(time: FrameTime): Promise<void> | void {
+ this.ctx.fillStyle = '#000'
+ this.ctx.fillRect(0, 0, this.w, this.h)
+ this.ctx.font = "50px Nunito";
+ this.ctx.fillStyle = '#fff'
+ this.ctx.fillText(`${time.seconds.toFixed(3)}`, 0, 50)
+ }
+ public fps = 30;
+ public length = 3 * this.fps;
+ public audioUrl = ['sneakysnitch.mp4', SneakySnitchUrl] as const
+}
diff --git a/static/robots.txt b/static/robots.txt
new file mode 100644
index 0000000..b6dd667
--- /dev/null
+++ b/static/robots.txt
@@ -0,0 +1,3 @@
+# allow crawling everything by default
+User-agent: *
+Disallow:
diff --git a/svelte.config.js b/svelte.config.js
new file mode 100644
index 0000000..0568a73
--- /dev/null
+++ b/svelte.config.js
@@ -0,0 +1,30 @@
+import adapter from '@sveltejs/adapter-static';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+import importMap from './import_map.json' with { type: 'json' };
+
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+ preprocess: vitePreprocess(),
+ kit: {
+ paths: {
+ base: '',
+ },
+ adapter: adapter({
+ fallback: '404.html',
+ }),
+ alias: Object.fromEntries(
+ Object.entries(importMap.imports)
+ .filter(([k]) => k !== '$lib' && k !== '$lib/')
+ .map(([k, v]) =>
+ v.startsWith('npm:')
+ ? 'npm:' + k === v
+ ? []
+ : [k, 'node_modules/' + v.substring(4)]
+ : [k, v],
+ )
+ .filter((v) => v.length !== 0),
+ ),
+ },
+};
+
+export default config;
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..c369c4c
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "./.svelte-kit/tsconfig.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ "moduleResolution": "bundler"
+ }
+ // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
+ // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
+ //
+ // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
+ // from the referenced tsconfig.json - TypeScript does not merge them in
+} \ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..d2f5e0a
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,23 @@
+import { sveltekit } from "@sveltejs/kit/vite";
+import { defineConfig } from "vite";
+import tailwindcss from '@tailwindcss/vite'
+
+const viteServerConfig: import('vite').Plugin = {
+ name: 'cors',
+ configureServer(server) {
+ server.middlewares.use((req, res, next) => {
+ res.setHeader("Access-Control-Allow-Origin", "*");
+ res.setHeader("Access-Control-Allow-Methods", "GET");
+ res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
+ res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
+ next();
+ });
+ }
+};
+
+export default defineConfig({
+ plugins: [sveltekit(), tailwindcss(), viteServerConfig],
+ optimizeDeps: {
+ exclude: ['@ffmpeg/ffmpeg', '@ffmpeg/util']
+ },
+});