Merge branch 'feat/merge-big-stuff'

This commit is contained in:
Maya 2025-09-03 00:00:50 +08:00
commit 237aa402cf
48 changed files with 2632 additions and 702 deletions

5
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"recommendations": [
"inlang.vs-code-extension"
]
}

373
bun.lock
View File

@ -2,59 +2,66 @@
"lockfileVersion": 1, "lockfileVersion": 1,
"workspaces": { "workspaces": {
"": { "": {
"name": "VERT", "name": "vert",
"dependencies": { "dependencies": {
"@bjorn3/browser_wasi_shim": "^0.4.1", "@bjorn3/browser_wasi_shim": "^0.4.2",
"@ffmpeg/ffmpeg": "^0.12.15", "@ffmpeg/ffmpeg": "^0.12.15",
"@ffmpeg/util": "^0.12.2", "@ffmpeg/util": "^0.12.2",
"@fontsource/azeret-mono": "^5.1.1", "@fontsource/azeret-mono": "^5.2.9",
"@fontsource/lexend": "^5.1.2", "@fontsource/lexend": "^5.2.9",
"@fontsource/radio-canada-big": "^5.1.1", "@fontsource/radio-canada-big": "^5.2.6",
"@imagemagick/magick-wasm": "^0.0.34", "@imagemagick/magick-wasm": "^0.0.35",
"@stripe/stripe-js": "^7.4.0", "@stripe/stripe-js": "^7.9.0",
"byte-data": "^19.0.1", "byte-data": "^19.0.1",
"client-zip": "^2.4.6", "client-zip": "^2.5.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-svelte": "^0.475.0", "lucide-svelte": "^0.542.0",
"music-metadata": "^11.0.0", "music-metadata": "^11.8.3",
"overlayscrollbars": "^2.12.0",
"overlayscrollbars-svelte": "^0.5.5",
"p-queue": "^8.1.0", "p-queue": "^8.1.0",
"riff-file": "^1.0.3", "riff-file": "^1.0.3",
"svelte-stripe": "^1.4.0", "svelte-stripe": "^1.4.0",
"vert-wasm": "^0.0.2", "vert-wasm": "^0.0.2",
"vite-plugin-wasm": "^3.4.1", "vite-plugin-wasm": "^3.5.0",
}, },
"devDependencies": { "devDependencies": {
"@poppanator/sveltekit-svg": "^5.0.0", "@inlang/paraglide-js": "2.2.0",
"@sveltejs/adapter-static": "^3.0.8", "@poppanator/sveltekit-svg": "^5.0.1",
"@sveltejs/kit": "^2.16.0", "@sveltejs/adapter-static": "^3.0.9",
"@sveltejs/kit": "^2.37.0",
"@sveltejs/vite-plugin-svelte": "^4.0.4", "@sveltejs/vite-plugin-svelte": "^4.0.4",
"@types/eslint": "^9.6.1", "@types/eslint": "^9.6.1",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.21",
"css-select": "5.1.0", "css-select": "5.1.0",
"eslint": "^9.18.0", "eslint": "^9.34.0",
"eslint-config-prettier": "^10.0.1", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^2.46.1", "eslint-plugin-svelte": "^2.46.1",
"globals": "^15.14.0", "globals": "^15.15.0",
"prettier": "^3.4.2", "prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.3.3", "prettier-plugin-svelte": "^3.4.0",
"prettier-plugin-tailwindcss": "^0.6.10", "prettier-plugin-tailwindcss": "^0.6.14",
"sass": "^1.83.4", "sass": "^1.91.0",
"svelte": "^5.19.0", "svelte": "^5.38.6",
"svelte-check": "^4.1.4", "svelte-check": "^4.3.1",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"typescript": "^5.7.3", "typescript": "^5.9.2",
"typescript-eslint": "^8.20.0", "typescript-eslint": "^8.42.0",
"vite": "^5.4.11", "vite": "^5.4.19",
"vite-plugin-top-level-await": "^1.5.0", "vite-plugin-top-level-await": "^1.6.0",
}, },
}, },
}, },
"trustedDependencies": [
"@swc/core",
"@parcel/watcher",
],
"packages": { "packages": {
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], "@bjorn3/browser_wasi_shim": ["@bjorn3/browser_wasi_shim@0.4.2", "", {}, "sha512-/iHkCVUG3VbcbmEHn5iIUpIrh7a7WPiwZ3sHy4HZKZzBdSadwdddYDZAII2zBvQYV0Lfi8naZngPCN7WPHI/hA=="],
"@bjorn3/browser_wasi_shim": ["@bjorn3/browser_wasi_shim@0.4.1", "", {}, "sha512-54kpBQX69TZ8I1zyDC8sziv/zPT1zoIadv3CmdIZNZ5WDF1houMjAzRZ3dwWvhXObiEBjOxXyS8Ja7vA0EfGEQ=="], "@borewit/text-codec": ["@borewit/text-codec@0.2.0", "", {}, "sha512-X999CKBxGwX8wW+4gFibsbiNdwqmdQEXmUejIWaIqdrHBgS5ARIOOeyiQbHjP9G58xVEPcuvP6VwwH3A0OFTOA=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
@ -102,21 +109,23 @@
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.4.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="],
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
"@eslint/config-array": ["@eslint/config-array@0.19.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w=="], "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="],
"@eslint/core": ["@eslint/core@0.12.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg=="], "@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="],
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.0", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ=="], "@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="],
"@eslint/js": ["@eslint/js@9.21.0", "", {}, "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw=="], "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
"@eslint/js": ["@eslint/js@9.34.0", "", {}, "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.7", "", { "dependencies": { "@eslint/core": "^0.12.0", "levn": "^0.4.1" } }, "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g=="], "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="],
"@ffmpeg/ffmpeg": ["@ffmpeg/ffmpeg@0.12.15", "", { "dependencies": { "@ffmpeg/types": "^0.12.4" } }, "sha512-1C8Obr4GsN3xw+/1Ww6PFM84wSQAGsdoTuTWPOj2OizsRDLT4CXTaVjPhkw6ARyDus1B9X/L2LiXHqYYsGnRFw=="], "@ffmpeg/ffmpeg": ["@ffmpeg/ffmpeg@0.12.15", "", { "dependencies": { "@ffmpeg/types": "^0.12.4" } }, "sha512-1C8Obr4GsN3xw+/1Ww6PFM84wSQAGsdoTuTWPOj2OizsRDLT4CXTaVjPhkw6ARyDus1B9X/L2LiXHqYYsGnRFw=="],
@ -124,11 +133,11 @@
"@ffmpeg/util": ["@ffmpeg/util@0.12.2", "", {}, "sha512-ouyoW+4JB7WxjeZ2y6KpRvB+dLp7Cp4ro8z0HIVpZVCM7AwFlHa0c4R8Y/a4M3wMqATpYKhC7lSFHQ0T11MEDw=="], "@ffmpeg/util": ["@ffmpeg/util@0.12.2", "", {}, "sha512-ouyoW+4JB7WxjeZ2y6KpRvB+dLp7Cp4ro8z0HIVpZVCM7AwFlHa0c4R8Y/a4M3wMqATpYKhC7lSFHQ0T11MEDw=="],
"@fontsource/azeret-mono": ["@fontsource/azeret-mono@5.2.5", "", {}, "sha512-GRzKYuD1CVOS6Jag/ohDCycLV9a3TK6y1T73A8q0JoDZTVO85DNapqLK+SV2gYtTFldahNAlDSIaizv9MLhR1A=="], "@fontsource/azeret-mono": ["@fontsource/azeret-mono@5.2.9", "", {}, "sha512-1qnbVspQPI38qhSTSidWU4bjG5ynWCfkMwfPxahqxejJO/u4yT1FbPqG73s4fDmQSuDQYoA8jfTpoQiod7+fuA=="],
"@fontsource/lexend": ["@fontsource/lexend@5.2.5", "", {}, "sha512-Mv2XQ+B4ek2lNCGRW5ddLTW8T3xTT17AnCk1IETpoef57XHz+e42fUfLAYMrmiJLOGpR44qnyJ5S6D323A5EIw=="], "@fontsource/lexend": ["@fontsource/lexend@5.2.9", "", {}, "sha512-0a5xzwksBilec8Q+QwPvTFKFcXYw31oyf5CthPKd+C5NJZDl1aHw4FcMz9bcRMPhq0LXO69BXI8aVxmj15pzNA=="],
"@fontsource/radio-canada-big": ["@fontsource/radio-canada-big@5.2.5", "", {}, "sha512-cNwWV/zPZuxtmMfoD0/v1AIL0X09hZ6yNMqnTgUw5Jy0rV4bPn32+RhKziU/itO4zgxDKjrKnR7Tb1h0+Khh+Q=="], "@fontsource/radio-canada-big": ["@fontsource/radio-canada-big@5.2.6", "", {}, "sha512-7ziSRyjIA1uTT5avHTaYHyoQfDiKihCy1wGz+hlKsxkHfpM5G5uVHAhiLcSzIjcMqykMtkyw5shRmgLZgebqTA=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
@ -136,21 +145,31 @@
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.2", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="], "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
"@imagemagick/magick-wasm": ["@imagemagick/magick-wasm@0.0.34", "", {}, "sha512-vSTe4cfR8U8r2Ityo6s3iRFVK903W4fhur3t/ixc6vLSAJXmqXC9cKXz2NkkDBUVU7ssJTYMiTKAb1S9RY+ZdQ=="], "@imagemagick/magick-wasm": ["@imagemagick/magick-wasm@0.0.35", "", {}, "sha512-Wh5C15QyOkJb1i37j9Vpi5OhqCoXwZ/v9LBxo+LUAhiBBtBnRK7Nlg98KGHdtiJocBE3C4FJ+rv+BMrFevzwnw=="],
"@inlang/paraglide-js": ["@inlang/paraglide-js@2.2.0", "", { "dependencies": { "@inlang/recommend-sherlock": "0.2.1", "@inlang/sdk": "2.4.9", "commander": "11.1.0", "consola": "3.4.0", "json5": "2.2.3", "unplugin": "^2.1.2", "urlpattern-polyfill": "^10.0.0" }, "bin": { "paraglide-js": "bin/run.js" } }, "sha512-pkpXu1LanvpcAbvpVPf7PgF11Uq7DliSEBngrcUN36l4ZOOpzn3QBTvVr/tJxvks0O67WseQgiMHet8KH7Oz5A=="],
"@inlang/recommend-sherlock": ["@inlang/recommend-sherlock@0.2.1", "", { "dependencies": { "comment-json": "^4.2.3" } }, "sha512-ckv8HvHy/iTqaVAEKrr+gnl+p3XFNwe5D2+6w6wJk2ORV2XkcRkKOJ/XsTUJbPSiyi4PI+p+T3bqbmNx/rDUlg=="],
"@inlang/sdk": ["@inlang/sdk@2.4.9", "", { "dependencies": { "@lix-js/sdk": "0.4.7", "@sinclair/typebox": "^0.31.17", "kysely": "^0.27.4", "sqlite-wasm-kysely": "0.3.0", "uuid": "^10.0.0" } }, "sha512-cvz/C1rF5WBxzHbEoiBoI6Sz6q6M+TdxfWkEGBYTD77opY8i8WN01prUWXEM87GPF4SZcyIySez9U0Ccm12oFQ=="],
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], "@lix-js/sdk": ["@lix-js/sdk@0.4.7", "", { "dependencies": { "@lix-js/server-protocol-schema": "0.1.1", "dedent": "1.5.1", "human-id": "^4.1.1", "js-sha256": "^0.11.0", "kysely": "^0.27.4", "sqlite-wasm-kysely": "0.3.0", "uuid": "^10.0.0" } }, "sha512-pRbW+joG12L0ULfMiWYosIW0plmW4AsUdiPCp+Z8rAsElJ+wJ6in58zhD3UwUcd4BNcpldEGjg6PdA7e0RgsDQ=="],
"@lix-js/server-protocol-schema": ["@lix-js/server-protocol-schema@0.1.1", "", {}, "sha512-jBeALB6prAbtr5q4vTuxnRZZv1M2rKe8iNqRQhFJ4Tv7150unEa0vKyz0hs8Gl3fUGsWaNJBh3J8++fpbrpRBQ=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@ -188,91 +207,103 @@
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@polka/url": ["@polka/url@1.0.0-next.28", "", {}, "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw=="], "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
"@poppanator/sveltekit-svg": ["@poppanator/sveltekit-svg@5.0.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0" }, "peerDependencies": { "svelte": ">=5.x", "svgo": ">=3.x", "vite": ">=5.x" } }, "sha512-b7hk55SF0HjTS+xFgMG20hy6W0F/m+yRA/ZWcjnsa391rB3Ys3desCiUyIKQYcfvcyuRiQCPedUJMYgu00VdCA=="], "@poppanator/sveltekit-svg": ["@poppanator/sveltekit-svg@5.0.1", "", { "dependencies": { "@rollup/pluginutils": "5.1.4", "svgo": "^3.2.0" }, "peerDependencies": { "svelte": ">=5.x", "vite": ">=5.x || >= 6.x" } }, "sha512-eCOm9Wb1eO9N5N49JNokxgbUZN79QV/fe02cRrrWbDbcAOvGoIFU0YKDZEluIaFTP2zOtOxMeCSSobM4foMShA=="],
"@rollup/plugin-virtual": ["@rollup/plugin-virtual@3.0.2", "", { "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A=="], "@rollup/plugin-virtual": ["@rollup/plugin-virtual@3.0.2", "", { "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A=="],
"@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="], "@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.9", "", { "os": "android", "cpu": "arm" }, "sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.50.0", "", { "os": "android", "cpu": "arm" }, "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.9", "", { "os": "android", "cpu": "arm64" }, "sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.50.0", "", { "os": "android", "cpu": "arm64" }, "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.34.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ=="], "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.50.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.34.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q=="], "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.50.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.34.9", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw=="], "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.50.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.34.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g=="], "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.50.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.34.9", "", { "os": "linux", "cpu": "arm" }, "sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg=="], "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.50.0", "", { "os": "linux", "cpu": "arm" }, "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.34.9", "", { "os": "linux", "cpu": "arm" }, "sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA=="], "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.50.0", "", { "os": "linux", "cpu": "arm" }, "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.34.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw=="], "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.50.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.34.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A=="], "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.50.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ=="],
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.34.9", "", { "os": "linux", "cpu": "none" }, "sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg=="], "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.50.0", "", { "os": "linux", "cpu": "none" }, "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ=="],
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.34.9", "", { "os": "linux", "cpu": "ppc64" }, "sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA=="], "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.50.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.34.9", "", { "os": "linux", "cpu": "none" }, "sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg=="], "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.50.0", "", { "os": "linux", "cpu": "none" }, "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.34.9", "", { "os": "linux", "cpu": "s390x" }, "sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ=="], "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.50.0", "", { "os": "linux", "cpu": "none" }, "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.34.9", "", { "os": "linux", "cpu": "x64" }, "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A=="], "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.50.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.34.9", "", { "os": "linux", "cpu": "x64" }, "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA=="], "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.50.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.34.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q=="], "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.50.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.34.9", "", { "os": "win32", "cpu": "ia32" }, "sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w=="], "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.50.0", "", { "os": "none", "cpu": "arm64" }, "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.9", "", { "os": "win32", "cpu": "x64" }, "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw=="], "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.50.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg=="],
"@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.50.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw=="],
"@stripe/stripe-js": ["@stripe/stripe-js@7.4.0", "", {}, "sha512-lQHQPfXPTBeh0XFjq6PqSBAyR7umwcJbvJhXV77uGCUDD6ymXJU/f2164ydLMLCCceNuPlbV9b+1smx98efwWQ=="], "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.50.0", "", { "os": "win32", "cpu": "x64" }, "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg=="],
"@sinclair/typebox": ["@sinclair/typebox@0.31.28", "", {}, "sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ=="],
"@sqlite.org/sqlite-wasm": ["@sqlite.org/sqlite-wasm@3.48.0-build4", "", { "bin": { "sqlite-wasm": "bin/index.js" } }, "sha512-hI6twvUkzOmyGZhQMza1gpfqErZxXRw6JEsiVjUbo7tFanVD+8Oil0Ih3l2nGzHdxPI41zFmfUQG7GHqhciKZQ=="],
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
"@stripe/stripe-js": ["@stripe/stripe-js@7.9.0", "", {}, "sha512-ggs5k+/0FUJcIgNY08aZTqpBTtbExkJMYMLSMwyucrhtWexVOEY1KJmhBsxf+E/Q15f5rbwBpj+t0t2AW2oCsQ=="],
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="], "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="],
"@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.8", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg=="], "@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.9", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-aytHXcMi7lb9ljsWUzXYQ0p5X1z9oWud2olu/EpmH7aCu4m84h7QLvb5Wp+CFirKcwoNnYvYWhyP/L8Vh1ztdw=="],
"@sveltejs/kit": ["@sveltejs/kit@2.18.0", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "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" }, "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-4DGCGiwNzgnPJySlMe/Qi6rKMK3ntphJaV95BTW+aggaTIAVZ5x3Bp+LURVLMxAEAtWAI5U449NafVxTS+kXbQ=="], "@sveltejs/kit": ["@sveltejs/kit@2.37.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-xgKtpjQ6Ry4mdShd01ht5AODUsW7+K1iValPDq7QX8zI1hWOKREH9GjG8SRCN5tC4K7UXmMhuQam7gbLByVcnw=="],
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@4.0.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0", "debug": "^4.3.7", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.12", "vitefu": "^1.0.3" }, "peerDependencies": { "svelte": "^5.0.0-next.96 || ^5.0.0", "vite": "^5.0.0" } }, "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA=="], "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@4.0.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0", "debug": "^4.3.7", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.12", "vitefu": "^1.0.3" }, "peerDependencies": { "svelte": "^5.0.0-next.96 || ^5.0.0", "vite": "^5.0.0" } }, "sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA=="],
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@3.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0", "svelte": "^5.0.0-next.96 || ^5.0.0", "vite": "^5.0.0" } }, "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ=="], "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@3.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0", "svelte": "^5.0.0-next.96 || ^5.0.0", "vite": "^5.0.0" } }, "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ=="],
"@swc/core": ["@swc/core@1.11.21", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.21" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.11.21", "@swc/core-darwin-x64": "1.11.21", "@swc/core-linux-arm-gnueabihf": "1.11.21", "@swc/core-linux-arm64-gnu": "1.11.21", "@swc/core-linux-arm64-musl": "1.11.21", "@swc/core-linux-x64-gnu": "1.11.21", "@swc/core-linux-x64-musl": "1.11.21", "@swc/core-win32-arm64-msvc": "1.11.21", "@swc/core-win32-ia32-msvc": "1.11.21", "@swc/core-win32-x64-msvc": "1.11.21" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-/Y3BJLcwd40pExmdar8MH2UGGvCBrqNN7hauOMckrEX2Ivcbv3IMhrbGX4od1dnF880Ed8y/E9aStZCIQi0EGw=="], "@swc/core": ["@swc/core@1.13.5", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.24" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.13.5", "@swc/core-darwin-x64": "1.13.5", "@swc/core-linux-arm-gnueabihf": "1.13.5", "@swc/core-linux-arm64-gnu": "1.13.5", "@swc/core-linux-arm64-musl": "1.13.5", "@swc/core-linux-x64-gnu": "1.13.5", "@swc/core-linux-x64-musl": "1.13.5", "@swc/core-win32-arm64-msvc": "1.13.5", "@swc/core-win32-ia32-msvc": "1.13.5", "@swc/core-win32-x64-msvc": "1.13.5" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ=="],
"@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.11.21", "", { "os": "darwin", "cpu": "arm64" }, "sha512-v6gjw9YFWvKulCw3ZA1dY+LGMafYzJksm1mD4UZFZ9b36CyHFowYVYug1ajYRIRqEvvfIhHUNV660zTLoVFR8g=="], "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.13.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ=="],
"@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.11.21", "", { "os": "darwin", "cpu": "x64" }, "sha512-CUiTiqKlzskwswrx9Ve5NhNoab30L1/ScOfQwr1duvNlFvarC8fvQSgdtpw2Zh3MfnfNPpyLZnYg7ah4kbT9JQ=="], "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.13.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng=="],
"@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.11.21", "", { "os": "linux", "cpu": "arm" }, "sha512-YyBTAFM/QPqt1PscD8hDmCLnqPGKmUZpqeE25HXY8OLjl2MUs8+O4KjwPZZ+OGxpdTbwuWFyMoxjcLy80JODvg=="], "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.13.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ=="],
"@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.11.21", "", { "os": "linux", "cpu": "arm64" }, "sha512-DQD+ooJmwpNsh4acrftdkuwl5LNxxg8U4+C/RJNDd7m5FP9Wo4c0URi5U0a9Vk/6sQNh9aSGcYChDpqCDWEcBw=="], "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.13.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw=="],
"@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.11.21", "", { "os": "linux", "cpu": "arm64" }, "sha512-y1L49+snt1a1gLTYPY641slqy55QotPdtRK9Y6jMi4JBQyZwxC8swWYlQWb+MyILwxA614fi62SCNZNznB3XSA=="], "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.13.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ=="],
"@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.11.21", "", { "os": "linux", "cpu": "x64" }, "sha512-NesdBXv4CvVEaFUlqKj+GA4jJMNUzK2NtKOrUNEtTbXaVyNiXjFCSaDajMTedEB0jTAd9ybB0aBvwhgkJUWkWA=="], "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.13.5", "", { "os": "linux", "cpu": "x64" }, "sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA=="],
"@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.11.21", "", { "os": "linux", "cpu": "x64" }, "sha512-qFV60pwpKVOdmX67wqQzgtSrUGWX9Cibnp1CXyqZ9Mmt8UyYGvmGu7p6PMbTyX7vdpVUvWVRf8DzrW2//wmVHg=="], "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.13.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q=="],
"@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.11.21", "", { "os": "win32", "cpu": "arm64" }, "sha512-DJJe9k6gXR/15ZZVLv1SKhXkFst8lYCeZRNHH99SlBodvu4slhh/MKQ6YCixINRhCwliHrpXPym8/5fOq8b7Ig=="], "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.13.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw=="],
"@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.11.21", "", { "os": "win32", "cpu": "ia32" }, "sha512-TqEXuy6wedId7bMwLIr9byds+mKsaXVHctTN88R1UIBPwJA92Pdk0uxDgip0pEFzHB/ugU27g6d8cwUH3h2eIw=="], "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.13.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw=="],
"@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.11.21", "", { "os": "win32", "cpu": "x64" }, "sha512-BT9BNNbMxdpUM1PPAkYtviaV0A8QcXttjs2MDtOeSqqvSJaPtyM+Fof2/+xSwQDmDEFzbGCcn75M5+xy3lGqpA=="], "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.13.5", "", { "os": "win32", "cpu": "x64" }, "sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q=="],
"@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="],
"@swc/types": ["@swc/types@0.1.21", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ=="], "@swc/types": ["@swc/types@0.1.24", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng=="],
"@swc/wasm": ["@swc/wasm@1.13.5", "", {}, "sha512-ZBZcxieydxNwgEU9eFAXGMaDb1Xoh+ZkZcUQ27LNJzc2lPSByoL6CSVqnYiaVo+n9JgqbYyHlMq+i7z0wRNTfA=="],
"@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="],
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
@ -282,33 +313,37 @@
"@types/eslint": ["@types/eslint@9.6.1", "", { "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag=="], "@types/eslint": ["@types/eslint@9.6.1", "", { "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag=="],
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.26.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.26.0", "@typescript-eslint/type-utils": "8.26.0", "@typescript-eslint/utils": "8.26.0", "@typescript-eslint/visitor-keys": "8.26.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.42.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/type-utils": "8.42.0", "@typescript-eslint/utils": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.42.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.26.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.26.0", "@typescript-eslint/types": "8.26.0", "@typescript-eslint/typescript-estree": "8.26.0", "@typescript-eslint/visitor-keys": "8.26.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.42.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/types": "8.42.0", "@typescript-eslint/typescript-estree": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.26.0", "", { "dependencies": { "@typescript-eslint/types": "8.26.0", "@typescript-eslint/visitor-keys": "8.26.0" } }, "sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA=="], "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.42.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.42.0", "@typescript-eslint/types": "^8.42.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.26.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.26.0", "@typescript-eslint/utils": "8.26.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q=="], "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.42.0", "", { "dependencies": { "@typescript-eslint/types": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0" } }, "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.26.0", "", {}, "sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA=="], "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.42.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.26.0", "", { "dependencies": { "@typescript-eslint/types": "8.26.0", "@typescript-eslint/visitor-keys": "8.26.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ=="], "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.42.0", "", { "dependencies": { "@typescript-eslint/types": "8.42.0", "@typescript-eslint/typescript-estree": "8.42.0", "@typescript-eslint/utils": "8.42.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.26.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.26.0", "@typescript-eslint/types": "8.26.0", "@typescript-eslint/typescript-estree": "8.26.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig=="], "@typescript-eslint/types": ["@typescript-eslint/types@8.42.0", "", {}, "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.26.0", "", { "dependencies": { "@typescript-eslint/types": "8.26.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg=="], "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.42.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.42.0", "@typescript-eslint/tsconfig-utils": "8.42.0", "@typescript-eslint/types": "8.42.0", "@typescript-eslint/visitor-keys": "8.42.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ=="],
"acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], "@typescript-eslint/utils": ["@typescript-eslint/utils@8.42.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/types": "8.42.0", "@typescript-eslint/typescript-estree": "8.42.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.42.0", "", { "dependencies": { "@typescript-eslint/types": "8.42.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ=="],
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], "ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
@ -322,7 +357,9 @@
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
"autoprefixer": ["autoprefixer@10.4.20", "", { "dependencies": { "browserslist": "^4.23.3", "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g=="], "array-timsort": ["array-timsort@1.0.3", "", {}, "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ=="],
"autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
@ -332,11 +369,11 @@
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
"brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="], "browserslist": ["browserslist@4.25.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001737", "electron-to-chromium": "^1.5.211", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg=="],
"byte-data": ["byte-data@19.0.1", "", {}, "sha512-xRvkTvO28wr0+0rErSETHD8Cw+P444Az3/jkTezaMw5R+TTW8ZNXuvPZf9/ZhnSRRvlMnJsVhc+ecYvOMy/MQQ=="], "byte-data": ["byte-data@19.0.1", "", {}, "sha512-xRvkTvO28wr0+0rErSETHD8Cw+P444Az3/jkTezaMw5R+TTW8ZNXuvPZf9/ZhnSRRvlMnJsVhc+ecYvOMy/MQQ=="],
@ -344,13 +381,13 @@
"camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="],
"caniuse-lite": ["caniuse-lite@1.0.30001702", "", {}, "sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA=="], "caniuse-lite": ["caniuse-lite@1.0.30001739", "", {}, "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"client-zip": ["client-zip@2.4.6", "", {}, "sha512-e7t1u14h/yT0A12qBwFsaus8UZZ8+MCaNAEn/z53mrukLq/LFcKX7TkbntAppGu8he2p8pz9vc5NEGE/h4ohlw=="], "client-zip": ["client-zip@2.5.0", "", {}, "sha512-ydG4nDZesbFurnNq0VVCp/yyomIBh+X/1fZPI/P24zbnG4dtC4tQAfI5uQsomigsUMeiRO2wiTPizLWQh+IAyQ=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
@ -358,27 +395,35 @@
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], "commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
"comment-json": ["comment-json@4.2.5", "", { "dependencies": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", "esprima": "^4.0.1", "has-own-prop": "^2.0.0", "repeat-string": "^1.6.1" } }, "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="],
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
"cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"css-select": ["css-select@5.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg=="], "css-select": ["css-select@5.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg=="],
"css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="], "css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="],
"css-what": ["css-what@6.1.0", "", {}, "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="], "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
"csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="], "csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="],
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
"dedent": ["dedent@1.5.1", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg=="],
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
@ -386,7 +431,7 @@
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
"devalue": ["devalue@5.1.1", "", {}, "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw=="], "devalue": ["devalue@5.3.2", "", {}, "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw=="],
"didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="],
@ -402,7 +447,7 @@
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
"electron-to-chromium": ["electron-to-chromium@1.5.112", "", {}, "sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA=="], "electron-to-chromium": ["electron-to-chromium@1.5.212", "", {}, "sha512-gE7ErIzSW+d8jALWMcOIgf+IB6lpfsg6NwOhPVwKzDtN2qcBix47vlin4yzSregYDxTCXOUqAZjVY/Z3naS7ww=="],
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
@ -416,25 +461,27 @@
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.21.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.0", "@eslint/js": "9.21.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg=="], "eslint": ["eslint@9.34.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.34.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg=="],
"eslint-compat-utils": ["eslint-compat-utils@0.5.1", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q=="], "eslint-compat-utils": ["eslint-compat-utils@0.5.1", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q=="],
"eslint-config-prettier": ["eslint-config-prettier@10.0.2", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "build/bin/cli.js" } }, "sha512-1105/17ZIMjmCOJOPNfVdbXafLCLj3hPmkmB7dLgt7XsQ/zkxSuDerE/xgO3RxoHysR1N1whmquY0lSn2O0VLg=="], "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="],
"eslint-plugin-svelte": ["eslint-plugin-svelte@2.46.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@jridgewell/sourcemap-codec": "^1.4.15", "eslint-compat-utils": "^0.5.1", "esutils": "^2.0.3", "known-css-properties": "^0.35.0", "postcss": "^8.4.38", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.1.0", "semver": "^7.6.2", "svelte-eslint-parser": "^0.43.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw=="], "eslint-plugin-svelte": ["eslint-plugin-svelte@2.46.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@jridgewell/sourcemap-codec": "^1.4.15", "eslint-compat-utils": "^0.5.1", "esutils": "^2.0.3", "known-css-properties": "^0.35.0", "postcss": "^8.4.38", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.1.0", "semver": "^7.6.2", "svelte-eslint-parser": "^0.43.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw=="],
"eslint-scope": ["eslint-scope@8.2.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A=="], "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="], "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
"espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="], "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
"esrap": ["esrap@1.4.5", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g=="], "esrap": ["esrap@2.1.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
@ -456,11 +503,13 @@
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
"fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="], "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
"file-type": ["file-type@19.6.0", "", { "dependencies": { "get-stream": "^9.0.1", "strtok3": "^9.0.1", "token-types": "^6.0.0", "uint8array-extras": "^1.3.0" } }, "sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ=="], "file-type": ["file-type@21.0.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.7", "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg=="],
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
@ -478,8 +527,6 @@
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
"glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
@ -490,20 +537,22 @@
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"has-own-prop": ["has-own-prop@2.0.0", "", {}, "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"human-id": ["human-id@4.1.1", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"ieee754-buffer": ["ieee754-buffer@2.0.0", "", {}, "sha512-AXUAT0nMEi7h1Is8HXGXof3eejl/GabZFKSj8Ym6kVRUSwrAb52EkAXywiCQYSHGQMRn7lvfY7vhPMjVc+Kybg=="], "ieee754-buffer": ["ieee754-buffer@2.0.0", "", {}, "sha512-AXUAT0nMEi7h1Is8HXGXof3eejl/GabZFKSj8Ym6kVRUSwrAb52EkAXywiCQYSHGQMRn7lvfY7vhPMjVc+Kybg=="],
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"immutable": ["immutable@5.0.3", "", {}, "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw=="], "immutable": ["immutable@5.1.3", "", {}, "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg=="],
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"import-meta-resolve": ["import-meta-resolve@4.1.0", "", {}, "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
@ -520,14 +569,14 @@
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
"is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
"jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
"js-sha256": ["js-sha256@0.11.1", "", {}, "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg=="],
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
@ -536,20 +585,22 @@
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"known-css-properties": ["known-css-properties@0.35.0", "", {}, "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A=="], "known-css-properties": ["known-css-properties@0.35.0", "", {}, "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A=="],
"kysely": ["kysely@0.27.6", "", {}, "sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ=="],
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
"lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
"link": ["link@2.1.1", "", { "bin": { "link": "dist/cli.js" } }, "sha512-NV3AUVYBovJ6eVQcTeRoPnZSxzt2LOijNd+ugEZKRy/XeQlpTRhVRkuDv5kOlXwMAUx30vfUc7asRFb9RT65yg=="],
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
@ -558,9 +609,9 @@
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"lucide-svelte": ["lucide-svelte@0.475.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5.0.0-next.42" } }, "sha512-N5+hFTPHaZe9HhqJDxxxODfYuOmI6v+JIowzERcea/uxytN/JZlehVTcINBNp8wMo7l6ov1Jf5srrDbkI/WsJg=="], "lucide-svelte": ["lucide-svelte@0.542.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5.0.0-next.42" } }, "sha512-KxqJycY4EWaGy1zk/7sqEcf48j4YaP9zEujYczzrsw5j9b9b5pjAJ1qGFKZvD7T6xz9reSYAfUAd6Bz62TdqGw=="],
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], "magic-string": ["magic-string@0.30.18", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ=="],
"mdn-data": ["mdn-data@2.0.30", "", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="], "mdn-data": ["mdn-data@2.0.30", "", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="],
@ -580,11 +631,11 @@
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"music-metadata": ["music-metadata@11.0.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "content-type": "^1.0.5", "debug": "^4.4.0", "file-type": "^19.6.0", "link": "^2.1.1", "media-typer": "^1.1.0", "strtok3": "^10.2.1", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-ZxppS3UuaV1xdWPDmXf4VX3M5+LdVng3xu1rXbZrZWlvtppdOfYrMCeMGDuOt31URTbkmbwuYaRYddkhY5DZMA=="], "music-metadata": ["music-metadata@11.8.3", "", { "dependencies": { "@borewit/text-codec": "^0.2.0", "@tokenizer/token": "^0.3.0", "content-type": "^1.0.5", "debug": "^4.4.1", "file-type": "^21.0.0", "media-typer": "^1.1.0", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.1" } }, "sha512-Tgiv4MlCgDb6XzelziB1mmL2xeoHls0KTpCm3Z3qr+LfF4mBEpkuc5vNrc927IT5+S5fv+vzStfI+HYC0igDpA=="],
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
"nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
@ -604,6 +655,10 @@
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
"overlayscrollbars": ["overlayscrollbars@2.12.0", "", {}, "sha512-mWJ5MOkcZ/ljHwfLw8+bN0V9ziGCoNoqULcp994j5DTGNQvnkWKWkA7rnO29Kyew5AoHxUnJ4Ndqfcl0HSQjXg=="],
"overlayscrollbars-svelte": ["overlayscrollbars-svelte@0.5.5", "", { "peerDependencies": { "overlayscrollbars": "^2.0.0", "svelte": "^5.0.0" } }, "sha512-+dRW3YZSvFbKi5vDCpnUOHuoPLLSdu0BUVVMYZdmfVghu7XkafDRebG2y91/ImPqj6YDAUsz1rcWVYhCJSS/pQ=="],
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
@ -624,17 +679,15 @@
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"peek-readable": ["peek-readable@6.1.1", "", {}, "sha512-7QmvgRKhxM0E2PGV4ocfROItVode+ELI27n4q+lpufZ+tRKBu/pBP8WOmw9HXn2ui/AUizqtvaVQhcJrOkRqYg=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
"pirates": ["pirates@4.0.6", "", {}, "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="], "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="],
"postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
@ -654,11 +707,11 @@
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="], "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
"prettier-plugin-svelte": ["prettier-plugin-svelte@3.3.3", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw=="], "prettier-plugin-svelte": ["prettier-plugin-svelte@3.4.0", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ=="],
"prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.11", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA=="], "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.14", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
@ -668,6 +721,8 @@
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"repeat-string": ["repeat-string@1.6.1", "", {}, "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="],
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
@ -676,15 +731,15 @@
"riff-file": ["riff-file@1.0.3", "", { "dependencies": { "byte-data": "^18.0.3" } }, "sha512-Vv8wwGr0BCks7VMI3Lv0houZee4DaHFjjTT0LMhMJKio2YmLncLeIVpK63ydSverngNk8XQPU3fbeP3bWgSIig=="], "riff-file": ["riff-file@1.0.3", "", { "dependencies": { "byte-data": "^18.0.3" } }, "sha512-Vv8wwGr0BCks7VMI3Lv0houZee4DaHFjjTT0LMhMJKio2YmLncLeIVpK63ydSverngNk8XQPU3fbeP3bWgSIig=="],
"rollup": ["rollup@4.34.9", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.34.9", "@rollup/rollup-android-arm64": "4.34.9", "@rollup/rollup-darwin-arm64": "4.34.9", "@rollup/rollup-darwin-x64": "4.34.9", "@rollup/rollup-freebsd-arm64": "4.34.9", "@rollup/rollup-freebsd-x64": "4.34.9", "@rollup/rollup-linux-arm-gnueabihf": "4.34.9", "@rollup/rollup-linux-arm-musleabihf": "4.34.9", "@rollup/rollup-linux-arm64-gnu": "4.34.9", "@rollup/rollup-linux-arm64-musl": "4.34.9", "@rollup/rollup-linux-loongarch64-gnu": "4.34.9", "@rollup/rollup-linux-powerpc64le-gnu": "4.34.9", "@rollup/rollup-linux-riscv64-gnu": "4.34.9", "@rollup/rollup-linux-s390x-gnu": "4.34.9", "@rollup/rollup-linux-x64-gnu": "4.34.9", "@rollup/rollup-linux-x64-musl": "4.34.9", "@rollup/rollup-win32-arm64-msvc": "4.34.9", "@rollup/rollup-win32-ia32-msvc": "4.34.9", "@rollup/rollup-win32-x64-msvc": "4.34.9", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ=="], "rollup": ["rollup@4.50.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.50.0", "@rollup/rollup-android-arm64": "4.50.0", "@rollup/rollup-darwin-arm64": "4.50.0", "@rollup/rollup-darwin-x64": "4.50.0", "@rollup/rollup-freebsd-arm64": "4.50.0", "@rollup/rollup-freebsd-x64": "4.50.0", "@rollup/rollup-linux-arm-gnueabihf": "4.50.0", "@rollup/rollup-linux-arm-musleabihf": "4.50.0", "@rollup/rollup-linux-arm64-gnu": "4.50.0", "@rollup/rollup-linux-arm64-musl": "4.50.0", "@rollup/rollup-linux-loongarch64-gnu": "4.50.0", "@rollup/rollup-linux-ppc64-gnu": "4.50.0", "@rollup/rollup-linux-riscv64-gnu": "4.50.0", "@rollup/rollup-linux-riscv64-musl": "4.50.0", "@rollup/rollup-linux-s390x-gnu": "4.50.0", "@rollup/rollup-linux-x64-gnu": "4.50.0", "@rollup/rollup-linux-x64-musl": "4.50.0", "@rollup/rollup-openharmony-arm64": "4.50.0", "@rollup/rollup-win32-arm64-msvc": "4.50.0", "@rollup/rollup-win32-ia32-msvc": "4.50.0", "@rollup/rollup-win32-x64-msvc": "4.50.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw=="],
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
"sass": ["sass@1.85.1", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-Uk8WpxM5v+0cMR0XjX9KfRIacmSG86RH4DCCZjLU2rFh5tyutt9siAXJ7G+YfxQ99Q6wrRMbMlVl6KqUms71ag=="], "sass": ["sass@1.91.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-aFOZHGf+ur+bp1bCHZ+u8otKGh77ZtmFyXDo4tlYvT7PWql41Kwd8wdkPqhhT+h2879IVblcHFglIMofsFd1EA=="],
"semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
"set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="],
@ -698,6 +753,8 @@
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"sqlite-wasm-kysely": ["sqlite-wasm-kysely@0.3.0", "", { "dependencies": { "@sqlite.org/sqlite-wasm": "^3.48.0-build2" }, "peerDependencies": { "kysely": "*" } }, "sha512-TzjBNv7KwRw6E3pdKdlRyZiTmUIE0UttT/Sl56MVwVARl/u5gp978KepazCJZewFUnlWHz9i3NQd4kOtP/Afdg=="],
"string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@ -708,7 +765,7 @@
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"strtok3": ["strtok3@10.2.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^6.1.1" } }, "sha512-Q2dTnW3UXokAvXmXvrvMoUj/me3LyJI76HNHeuGMh2o0As/vzd7eHV3ncLOyvu928vQIDbE7Vf9ldEnC7cwy1w=="], "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="],
"sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="], "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="],
@ -716,9 +773,9 @@
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
"svelte": ["svelte@5.22.5", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.3", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-+2BDWVa/rqr34oM+5HFUSmCCPdBBeNqFv2Xc/SSB8kV4iQhWWBNvU9/nd5Dz3PkAGl7Bm/AndS1vY4fe1MgTKA=="], "svelte": ["svelte@5.38.6", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-ltBPlkvqk3bgCK7/N323atUpP3O3Y+DrGV4dcULrsSn4fZaaNnOmdplNznwfdWclAgvSr5rxjtzn/zJhRm6TKg=="],
"svelte-check": ["svelte-check@4.1.5", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-Gb0T2IqBNe1tLB9EB1Qh+LOe+JB8wt2/rNBDGvkxQVvk8vNeAoG+vZgFB/3P5+zC7RWlyBlzm9dVjZFph/maIg=="], "svelte-check": ["svelte-check@4.3.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg=="],
"svelte-eslint-parser": ["svelte-eslint-parser@0.43.0", "", { "dependencies": { "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "postcss": "^8.4.39", "postcss-scss": "^4.0.9" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA=="], "svelte-eslint-parser": ["svelte-eslint-parser@0.43.0", "", { "dependencies": { "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "postcss": "^8.4.39", "postcss-scss": "^4.0.9" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA=="],
@ -734,26 +791,30 @@
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"token-types": ["token-types@6.0.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA=="], "token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="],
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
"ts-api-utils": ["ts-api-utils@2.0.1", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w=="], "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
"typescript-eslint": ["typescript-eslint@8.26.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.26.0", "@typescript-eslint/parser": "8.26.0", "@typescript-eslint/utils": "8.26.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-PtVz9nAnuNJuAVeUFvwztjuUgSnJInODAUx47VDwWPXzd5vismPOtPtt83tzNXyOjVQbPRp786D6WFW/M2koIA=="], "typescript-eslint": ["typescript-eslint@8.42.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/parser": "8.42.0", "@typescript-eslint/typescript-estree": "8.42.0", "@typescript-eslint/utils": "8.42.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ozR/rQn+aQXQxh1YgbCzQWDFrsi9mcg+1PM3l/z5o1+20P7suOIaNg515bpr/OYt6FObz/NHcBstydDLHWeEKg=="],
"uint8array-extras": ["uint8array-extras@1.4.0", "", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="], "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
"unplugin": ["unplugin@2.3.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw=="],
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"urlpattern-polyfill": ["urlpattern-polyfill@10.1.0", "", {}, "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw=="],
"utf8-buffer": ["utf8-buffer@1.0.0", "", {}, "sha512-ueuhzvWnp5JU5CiGSY4WdKbiN/PO2AZ/lpeLiz2l38qwdLy/cW40XobgyuIWucNyum0B33bVB0owjFCeGBSLqg=="], "utf8-buffer": ["utf8-buffer@1.0.0", "", {}, "sha512-ueuhzvWnp5JU5CiGSY4WdKbiN/PO2AZ/lpeLiz2l38qwdLy/cW40XobgyuIWucNyum0B33bVB0owjFCeGBSLqg=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
@ -762,13 +823,15 @@
"vert-wasm": ["vert-wasm@0.0.2", "", {}, "sha512-QCkhkATZkWp3OPpIrlcLVd5S4DQXeDEWIPI1eV+Ku+sUVoCM4s9DQbNKtnJ8s4leMW3AD4E7cY/gXB/9TsI3WA=="], "vert-wasm": ["vert-wasm@0.0.2", "", {}, "sha512-QCkhkATZkWp3OPpIrlcLVd5S4DQXeDEWIPI1eV+Ku+sUVoCM4s9DQbNKtnJ8s4leMW3AD4E7cY/gXB/9TsI3WA=="],
"vite": ["vite@5.4.14", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA=="], "vite": ["vite@5.4.19", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA=="],
"vite-plugin-top-level-await": ["vite-plugin-top-level-await@1.5.0", "", { "dependencies": { "@rollup/plugin-virtual": "^3.0.2", "@swc/core": "^1.10.16", "uuid": "^10.0.0" }, "peerDependencies": { "vite": ">=2.8" } }, "sha512-r/DtuvHrSqUVk23XpG2cl8gjt1aATMG5cjExXL1BUTcSNab6CzkcPua9BPEc9fuTP5UpwClCxUe3+dNGL0yrgQ=="], "vite-plugin-top-level-await": ["vite-plugin-top-level-await@1.6.0", "", { "dependencies": { "@rollup/plugin-virtual": "^3.0.2", "@swc/core": "^1.12.14", "@swc/wasm": "^1.12.14", "uuid": "10.0.0" }, "peerDependencies": { "vite": ">=2.8" } }, "sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww=="],
"vite-plugin-wasm": ["vite-plugin-wasm@3.4.1", "", { "peerDependencies": { "vite": "^2 || ^3 || ^4 || ^5 || ^6" } }, "sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA=="], "vite-plugin-wasm": ["vite-plugin-wasm@3.5.0", "", { "peerDependencies": { "vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7" } }, "sha512-X5VWgCnqiQEGb+omhlBVsvTfxikKtoOgAzQ95+BZ8gQ+VfMHIjSHr0wyvXFQCa0eKQ0fKyaL0kWcEnYqBac4lQ=="],
"vitefu": ["vitefu@1.0.6", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["vite"] }, "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA=="], "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
@ -790,6 +853,8 @@
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
@ -798,8 +863,6 @@
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"file-type/strtok3": ["strtok3@9.1.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^5.3.1" } }, "sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw=="],
"glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
@ -822,23 +885,25 @@
"svelte-eslint-parser/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], "svelte-eslint-parser/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="],
"svgo/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
"tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
"tailwindcss/postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="], "tailwindcss/postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="],
"token-types/@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="],
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
"file-type/strtok3/peek-readable": ["peek-readable@5.4.2", "", {}, "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg=="], "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
@ -846,7 +911,7 @@
"tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], "tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
"tailwindcss/postcss-load-config/yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="], "tailwindcss/postcss-load-config/yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],

205
messages/en.json Normal file
View File

@ -0,0 +1,205 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"navbar": {
"upload": "Upload",
"convert": "Convert",
"settings": "Settings",
"about": "About",
"toggle_theme": "Toggle theme"
},
"footer": {
"copyright": "© {year} VERT.",
"source_code": "Source code",
"discord_server": "Discord server"
},
"upload": {
"title": "The file converter you'll love.",
"subtitle": "All image, audio, and document processing is done on your device. Videos are converted on our lightning-fast servers. No file size limit, no ads, and completely open source.",
"uploader": {
"text": "Drop or click to {action}",
"convert": "convert",
"jpegify": "jpegify"
},
"cards": {
"title": "VERT supports...",
"images": "Images",
"audio": "Audio",
"documents": "Documents",
"video": "Video",
"video_server_processing": "Server supported",
"local_supported": "Local supported",
"status": {
"text": "<b>Status:</b> {status}",
"ready": "ready",
"not_ready": "not ready"
},
"supported_formats": "Supported formats:"
},
"tooltip": {
"partial_support": "This format can only be converted as {direction}.",
"direction_input": "input (from)",
"direction_output": "output (to)",
"video_server_processing": "Video uploads to a server for processing by default, learn how to set it up locally here."
}
},
"convert": {
"panel": {
"convert_all": "Convert all",
"download_all": "Download all as .zip",
"remove_all": "Remove all files",
"set_all_to": "Set all to",
"na": "N/A"
},
"dropdown": {
"audio": "Audio",
"video": "Video",
"doc": "Document",
"image": "Image",
"placeholder": "Search format"
},
"tooltips": {
"unknown_file": "Unknown file type",
"audio_file": "Audio file",
"video_file": "Video file",
"document_file": "Document file",
"image_file": "Image file",
"convert_file": "Convert this file",
"download_file": "Download this file"
},
"errors": {
"cant_convert": "We can't convert this file.",
"vertd_server": "what are you doing..? you're supposed to run the vertd server!",
"unsupported_format": "Only image, video, audio, and document files are supported",
"vertd_not_found": "Could not find the vertd instance to start video conversion. Are you sure the instance URL is set correctly?"
}
},
"settings": {
"title": "Settings",
"errors": {
"save_failed": "Failed to save settings!"
},
"appearance": {
"title": "Appearance",
"brightness_theme": "Brightness theme",
"brightness_description": "Want a sunny flash-bang, or a quiet lonely night?",
"light": "Light",
"dark": "Dark",
"effect_settings": "Effect settings",
"effect_description": "Would you like fancy effects, or a more static experience?",
"enable": "Enable",
"disable": "Disable"
},
"conversion": {
"title": "Conversion",
"filename_format": "File name format",
"filename_description": "This will determine the name of the file on download, <b>not including the file extension.</b> You can put these following templates in the format, which will be replaced with the relevant information: <b>%name%</b> for the original file name, <b>%extension%</b> for the original file extension, and <b>%date%</b> for a date string of when the file was converted.",
"placeholder": "VERT_%name%",
"quality": "Conversion quality",
"quality_description": "This changes the default output quality of the converted files (in its category). Higher values may result in longer conversion times and file size.",
"quality_video": "This changes the default output quality of the converted video files. Higher values may result in longer conversion times and file size.",
"quality_audio": "Audio (kbps)",
"quality_images": "Image (%)",
"rate": "Sample rate (Hz)"
},
"vertd": {
"title": "Video conversion",
"status": "status:",
"loading": "loading...",
"available": "available, commit id {commitId}",
"unavailable": "unavailable (is the url right?)",
"description": "The <code>vertd</code> project is a server wrapper for FFmpeg. This allows you to convert videos through the convenience of VERT's web interface, while still being able to harness the power of your GPU to do it as quickly as possible.",
"hosting_info": "We host a public instance for your convenience, but it is quite easy to host your own on your PC or server if you know what you are doing. You can download the server binaries [vertd_link]here[/vertd_link] - the process of setting this up will become easier in the future, so stay tuned!",
"instance_url": "Instance URL",
"url_placeholder": "Example: http://localhost:24153",
"conversion_speed": "Conversion speed",
"speed_description": "This describes the tradeoff between speed and quality. Faster speeds will result in lower quality, but will get the job done quicker.",
"speeds": {
"very_slow": "Very Slow",
"slower": "Slower",
"slow": "Slow",
"medium": "Medium",
"fast": "Fast",
"ultra_fast": "Ultra Fast"
}
},
"privacy": {
"title": "Privacy",
"plausible_title": "Plausible analytics",
"plausible_description": "We use [plausible_link]Plausible[/plausible_link], a privacy-focused analytics tool, to gather completely anonymous statistics. All data is anonymized and aggregated, and no identifiable information is ever sent or stored. You can view the analytics [analytics_link]here[/analytics_link] and choose to opt out below.",
"opt_in": "Opt-in",
"opt_out": "Opt-out"
},
"language": {
"title": "Language",
"description": "Select your preferred language for the VERT interface."
}
},
"about": {
"title": "About",
"why": {
"title": "Why VERT?",
"description": "<b>File converters have always disappointed us.</b> They're ugly, riddled with ads, and most importantly; slow. We decided to solve this problem once and for all by making an alternative that solves all those problems, and more.<br/><br/>All non-video files are converted completely on-device; this means that there's no delay between sending and receiving the files from a server, and we never get to snoop on the files you convert.<br/><br/>Video files get uploaded to our lightning-fast RTX 4000 Ada server. Your videos stay on there for an hour if you do not convert them. If you do convert the file, the video will stay on the server for an hour, or until it is downloaded. The file will then be deleted from our server."
},
"sponsors": {
"title": "Sponsors",
"description": "Want to support us? Contact a developer in the [discord_link]Discord[/discord_link] server, or send an email to",
"email_copied": "Email copied to clipboard!"
},
"resources": {
"title": "Resources",
"discord": "Discord",
"source": "Source",
"email": "Email"
},
"donate": {
"title": "Donate to VERT",
"description": "With your support, we can keep maintaining and improving VERT.",
"one_time": "One-time",
"monthly": "Monthly",
"custom": "Custom",
"pay_now": "Pay now",
"donate_amount": "Donate ${amount} USD",
"thank_you": "Thank you for your donation!",
"payment_failed": "Payment failed: {message}{period} You have not been charged.",
"donation_error": "An error occurred while processing your donation. Please try again later.",
"payment_error": "Error fetching payment details. Please try again later."
},
"credits": {
"title": "Credits",
"contact_team": "If you would like to contact the development team, please use the email found on the \"Resources\" card.",
"notable_contributors": "Notable contributors",
"notable_description": "We'd like to thank these people for their major contributions to VERT.",
"github_contributors": "GitHub contributors",
"github_description": "Big [jpegify_link]thanks[/jpegify_link] to all these people for helping out! [github_link]Want to help too?[/github_link]",
"no_contributors": "Seems like no one has contributed yet... [contribute_link]be the first to contribute![/contribute_link]",
"libraries": "Libraries",
"libraries_description": "A big thanks to FFmpeg (audio, video), ImageMagick (images) and Pandoc (documents) for maintaining such excellent libraries for so many years. VERT relies on them to provide you with your conversions.",
"roles": {
"lead_developer": "Lead developer; conversion backend, UI implementation",
"developer": "Developer; UI implementation",
"designer": "Designer; UX, branding, marketing",
"docker_ci": "Maintaining Docker & CI support",
"former_cofounder": "Former co-founder & designer"
}
},
"errors": {
"github_contributors": "Error fetching GitHub contributors"
}
},
"workers": {
"errors": {
"general": "Error converting {file}: {message}",
"magick": "Error in Magick worker, image conversion may not work as expected.",
"ffmpeg": "Error loading ffmpeg, some features may not work.",
"no_audio": "No audio stream found.",
"invalid_rate": "Invalid sample rate specified: {rate}Hz"
}
},
"jpegify": {
"title": "SECRET JPEGIFY!!!",
"subtitle": "(shh... don't tell anyone!)",
"button": "JPEGIFY {compression}%!!!",
"download": "Download",
"delete": "Delete"
}
}

205
messages/es.json Normal file
View File

@ -0,0 +1,205 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"navbar": {
"upload": "Subir",
"convert": "Convertir",
"settings": "Ajustes",
"about": "Acerca de",
"toggle_theme": "Cambiar tema"
},
"footer": {
"copyright": "© {year} VERT.",
"source_code": "Código fuente",
"discord_server": "Servidor de Discord"
},
"upload": {
"title": "El convertidor de archivos que te encantará.",
"subtitle": "Todo el procesamiento de imágenes, audio y documentos es hecho en tu dispositivo. Los vídeos son convertidos en nuestros servidores ultra rápidos. Sin límite de tamaño de archivo, sin anuncios y de código abierto.",
"uploader": {
"text": "Arrastra o haz clic para {action}",
"convert": "convertir",
"jpegify": "jpegificar"
},
"cards": {
"title": "VERT soporta...",
"images": "Imágenes",
"audio": "Audio",
"documents": "Documentos",
"video": "Vídeo",
"video_server_processing": "Soportado por el servidor",
"local_supported": "Soportado localmente",
"status": {
"text": "<b>Estado:</b> {status}",
"ready": "listo",
"not_ready": "no listo"
},
"supported_formats": "Formatos soportados:"
},
"tooltip": {
"partial_support": "Este formato solo se puede convertir a {direction}.",
"direction_input": "entrada (desde)",
"direction_output": "salida (hacia)",
"video_server_processing": "Por defecto, los vídeos se suben a un servidor para ser procesados. Aprende cómo instalarlo localmente aquí."
}
},
"convert": {
"panel": {
"convert_all": "Convertir todo",
"download_all": "Comprimir todo",
"remove_all": "Quitar todos los archivos",
"set_all_to": "Cambiar todos a",
"na": "N/A"
},
"dropdown": {
"audio": "Audio",
"video": "Vídeo",
"doc": "Documento",
"image": "Imagen",
"placeholder": "Buscar formato"
},
"tooltips": {
"unknown_file": "Formato de archivo desconocido",
"audio_file": "Audio",
"video_file": "Vídeo",
"document_file": "Documento",
"image_file": "Imagen",
"convert_file": "Convertir este archivo",
"download_file": "Descargar este archivo"
},
"errors": {
"cant_convert": "No podemos convertir este archivo.",
"vertd_server": "¿Qué estás haciendo..? ¡Debes ejecutar el servidor de vertd!",
"unsupported_format": "Solo aceptamos imágenes, vídeos, audios y documentos.",
"vertd_not_found": "No se encontró la instancia de vertd para iniciar la conversión de vídeos. ¿Estás seguro de que la URL es correcta?"
}
},
"settings": {
"title": "Ajustes",
"errors": {
"save_failed": "¡No se han podido guardar los ajustes!"
},
"appearance": {
"title": "Apariencia",
"brightness_theme": "Tema",
"brightness_description": "¿Prefieres una flash-bang soleada o una silenciosa y solitaria noche?",
"light": "Claro",
"dark": "Oscuro",
"effect_settings": "Efectos",
"effect_description": "¿Prefieres efectos en la interfaz o una experiencia más estática?",
"enable": "Habilitar",
"disable": "Deshabilitar"
},
"conversion": {
"title": "Conversión",
"filename_format": "Formato del nombre de archivo",
"filename_description": "Esto va a determinar el nombre del archivo al ser descargado <b>sin incluir la extensión</b>. Puedes poner las siguientes plantillas en el formato, las cuales serán reemplazadas con la información que les corresponde: <b>%name%</b> para el nombre original, <b>%extension%</b> para la extensión original del archivo y <b>%date%</b> para la fecha de cuando el archivo fue convertido.",
"placeholder": "VERT_%name%",
"quality": "Calidad de la conversión",
"quality_description": "Esto cambia la calidad por defecto de los archivos convertidos (en su categoría). Valores más altos pueden resultar en tiempos de conversión y tamaños de archivo más largos.",
"quality_video": "Esto cambia la calidad por defecto de los vídeos convertidos. Valores más altos pueden resultar en tiempos de conversión y tamaños de archivo más largos.",
"quality_audio": "Audio (kbps)",
"quality_images": "Imagen (%)",
"rate": "Tasa de muestreo (Hz)"
},
"vertd": {
"title": "Conversión de vídeo",
"status": "estado:",
"loading": "cargando...",
"available": "disponible, id del commit {commitId}",
"unavailable": "no disponible (¿has comprobado la url?)",
"description": "<code>vertd</code> es un proyecto que actúa como un servidor intermediario (\"wrapper\") para FFmpeg. Permite convertir vídeos sin dejar de lado la conveniente interfaz web de VERT y, a la vez, aprovecha la potencia de tu GPU para hacerlo lo más rápido posible.",
"hosting_info": "Alojamos una instancia pública para tu conveniencia, pero es bastante fácil alojar una propia en tu PC o servidor si sabes lo que estás haciendo. Puedes descargar los binarios del servidor [vertd_link]aquí[/vertd_link]. ¡El proceso de instalación será más fácil en el futuro, así que mantente atento!",
"instance_url": "URL de la instancia",
"url_placeholder": "Ejemplo: http://localhost:24153",
"conversion_speed": "Velocidad de conversión",
"speed_description": "Esto describe el equilibrio entre velocidad y calidad. Velocidades más rápidas resultarán en una calidad más baja, pero harán el trabajo más rápido.",
"speeds": {
"very_slow": "Extremadamente lento",
"slower": "Muy lento",
"slow": "Lento",
"medium": "Medio",
"fast": "Rápido",
"ultra_fast": "Súper rápido"
}
},
"privacy": {
"title": "Privacidad",
"plausible_title": "Analíticas de Plausible",
"plausible_description": "Usamos [plausible_link]Plausible[/plausible_link], una herramienta de analíticas orientada a la privacidad para recopilar estadísticas completamente anónimas. Toda la información que recopilamos es anonimizada y agregada, y en ningún momento se envía ni se almacena información que permita identificarte. Puedes ver las estadísticas [analytics_link]aquí[/analytics_link] y excluirte de ellas a continuación:",
"opt_in": "Participar",
"opt_out": "No participar"
},
"language": {
"title": "Lenguaje",
"description": "Selecciona el lenguaje que prefieres usar para la interfaz de VERT."
}
},
"about": {
"title": "Acerca de",
"why": {
"title": "¿Por qué VERT?",
"description": "<b>Los conversores de archivos siempre nos han decepcionado.</b> Son feos, están llenos de anuncios y, lo más importante, son lentos. Decidimos solucionar este problema de una vez por todas creando una alternativa que resuelve todo eso, y más.<br/><br/>Todos los archivos (exceptuando vídeos) se convierten directamente en tu dispositivo; esto significa que no hay demoras por subir o bajar archivos de un servidor, y nunca tenemos acceso a los archivos que conviertes.<br/><br/>Los vídeos se suben a nuestro servidor ultra rápido equipado con una RTX 4000 Ada. Tus vídeos permanecen allí durante una hora si no los conviertes. Si los conviertes, el archivo se guarda durante una hora, o hasta que lo descargues. Luego, el archivo se elimina del servidor."
},
"sponsors": {
"title": "Patrocinadores",
"description": "¿Quieres apoyarnos? Contacta a un desarrollador en el servidor de [discord_link]Discord[/discord_link] o envía un correo a",
"email_copied": "¡Email copiado al portapapeles!"
},
"resources": {
"title": "Recursos",
"discord": "Discord",
"source": "Fuente",
"email": "Email"
},
"donate": {
"title": "Donar a VERT",
"description": "Con tu apoyo, podemos seguir manteniendo y mejorando VERT.",
"one_time": "Una sola vez",
"monthly": "Mensual",
"custom": "Personalizado",
"pay_now": "Pagar ahora",
"donate_amount": "Donar ${amount} USD",
"thank_you": "¡Gracias por tu donación!",
"payment_failed": "Pago fallido: {message}{period} No se ha efectuado ningún cargo.",
"donation_error": "Ha ocurrido un error al procesar tu donación. Por favor, inténtalo de nuevo más tarde.",
"payment_error": "Ha ocurrido un error al obtener los detalles del pago. Por favor, inténtalo de nuevo más tarde."
},
"credits": {
"title": "Créditos",
"contact_team": "Si te gustaría contactar al equipo de desarrollo, por favor usa el email que se encuentra en la tarjeta de \"Recursos\".",
"notable_contributors": "Colaboradores destacados",
"notable_description": "Queremos dar las gracias a las siguientes personas por sus importantes contribuciones a VERT.",
"github_contributors": "Contribuidores de GitHub",
"github_description": "¡Muchas [jpegify_link]gracias[/jpegify_link] a todos los que han contribuido! [github_link]¿Quieres contribuir también?[/github_link]",
"no_contributors": "Parece que nadie ha contribuido todavía... [contribute_link]¡Sé el primero en hacerlo![/contribute_link]",
"libraries": "Librerías",
"libraries_description": "Muchas gracias a FFmpeg (audio, vídeo), ImageMagick (imágenes) y Pandoc (documentos) por mantener librerías excelentes por tantos años. VERT depende de ellas para proporcionar tus conversiones.",
"roles": {
"lead_developer": "Líder de desarrollo; implementación del backend de conversión e interfaz",
"developer": "Desarrollador; implementación de la interfaz",
"designer": "Diseñador; UX, branding y marketing",
"docker_ci": "Mantenimiento del soporte para Docker y CI",
"former_cofounder": "Excofundador; diseñador"
}
},
"errors": {
"github_contributors": "Ocurrió un error mientras se obtenían los contribuidores de GitHub."
}
},
"workers": {
"errors": {
"general": "Ocurrió un error mientras se convertía {file}: {message}",
"magick": "Ocurrió un error en el módulo de Magick, la conversión de imágenes puede que no funcione correctamente.",
"ffmpeg": "No se pudo cargar FFmpeg, algunas funciones podrían no funcionar.",
"no_audio": "No se encontró una pista de audio.",
"invalid_rate": "La tasa de muestreo especificada no es válida: {rate}Hz"
}
},
"jpegify": {
"title": "¡¡¡JPEGIFICADOR SECRETO!!!",
"subtitle": "(shh... ¡no se lo digas a nadie!)",
"button": "¡¡¡JPEGIFICAR {compression}%!!!",
"download": "Descargar",
"delete": "Eliminar"
}
}

View File

@ -12,47 +12,54 @@
"lint": "prettier --check . && eslint ." "lint": "prettier --check . && eslint ."
}, },
"devDependencies": { "devDependencies": {
"@poppanator/sveltekit-svg": "^5.0.0", "@poppanator/sveltekit-svg": "^5.0.1",
"@sveltejs/adapter-static": "^3.0.8", "@sveltejs/adapter-static": "^3.0.9",
"@sveltejs/kit": "^2.16.0", "@sveltejs/kit": "^2.37.0",
"@sveltejs/vite-plugin-svelte": "^4.0.4", "@sveltejs/vite-plugin-svelte": "^4.0.4",
"@types/eslint": "^9.6.1", "@types/eslint": "^9.6.1",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.21",
"css-select": "5.1.0", "css-select": "5.1.0",
"eslint": "^9.18.0", "eslint": "^9.34.0",
"eslint-config-prettier": "^10.0.1", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^2.46.1", "eslint-plugin-svelte": "^2.46.1",
"globals": "^15.14.0", "globals": "^15.15.0",
"prettier": "^3.4.2", "prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.3.3", "prettier-plugin-svelte": "^3.4.0",
"prettier-plugin-tailwindcss": "^0.6.10", "prettier-plugin-tailwindcss": "^0.6.14",
"sass": "^1.83.4", "sass": "^1.91.0",
"svelte": "^5.19.0", "svelte": "^5.38.6",
"svelte-check": "^4.1.4", "svelte-check": "^4.3.1",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"typescript": "^5.7.3", "typescript": "^5.9.2",
"typescript-eslint": "^8.20.0", "typescript-eslint": "^8.42.0",
"vite": "^5.4.11", "vite": "^5.4.19",
"vite-plugin-top-level-await": "^1.5.0" "vite-plugin-top-level-await": "^1.6.0",
"@inlang/paraglide-js": "2.2.0"
}, },
"dependencies": { "dependencies": {
"@bjorn3/browser_wasi_shim": "^0.4.1", "@bjorn3/browser_wasi_shim": "^0.4.2",
"@ffmpeg/ffmpeg": "^0.12.15", "@ffmpeg/ffmpeg": "^0.12.15",
"@ffmpeg/util": "^0.12.2", "@ffmpeg/util": "^0.12.2",
"@fontsource/azeret-mono": "^5.1.1", "@fontsource/azeret-mono": "^5.2.9",
"@fontsource/lexend": "^5.1.2", "@fontsource/lexend": "^5.2.9",
"@fontsource/radio-canada-big": "^5.1.1", "@fontsource/radio-canada-big": "^5.2.6",
"@imagemagick/magick-wasm": "^0.0.34", "@imagemagick/magick-wasm": "^0.0.35",
"@stripe/stripe-js": "^7.4.0", "@stripe/stripe-js": "^7.9.0",
"byte-data": "^19.0.1", "byte-data": "^19.0.1",
"client-zip": "^2.4.6", "client-zip": "^2.5.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-svelte": "^0.475.0", "lucide-svelte": "^0.542.0",
"music-metadata": "^11.0.0", "music-metadata": "^11.8.3",
"overlayscrollbars": "^2.12.0",
"overlayscrollbars-svelte": "^0.5.5",
"p-queue": "^8.1.0", "p-queue": "^8.1.0",
"riff-file": "^1.0.3", "riff-file": "^1.0.3",
"svelte-stripe": "^1.4.0", "svelte-stripe": "^1.4.0",
"vert-wasm": "^0.0.2", "vert-wasm": "^0.0.2",
"vite-plugin-wasm": "^3.4.1" "vite-plugin-wasm": "^3.5.0"
} },
"trustedDependencies": [
"@parcel/watcher",
"@swc/core"
]
} }

1
project.inlang/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
cache

View File

@ -0,0 +1 @@
ff77Td2rnvEqQyzBYT

View File

@ -0,0 +1,15 @@
{
"$schema": "https://inlang.com/schema/project-settings",
"baseLocale": "en",
"locales": [
"en",
"es"
],
"modules": [
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"
],
"plugin.inlang.messageFormat": {
"pathPattern": "./messages/{locale}.json"
}
}

View File

@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="%lang%">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />

15
src/hooks.server.ts Normal file
View File

@ -0,0 +1,15 @@
import type { Handle } from '@sveltejs/kit';
import { paraglideMiddleware } from '$lib/paraglide/server';
// creating a handle to use the paraglide middleware
const paraglideHandle: Handle = ({ event, resolve }) =>
paraglideMiddleware(event.request, ({ request: localizedRequest, locale }) => {
event.request = localizedRequest;
return resolve(event, {
transformPageChunk: ({ html }) => {
return html.replace('%lang%', locale);
}
});
});
export const handle: Handle = paraglideHandle;

6
src/hooks.ts Normal file
View File

@ -0,0 +1,6 @@
import type { Reroute } from '@sveltejs/kit';
import { deLocalizeUrl } from '$lib/paraglide/runtime';
export const reroute: Reroute = (request) => {
return deLocalizeUrl(request.url).pathname;
};

View File

@ -7,6 +7,7 @@
import ProgressBar from "../visual/ProgressBar.svelte"; import ProgressBar from "../visual/ProgressBar.svelte";
import FormatDropdown from "./FormatDropdown.svelte"; import FormatDropdown from "./FormatDropdown.svelte";
import { categories } from "$lib/converters"; import { categories } from "$lib/converters";
import { m } from "$lib/paraglide/messages";
const length = $derived(files.files.length); const length = $derived(files.files.length);
const progress = $derived(files.files.filter((f) => f.result).length); const progress = $derived(files.files.filter((f) => f.result).length);
@ -27,7 +28,7 @@
disabled={!files.ready} disabled={!files.ready}
> >
<RefreshCw size="24" /> <RefreshCw size="24" />
<p>Convert all</p> <p>{m["convert.panel.convert_all"]()}</p>
</button> </button>
<button <button
class="btn {$effects class="btn {$effects
@ -37,7 +38,7 @@
onclick={() => files.downloadAll()} onclick={() => files.downloadAll()}
> >
<FolderArchiveIcon size="24" /> <FolderArchiveIcon size="24" />
<p>Download all as .zip</p> <p>{m["convert.panel.download_all"]()}</p>
</button> </button>
{#if $isMobile} {#if $isMobile}
<button <button
@ -48,10 +49,13 @@
onclick={() => (files.files = [])} onclick={() => (files.files = [])}
> >
<Trash2Icon size="24" /> <Trash2Icon size="24" />
<p>Remove all files</p> <p>{m["convert.panel.remove_all"]()}</p>
</button> </button>
{:else} {:else}
<Tooltip text="Remove all files" position="right"> <Tooltip
text={m["convert.panel.remove_all"]()}
position="right"
>
<button <button
class="btn p-4 {$effects class="btn p-4 {$effects
? '' ? ''
@ -66,8 +70,11 @@
</div> </div>
<div class="w-full bg-separator h-0.5 flex md:hidden"></div> <div class="w-full bg-separator h-0.5 flex md:hidden"></div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<p class="whitespace-nowrap text-xl">Set all to</p> <p class="whitespace-nowrap text-xl">
{#if files.requiredConverters.length === 1} {m["convert.panel.set_all_to"]()}
</p>
<!-- video and audio together still have this dropdown disabled because audio has just ffmpeg (video has vertd & ffmpeg), even tho it can convert between video and audio -->
{#if files.files.length > 0 && files.files.every((f) => JSON.stringify(f.converters) === JSON.stringify(files.files[0].converters))}
<FormatDropdown <FormatDropdown
onselect={(r) => onselect={(r) =>
files.files.forEach((f) => { files.files.forEach((f) => {
@ -77,9 +84,10 @@
} }
})} })}
{categories} {categories}
dropdownSize={"large"}
/> />
{:else} {:else}
<Dropdown options={["N/A"]} disabled /> <Dropdown options={[m["convert.panel.na"]()]} disabled />
{/if} {/if}
</div> </div>
</div> </div>

View File

@ -101,7 +101,7 @@
{/each} {/each}
</div> </div>
<ChevronDown <ChevronDown
class="w-4 h-4 ml-4 mt-0.5 flex-shrink-0" class="w-4 h-4 ml-3 mt-0.5 flex-shrink-0"
style="transform: rotate({open style="transform: rotate({open
? 180 ? 180
: 0}deg); transition: transform {duration}ms {transition};" : 0}deg); transition: transform {duration}ms {transition};"

View File

@ -7,6 +7,8 @@
extension?: string; extension?: string;
prefix?: string; prefix?: string;
type?: string; type?: string;
min?: number;
max?: number;
}; };
let { let {
@ -17,29 +19,34 @@
extension, extension,
prefix, prefix,
type = "text", type = "text",
min = 0,
max = 100,
}: Props = $props(); }: Props = $props();
</script> </script>
<div class="relative flex w-full {className}"> <div class="relative flex w-full {className}">
<input <input
{type} {type}
{min}
{max}
bind:value bind:value
{placeholder} {placeholder}
{disabled} {disabled}
class="w-full p-3 rounded-lg bg-panel border-2 border-button class="w-full p-3 rounded-lg bg-panel border-2 border-button
{prefix ? 'pl-[2rem]' : 'pl-3'} {prefix ? 'pl-[2rem]' : 'pl-3'}
{extension ? 'pr-[4rem]' : 'pr-3'}" {extension ? 'pr-[4rem]' : 'pr-3'}
{disabled && 'opacity-50 cursor-not-allowed'}"
/> />
{#if prefix} {#if prefix}
<div class="absolute left-0 top-0 bottom-0 flex items-center px-2"> <div class="absolute left-0 top-0 bottom-0 flex items-center px-2">
<span class="text-sm text-gray-400 px-2 py-1 rounded" <span class="text-sm text-gray-400 px-2 py-1 rounded">{prefix}</span
>{prefix}</span
> >
</div> </div>
{/if} {/if}
{#if extension} {#if extension}
<div class="absolute right-0 top-0 bottom-0 flex items-center px-4"> <div class="absolute right-0 top-0 bottom-0 flex items-center px-4">
<span class="text-sm bg-button text-black dynadark:text-white px-2 py-1 rounded" <span
class="text-sm bg-button text-black dynadark:text-white px-2 py-1 rounded"
>{extension}</span >{extension}</span
> >
</div> </div>

View File

@ -1,7 +1,9 @@
<script lang="ts"> <script lang="ts">
import { duration, fade, transition } from "$lib/animation"; import { duration, fade, transition } from "$lib/animation";
import { m } from "$lib/paraglide/messages";
import { isMobile, files } from "$lib/store/index.svelte"; import { isMobile, files } from "$lib/store/index.svelte";
import type { Categories } from "$lib/types"; import type { Categories } from "$lib/types";
import clsx from "clsx";
import { ChevronDown, SearchIcon } from "lucide-svelte"; import { ChevronDown, SearchIcon } from "lucide-svelte";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { quintOut } from "svelte/easing"; import { quintOut } from "svelte/easing";
@ -12,6 +14,7 @@
selected?: string; selected?: string;
onselect?: (option: string) => void; onselect?: (option: string) => void;
disabled?: boolean; disabled?: boolean;
dropdownSize?: "default" | "large" | "small";
}; };
let { let {
@ -20,76 +23,127 @@
selected = $bindable(""), selected = $bindable(""),
onselect, onselect,
disabled, disabled,
dropdownSize = "default",
}: Props = $props(); }: Props = $props();
let open = $state(false); let open = $state(false);
let hover = $state(false);
let dropdown = $state<HTMLDivElement>(); let dropdown = $state<HTMLDivElement>();
let currentCategory = $state<string | null>(); let currentCategory = $state<string | null>();
let searchQuery = $state(""); let searchQuery = $state("");
let dropdownMenu: HTMLElement | undefined = $state(); let dropdownMenu: HTMLElement | undefined = $state();
let rootCategory: string | null = null;
// initialize current category // initialize current category
$effect(() => { $effect(() => {
if (!currentCategory) { if (currentCategory) return;
if (selected) { let foundCat: string | undefined;
const foundCat = Object.keys(categories).find((cat) =>
categories[cat].formats.includes(selected), if (selected) {
); foundCat = Object.keys(categories).find((cat) =>
currentCategory = categories[cat].formats.includes(selected),
foundCat || Object.keys(categories)[0] || null; );
} else { } else {
// find category based on file types // find category based on file types
const fileFormats = files.files.map((f) => f.from); const fileFormats = files.files.map((f) => f.from);
const foundCat = Object.keys(categories).find((cat) => foundCat = Object.keys(categories).find((cat) =>
fileFormats.some((format) => fileFormats.some((format) =>
categories[cat].formats.includes(format), categories[cat].formats.includes(format),
), ),
); );
currentCategory =
foundCat || Object.keys(categories)[0] || null;
}
} }
currentCategory = foundCat || Object.keys(categories)[0] || null;
rootCategory = currentCategory;
}); });
// other available categories based on current category (e.g. converting between video and audio) // other available categories based on current category (e.g. converting between video and audio)
const availableCategories = $derived.by(() => { const availableCategories = $derived.by(() => {
if (!currentCategory) return Object.keys(categories); if (!rootCategory) return Object.keys(categories);
return Object.keys(categories).filter( let finalCategories = Object.keys(categories).filter(
(cat) => (cat) =>
cat === currentCategory || cat === rootCategory ||
categories[cat].canConvertTo?.includes(currentCategory || ""), categories[rootCategory!]?.canConvertTo?.includes(cat),
); );
if (from === ".gif") finalCategories.push("video");
return finalCategories;
}); });
const shouldInclude = (format: string, category: string): boolean => {
// if converting from audio to video, dont show gifs
if (
categories["audio"]?.formats.includes(from ?? "") &&
format === ".gif"
) {
return false;
}
return true;
};
const filteredData = $derived.by(() => { const filteredData = $derived.by(() => {
const normalize = (str: string) => str.replace(/^\./, "").toLowerCase();
// if no query, return formats for current category
if (!searchQuery) { if (!searchQuery) {
return { return {
categories: availableCategories, categories: availableCategories,
formats: currentCategory formats: currentCategory
? categories[currentCategory].formats ? categories[currentCategory].formats.filter((format) =>
shouldInclude(format, currentCategory!),
)
: [], : [],
}; };
} }
const searchLower = normalize(searchQuery);
// filter categories that have matching formats // find all categories that have formats matching the search query
const matchingCategories = availableCategories.filter((cat) => const matchingCategories = availableCategories.filter((cat) =>
categories[cat].formats.some((format) => categories[cat].formats.some(
format.toLowerCase().includes(searchQuery.toLowerCase()), (format) =>
normalize(format).includes(searchLower) &&
shouldInclude(format, cat),
), ),
); );
if (matchingCategories.length === 0) {
return {
categories: availableCategories,
formats: [],
};
}
// only show formats from the current category that match the search // if current category has no matches, switch to first category that does
const filteredFormats = const currentCategoryHasMatches =
currentCategory && categories[currentCategory] currentCategory &&
? categories[currentCategory].formats.filter((format) => matchingCategories.some((cat) => cat === currentCategory);
format if (!currentCategoryHasMatches && matchingCategories.length > 0) {
.toLowerCase() const newCategory = matchingCategories[0];
.includes(searchQuery.toLowerCase()), currentCategory = newCategory;
) }
: [];
// return formats only from the current category that match the search
let filteredFormats = currentCategory
? categories[currentCategory].formats.filter(
(format) =>
normalize(format).includes(searchLower) &&
shouldInclude(format, currentCategory!),
)
: [];
// sorting exact match first, then others
filteredFormats = filteredFormats.sort((a, b) => {
const aExact = normalize(a) === searchLower;
const bExact = normalize(b) === searchLower;
if (aExact && !bExact) return -1;
if (!aExact && bExact) return 1;
return 0;
});
return { return {
categories: matchingCategories, categories:
matchingCategories.length > 0
? matchingCategories
: availableCategories,
formats: filteredFormats, formats: filteredFormats,
}; };
}); });
@ -97,6 +151,21 @@
const selectOption = (option: string) => { const selectOption = (option: string) => {
selected = option; selected = option;
open = false; open = false;
// find the category of this option if it's not in the current category
if (
currentCategory &&
!categories[currentCategory].formats.includes(option)
) {
const formatCategory = Object.keys(categories).find((cat) =>
categories[cat].formats.includes(option),
);
if (formatCategory) {
currentCategory = formatCategory;
}
}
onselect?.(option); onselect?.(option);
}; };
@ -106,7 +175,39 @@
}; };
const handleSearch = (event: Event) => { const handleSearch = (event: Event) => {
searchQuery = (event.target as HTMLInputElement).value; const query = (event.target as HTMLInputElement).value;
searchQuery = query;
// find which categories have matching formats & switch
if (query) {
const queryLower = query.toLowerCase();
const categoriesWithMatches = availableCategories.filter((cat) =>
categories[cat].formats.some((format) =>
format.toLowerCase().includes(queryLower),
),
);
if (categoriesWithMatches.length > 0) {
const currentHasMatches =
currentCategory &&
categories[currentCategory].formats.some((format) =>
format.toLowerCase().includes(queryLower),
);
if (!currentHasMatches) {
currentCategory = categoriesWithMatches[0];
}
}
}
};
const onEnter = (event: KeyboardEvent) => {
if (event.key === "Enter") {
event.preventDefault();
if (filteredData.formats.length > 0) {
selectOption(filteredData.formats[0]);
}
}
}; };
const clickDropdown = () => { const clickDropdown = () => {
@ -159,12 +260,12 @@
class="relative flex items-center justify-center w-full font-display px-3 py-3.5 bg-button rounded-full overflow-hidden cursor-pointer focus:!outline-none class="relative flex items-center justify-center w-full font-display px-3 py-3.5 bg-button rounded-full overflow-hidden cursor-pointer focus:!outline-none
{disabled ? 'opacity-50 cursor-auto' : 'cursor-pointer'}" {disabled ? 'opacity-50 cursor-auto' : 'cursor-pointer'}"
onclick={() => clickDropdown()} onclick={() => clickDropdown()}
onmouseenter={() => (hover = true)}
onmouseleave={() => (hover = false)}
{disabled} {disabled}
> >
<!-- <p>{selected}</p> --> <!-- <p>{selected}</p> -->
<div class="grid grid-cols-1 grid-rows-1 w-fit flex-grow-0"> <div
class="grid grid-cols-1 grid-rows-1 w-fit flex-grow-0 max-h-[2.5rem] overflow-hidden"
>
{#key selected} {#key selected}
<p <p
in:fade={{ in:fade={{
@ -175,7 +276,7 @@
duration, duration,
easing: quintOut, easing: quintOut,
}} }}
class="col-start-1 row-start-1 text-center font-body font-medium" class="col-start-1 row-start-1 text-center font-body font-medium truncate max-w-[4rem]"
> >
{selected} {selected}
</p> </p>
@ -183,7 +284,7 @@
{#if currentCategory} {#if currentCategory}
{#each categories[currentCategory].formats as option} {#each categories[currentCategory].formats as option}
<p <p
class="col-start-1 row-start-1 invisible pointer-events-none" class="col-start-1 row-start-1 invisible pointer-events-none truncate max-w-[2.5rem]"
> >
{option} {option}
</p> </p>
@ -191,7 +292,7 @@
{/if} {/if}
</div> </div>
<ChevronDown <ChevronDown
class="w-4 h-4 ml-4 mt-0.5 flex-shrink-0" class="w-4 h-4 ml-3 mt-0.5 flex-shrink-0"
style="transform: rotate({open style="transform: rotate({open
? 180 ? 180
: 0}deg); transition: transform {duration}ms {transition};" : 0}deg); transition: transform {duration}ms {transition};"
@ -200,24 +301,31 @@
{#if open} {#if open}
<div <div
bind:this={dropdownMenu} bind:this={dropdownMenu}
style={hover ? "will-change: opacity, fade, transform" : ""}
transition:fade={{ transition:fade={{
duration, duration,
easing: quintOut, easing: quintOut,
}} }}
class={$isMobile class={clsx(
? "fixed inset-x-0 bottom-0 w-full z-[200] shadow-xl bg-panel-alt shadow-black/25 rounded-t-2xl overflow-hidden" $isMobile
: "w-[250%] min-w-full shadow-xl bg-panel-alt shadow-black/25 absolute -translate-x-1/2 top-full mt-2 z-50 rounded-2xl overflow-hidden"} ? "fixed inset-x-0 bottom-0 w-full z-[200] shadow-xl bg-panel-alt shadow-black/25 rounded-t-2xl overflow-hidden"
: "min-w-full shadow-xl bg-panel-alt shadow-black/25 absolute -translate-x-1/2 top-full mt-2 z-50 rounded-2xl overflow-hidden",
!$isMobile && {
"w-[320%]": dropdownSize === "large",
"w-[250%]": dropdownSize === "default",
"w-[150%]": dropdownSize === "small",
},
)}
> >
<!-- search box --> <!-- search box -->
<div class="p-3 w-full"> <div class="p-3 w-full">
<div class="relative"> <div class="relative">
<input <input
type="text" type="text"
placeholder="Search format" placeholder={m["convert.dropdown.placeholder"]()}
class="flex-grow w-full !pl-11 !pr-3 rounded-lg bg-panel text-foreground" class="flex-grow w-full !pl-11 !pr-3 rounded-lg bg-panel text-foreground"
bind:value={searchQuery} bind:value={searchQuery}
oninput={handleSearch} oninput={handleSearch}
onkeydown={onEnter}
onfocus={() => {}} onfocus={() => {}}
id="format-search" id="format-search"
autocomplete="off" autocomplete="off"
@ -227,38 +335,56 @@
> >
<SearchIcon class="w-4 h-4" /> <SearchIcon class="w-4 h-4" />
</span> </span>
{#if searchQuery}
<span
class="absolute right-2 top-1/2 -translate-y-1/2 text-xs text-muted"
style="font-size: 0.7rem;"
>
{filteredData.formats.length}
{filteredData.formats.length === 1
? "result"
: "results"}
</span>
{/if}
</div> </div>
</div> </div>
<!-- available categories --> <!-- available categories -->
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
{#each filteredData.categories as category} {#each filteredData.categories as category}
<button <button
class="flex-grow text-lg hover:text-muted/20 border-b-[1px] pb-2 capitalize {currentCategory === class="flex-grow text-lg hover:text-muted/20 border-b-[1px] pb-2 capitalize
category {currentCategory === category
? 'text-accent border-b-accent' ? 'text-accent border-b-accent'
: 'border-b-separator text-muted'}" : 'border-b-separator text-muted'}"
onclick={() => selectCategory(category)} onclick={() => selectCategory(category)}
> >
{category} {(m as any)[`convert.dropdown.${category}`]?.()}
</button> </button>
{/each} {/each}
</div> </div>
<!-- available formats --> <!-- available formats -->
<div class="max-h-80 overflow-y-auto grid grid-cols-3 gap-2 p-2"> <div class="max-h-80 overflow-y-auto grid grid-cols-3 gap-2 p-2">
{#each filteredData.formats as format} {#if filteredData.formats.length > 0}
<button {#each filteredData.formats as format}
class="w-full p-2 text-center rounded-xl <button
{format === selected class="w-full p-2 text-center rounded-xl
? 'bg-accent text-black' {format === selected
: 'hover:bg-panel'} ? 'bg-accent text-black'
{format === from ? 'bg-separator' : ''}" : format === from
onclick={() => selectOption(format)} ? 'bg-separator'
> : 'hover:bg-panel'}"
{format} onclick={() => selectOption(format)}
</button> >
{/each} {format}
</button>
{/each}
{:else}
<div class="col-span-3 text-center p-4 text-muted">
{searchQuery
? "No formats match your search"
: "No formats available"}
</div>
{/if}
</div> </div>
</div> </div>
{/if} {/if}

View File

@ -7,6 +7,7 @@
import { converters } from "$lib/converters"; import { converters } from "$lib/converters";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { page } from "$app/state"; import { page } from "$app/state";
import { m } from "$lib/paraglide/messages";
type Props = { type Props = {
class?: string; class?: string;
@ -98,7 +99,11 @@
<UploadIcon class="w-full h-full text-on-accent" /> <UploadIcon class="w-full h-full text-on-accent" />
</div> </div>
<h2 class="text-center text-2xl font-semibold mt-4"> <h2 class="text-center text-2xl font-semibold mt-4">
Drop or click to {jpegify ? "JPEGIFY" : "convert"} {m["upload.uploader.text"]({
action: jpegify
? m["upload.uploader.jpegify"]()
: m["upload.uploader.convert"]()
})}
</h2> </h2>
</Panel> </Panel>
</button> </button>

View File

@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
import { GITHUB_URL_VERT, DISCORD_URL } from "$lib/consts"; import { GITHUB_URL_VERT, DISCORD_URL } from "$lib/consts";
import { m } from "$lib/paraglide/messages";
const items = Object.entries({ const items = $derived([
//"Privacy policy": "#", [m["footer.source_code"](), GITHUB_URL_VERT],
"Source code": GITHUB_URL_VERT, [m["footer.discord_server"](), DISCORD_URL],
"Discord server": DISCORD_URL, ]);
});
const year = new Date().getFullYear(); const year = new Date().getFullYear();
</script> </script>
@ -16,7 +16,7 @@
<div <div
class="w-full h-full flex items-center justify-center text-muted gap-3 relative" class="w-full h-full flex items-center justify-center text-muted gap-3 relative"
> >
<p>© {year} VERT.</p> <p>{m["footer.copyright"]({ year })}</p>
{#each items as [name, url] (name)} {#each items as [name, url] (name)}
<!-- bullet point --> <!-- bullet point -->
<p></p> <p></p>

View File

@ -22,6 +22,7 @@
import Logo from "../../visual/svg/Logo.svelte"; import Logo from "../../visual/svg/Logo.svelte";
import { beforeNavigate } from "$app/navigation"; import { beforeNavigate } from "$app/navigation";
import Tooltip from "$lib/components/visual/Tooltip.svelte"; import Tooltip from "$lib/components/visual/Tooltip.svelte";
import { m } from "$lib/paraglide/messages";
const items = $derived< const items = $derived<
{ {
@ -33,13 +34,13 @@
}[] }[]
>([ >([
{ {
name: "Upload", name: m["navbar.upload"](),
url: "/", url: "/",
activeMatch: (pathname) => pathname === "/", activeMatch: (pathname) => pathname === "/",
icon: UploadIcon, icon: UploadIcon,
}, },
{ {
name: "Convert", name: m["navbar.convert"](),
url: "/convert/", url: "/convert/",
activeMatch: (pathname) => activeMatch: (pathname) =>
pathname === "/convert/" || pathname === "/convert", pathname === "/convert/" || pathname === "/convert",
@ -47,13 +48,13 @@
badge: files.files.length, badge: files.files.length,
}, },
{ {
name: "Settings", name: m["navbar.settings"](),
url: "/settings/", url: "/settings/",
activeMatch: (pathname) => pathname.startsWith("/settings"), activeMatch: (pathname) => pathname.startsWith("/settings"),
icon: SettingsIcon, icon: SettingsIcon,
}, },
{ {
name: "About", name: m["navbar.about"](),
url: "/about/", url: "/about/",
activeMatch: (pathname) => pathname.startsWith("/about"), activeMatch: (pathname) => pathname.startsWith("/about"),
icon: InfoIcon, icon: InfoIcon,
@ -63,6 +64,7 @@
let links = $state<HTMLAnchorElement[]>([]); let links = $state<HTMLAnchorElement[]>([]);
let container = $state<HTMLDivElement>(); let container = $state<HTMLDivElement>();
let containerRect = $derived(container?.getBoundingClientRect()); let containerRect = $derived(container?.getBoundingClientRect());
let isInitialized = $state(false);
const linkRects = $derived(links.map((l) => l.getBoundingClientRect())); const linkRects = $derived(links.map((l) => l.getBoundingClientRect()));
@ -72,6 +74,16 @@
const isSecretPage = $derived(selectedIndex === -1); const isSecretPage = $derived(selectedIndex === -1);
$effect(() => {
if (containerRect && linkRects.length > 0 && links.length > 0) {
setTimeout(() => {
isInitialized = true;
}, 10);
} else {
isInitialized = false;
}
});
beforeNavigate((e) => { beforeNavigate((e) => {
const oldIndex = items.findIndex((i) => const oldIndex = items.findIndex((i) =>
i.activeMatch(e.from?.url.pathname || ""), i.activeMatch(e.from?.url.pathname || ""),
@ -144,7 +156,7 @@
</div> </div>
{/if} {/if}
</div> </div>
<p class="font-medium hidden md:flex"> <p class="font-medium hidden md:flex min-w-0">
{item.name} {item.name}
</p> </p>
</div> </div>
@ -156,7 +168,7 @@
<div bind:this={container}> <div bind:this={container}>
<Panel class="max-w-[778px] w-screen h-20 flex items-center gap-3 relative"> <Panel class="max-w-[778px] w-screen h-20 flex items-center gap-3 relative">
{@const linkRect = linkRects.at(selectedIndex) || linkRects[0]} {@const linkRect = linkRects.at(selectedIndex) || linkRects[0]}
{#if linkRect} {#if linkRect && isInitialized}
<div <div
class="absolute bg-panel-highlight rounded-xl" class="absolute bg-panel-highlight rounded-xl"
style="width: {linkRect.width}px; height: {linkRect.height}px; top: {linkRect.top - style="width: {linkRect.width}px; height: {linkRect.height}px; top: {linkRect.top -
@ -180,7 +192,7 @@
{@render link(item, i)} {@render link(item, i)}
{/each} {/each}
<div class="w-0.5 bg-separator h-full hidden md:flex"></div> <div class="w-0.5 bg-separator h-full hidden md:flex"></div>
<Tooltip text="Toggle theme" position="right"> <Tooltip text={m["navbar.toggle_theme"]()} position="right">
<button <button
onclick={() => { onclick={() => {
const isDark = const isDark =

View File

@ -51,7 +51,7 @@
</script> </script>
<div <div
class="flex items-center justify-between max-w-sm p-4 gap-4 bg-accent-{color} border-accent-{color}-alt border-l-4 rounded-lg shadow-md" class="flex items-center justify-between max-w-[100%] md:max-w-md p-4 gap-4 bg-accent-{color} border-accent-{color}-alt border-l-4 rounded-lg shadow-md"
in:fly={{ in:fly={{
duration: durations.enter, duration: durations.enter,
easing: quintOut, easing: quintOut,
@ -70,7 +70,9 @@
stroke="2" stroke="2"
fill="none" fill="none"
/> />
<p class="text-black font-normal whitespace-pre-wrap">{message}</p> <p class="text-black font-normal whitespace-pre-wrap break-all">
{message}
</p>
</div> </div>
<button <button
class="text-gray-600 hover:text-black flex-shrink-0" class="text-gray-600 hover:text-black flex-shrink-0"

View File

@ -1,18 +1,50 @@
<script lang="ts"> <script lang="ts">
import { fade } from "$lib/animation"; import { fade } from "$lib/animation";
interface Props { interface Props {
children: () => any; children: () => any;
text: string; text: string;
className?: string;
position?: "top" | "bottom" | "left" | "right"; position?: "top" | "bottom" | "left" | "right";
} }
let { children, text, position = "top" }: Props = $props(); let { children, text, className, position = "top" }: Props = $props();
let showTooltip = $state(false); let showTooltip = $state(false);
let timeout: number = 0; let timeout: number = 0;
let triggerElement: HTMLElement;
let tooltipElement = $state<HTMLElement>();
let tooltipPosition = $state({ x: 0, y: 0 });
function show() { function show() {
timeout = setTimeout(() => { timeout = setTimeout(() => {
if (!triggerElement) return;
const rect = triggerElement.getBoundingClientRect();
switch (position) {
case "top":
tooltipPosition = {
x: rect.left + rect.width / 2,
y: rect.top - 10,
};
break;
case "bottom":
tooltipPosition = {
x: rect.left + rect.width / 2,
y: rect.bottom + 10,
};
break;
case "left":
tooltipPosition = {
x: rect.left - 10,
y: rect.top + rect.height / 2,
};
break;
case "right":
tooltipPosition = {
x: rect.right + 10,
y: rect.top + rect.height / 2,
};
break;
}
showTooltip = true; showTooltip = true;
}, 500); }, 500);
} }
@ -21,10 +53,23 @@
showTooltip = false; showTooltip = false;
clearTimeout(timeout); clearTimeout(timeout);
} }
$effect(() => {
if (showTooltip && tooltipElement) {
document.body.appendChild(tooltipElement);
}
return () => {
if (tooltipElement && tooltipElement.parentNode === document.body) {
document.body.removeChild(tooltipElement);
}
};
});
</script> </script>
<div <span
class="relative inline-block" bind:this={triggerElement}
class="relative inline-block {className}"
onmouseenter={show} onmouseenter={show}
onmouseleave={hide} onmouseleave={hide}
onfocusin={show} onfocusin={show}
@ -34,26 +79,30 @@
role="tooltip" role="tooltip"
> >
{@render children()} {@render children()}
{#if showTooltip} </span>
<div
class="tooltip tooltip-{position}" {#if showTooltip}
transition:fade={{ <span
duration: 100, bind:this={tooltipElement}
}} class="tooltip tooltip-{position}"
> style="left: {tooltipPosition.x}px; top: {tooltipPosition.y}px;"
{text} transition:fade={{
</div> duration: 100,
{/if} }}
</div> >
{text}
</span>
{/if}
<style> <style>
.tooltip { .tooltip {
--border-size: 1px; --border-size: 1px;
@apply absolute z-10 bg-panel-alt text-foreground border border-stone-400 dynadark:border-white drop-shadow-lg text-xs px-4 py-2 rounded-full whitespace-nowrap pointer-events-none; @apply fixed bg-panel-alt text-foreground border border-stone-400 dynadark:border-white drop-shadow-lg text-xs rounded-full pointer-events-none z-[999] max-w-xs break-words whitespace-normal;
@apply px-5 py-2.5;
} }
.tooltip-top { .tooltip-top {
@apply bottom-full left-1/2 -translate-x-1/2 mb-3; transform: translate(-50%, -100%);
} }
.tooltip-top::after { .tooltip-top::after {
@ -67,7 +116,7 @@
} }
.tooltip-bottom { .tooltip-bottom {
@apply top-full left-1/2 -translate-x-1/2 mt-3; transform: translate(-50%, 20%);
} }
.tooltip-bottom::after { .tooltip-bottom::after {
@ -81,7 +130,7 @@
} }
.tooltip-left { .tooltip-left {
@apply right-full top-1/2 -translate-y-1/2 mr-3; transform: translate(-100%, -50%);
} }
.tooltip-left::after { .tooltip-left::after {
@ -89,7 +138,7 @@
} }
.tooltip-right { .tooltip-right {
@apply left-full top-1/2 -translate-y-1/2 ml-3; transform: translate(0%, -50%);
} }
.tooltip-right::after { .tooltip-right::after {

View File

@ -5,8 +5,9 @@ export class FormatInfo {
constructor( constructor(
name: string, name: string,
public fromSupported: boolean, public fromSupported = true,
public toSupported: boolean, public toSupported = true,
public isNative = true,
) { ) {
this.name = name; this.name = name;
if (!this.name.startsWith(".")) { if (!this.name.startsWith(".")) {

View File

@ -4,6 +4,34 @@ import { FFmpeg } from "@ffmpeg/ffmpeg";
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import { error, log } from "$lib/logger"; import { error, log } from "$lib/logger";
import { addToast } from "$lib/store/ToastProvider"; import { addToast } from "$lib/store/ToastProvider";
import { m } from "$lib/paraglide/messages";
import { Settings } from "$lib/sections/settings/index.svelte";
// TODO: differentiate in UI? (not native formats)
const videoFormats = [
"mkv",
"mp4",
"avi",
"mov",
"webm",
"ts",
"mts",
"m2ts",
"wmv",
"mpg",
"mpeg",
"flv",
"f4v",
"vob",
"m4v",
"3gp",
"3g2",
"mxf",
"ogv",
"rm",
"rmvb",
"divx",
];
export class FFmpegConverter extends Converter { export class FFmpegConverter extends Converter {
private ffmpeg: FFmpeg = null!; private ffmpeg: FFmpeg = null!;
@ -15,6 +43,7 @@ export class FFmpegConverter extends Converter {
new FormatInfo("wav", true, true), new FormatInfo("wav", true, true),
new FormatInfo("flac", true, true), new FormatInfo("flac", true, true),
new FormatInfo("ogg", true, true), new FormatInfo("ogg", true, true),
new FormatInfo("mogg", true, false),
new FormatInfo("oga", true, true), new FormatInfo("oga", true, true),
new FormatInfo("opus", true, true), new FormatInfo("opus", true, true),
new FormatInfo("aac", true, true), new FormatInfo("aac", true, true),
@ -22,9 +51,24 @@ export class FFmpegConverter extends Converter {
new FormatInfo("wma", true, true), new FormatInfo("wma", true, true),
new FormatInfo("amr", true, true), new FormatInfo("amr", true, true),
new FormatInfo("ac3", true, true), new FormatInfo("ac3", true, true),
new FormatInfo("alac", true, false), new FormatInfo("alac", true, true),
new FormatInfo("aiff", true, true), new FormatInfo("aiff", true, true),
new FormatInfo("aifc", true, true),
new FormatInfo("aif", true, true), new FormatInfo("aif", true, true),
new FormatInfo("mp1", true, false),
new FormatInfo("mp2", true, true),
new FormatInfo("mpc", true, false), // unknown if it works, can't find sample file but ffmpeg should support i think?
//new FormatInfo("raw", true, false), // usually pcm
new FormatInfo("dsd", true, false), // dsd
new FormatInfo("dsf", true, false), // dsd
new FormatInfo("dff", true, false), // dsd
new FormatInfo("mqa", true, false),
new FormatInfo("au", true, true),
new FormatInfo("caf", true, true),
new FormatInfo("m4b", true, true),
new FormatInfo("voc", true, true),
new FormatInfo("weba", true, true),
...videoFormats.map((f) => new FormatInfo(f, true, true, false)),
]; ];
public readonly reportsProgress = true; public readonly reportsProgress = true;
@ -47,41 +91,542 @@ export class FFmpegConverter extends Converter {
})(); })();
} catch (err) { } catch (err) {
error(["converters", this.name], `error loading ffmpeg: ${err}`); error(["converters", this.name], `error loading ffmpeg: ${err}`);
addToast( addToast("error", m["workers.errors.ffmpeg"]());
"error",
`Error loading ffmpeg, some features may not work.`,
);
} }
} }
public async convert(input: VertFile, to: string): Promise<VertFile> { public async convert(input: VertFile, to: string): Promise<VertFile> {
if (!to.startsWith(".")) to = `.${to}`; if (!to.startsWith(".")) to = `.${to}`;
const ffmpeg = new FFmpeg();
ffmpeg.on("progress", (progress) => { let conversionError: string | null = null;
input.progress = progress.progress * 100; const ffmpeg = await this.setupFFmpeg(input);
});
const baseURL = // listen for errors during conversion
"https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.6/dist/esm"; const errorListener = (l: { message: string }) => {
await ffmpeg.load({ const msg = l.message;
coreURL: `${baseURL}/ffmpeg-core.js`, if (
wasmURL: `${baseURL}/ffmpeg-core.wasm`, msg.includes("Specified sample rate") &&
}); msg.includes("is not supported")
) {
const rate = Settings.instance.settings.ffmpegCustomSampleRate;
conversionError = m["workers.errors.invalid_rate"]({ rate });
} else if (msg.includes("Stream map '0:a:0' matches no streams.")) {
conversionError = m["workers.errors.no_audio"]();
} else if (
msg.includes("Error initializing output stream") ||
msg.includes("Error while opening encoder") ||
msg.includes("Error while opening decoder") ||
(msg.includes("Error") && msg.includes("stream")) ||
msg.includes("Conversion failed!")
) {
// other general errors
if (!conversionError) conversionError = msg;
}
};
ffmpeg.on("log", errorListener);
const buf = new Uint8Array(await input.file.arrayBuffer()); const buf = new Uint8Array(await input.file.arrayBuffer());
await ffmpeg.writeFile("input", buf); await ffmpeg.writeFile("input", buf);
log( log(
["converters", this.name], ["converters", this.name],
`wrote ${input.name} to ffmpeg virtual fs`, `wrote ${input.name} to ffmpeg virtual fs`,
); );
await ffmpeg.exec(["-i", "input", "output" + to]);
log(["converters", this.name], `executed ffmpeg command`); const command = await this.buildConversionCommand(ffmpeg, input, to);
log(["converters", this.name], `FFmpeg command: ${command.join(" ")}`);
await ffmpeg.exec(command);
log(["converters", this.name], "executed ffmpeg command");
if (conversionError) {
ffmpeg.off("log", errorListener);
ffmpeg.terminate();
throw new Error(conversionError);
}
const output = (await ffmpeg.readFile( const output = (await ffmpeg.readFile(
"output" + to, "output" + to,
)) as unknown as Uint8Array; )) as unknown as Uint8Array;
if (!output || output.length === 0) {
ffmpeg.off("log", errorListener);
ffmpeg.terminate();
throw new Error("empty file returned");
}
const outputFileName =
input.name.split(".").slice(0, -1).join(".") + to;
log( log(
["converters", this.name], ["converters", this.name],
`read ${input.name.split(".").slice(0, -1).join(".") + to} from ffmpeg virtual fs`, `read ${outputFileName} from ffmpeg virtual fs`,
); );
ffmpeg.off("log", errorListener);
ffmpeg.terminate(); ffmpeg.terminate();
return new VertFile(new File([output], input.name), to);
const outBuf = new Uint8Array(output).buffer.slice(0);
return new VertFile(new File([outBuf], outputFileName), to);
}
private async setupFFmpeg(input: VertFile): Promise<FFmpeg> {
const ffmpeg = new FFmpeg();
ffmpeg.on("progress", (progress) => {
input.progress = progress.progress * 100;
});
ffmpeg.on("log", (l) => {
log(["converters", this.name], l.message);
});
const baseURL =
"https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm";
await ffmpeg.load({
coreURL: `${baseURL}/ffmpeg-core.js`,
wasmURL: `${baseURL}/ffmpeg-core.wasm`,
});
return ffmpeg;
}
private async detectAudioBitrate(ffmpeg: FFmpeg): Promise<number | null> {
const args = [
"-v",
"quiet",
"-select_streams",
"a:0",
"-show_entries",
"stream=bit_rate",
"-of",
"default=noprint_wrappers=1:nokey=1",
"input",
];
try {
let bitrate: number | null = null;
const bitrateListener = (event: { message: string }) => {
if (bitrate !== null) return;
const n = parseInt(event.message.trim(), 10);
if (!n) return;
bitrate = Math.round(n / 1000);
log(
["converters", this.name],
`Detected stream audio bitrate: ${bitrate} kbps`,
);
};
ffmpeg.on("log", bitrateListener);
try {
await ffmpeg.ffprobe.call(ffmpeg, args);
return bitrate;
} finally {
ffmpeg.off("log", bitrateListener);
}
} catch {
return null;
}
}
private async detectAudioSampleRate(
ffmpeg: FFmpeg,
): Promise<number | null> {
const args = [
"-v",
"quiet",
"-select_streams",
"a:0",
"-show_entries",
"stream=sample_rate",
"-of",
"default=noprint_wrappers=1:nokey=1",
"input",
];
try {
let sampleRate: number | null = null;
const sampleRateListener = (event: { message: string }) => {
if (sampleRate !== null) return;
const n = parseInt(event.message.trim(), 10);
if (!n) return;
sampleRate = n;
log(
["converters", this.name],
`Detected stream audio sample rate: ${sampleRate} Hz`,
);
};
ffmpeg.on("log", sampleRateListener);
try {
await ffmpeg.ffprobe.call(ffmpeg, args);
return sampleRate;
} finally {
ffmpeg.off("log", sampleRateListener);
}
} catch {
return null;
}
}
private async buildConversionCommand(
ffmpeg: FFmpeg,
input: VertFile,
to: string,
): Promise<string[]> {
const inputFormat = input.from.slice(1);
const outputFormat = to.slice(1);
const lossless = ["flac", "alac", "wav", "dsd", "dsf", "dff"];
const userSetting = Settings.instance.settings.ffmpegQuality;
const userSampleRate = Settings.instance.settings.ffmpegSampleRate;
const customSampleRate =
Settings.instance.settings.ffmpegCustomSampleRate ?? 44100;
let audioBitrateArgs: string[] = [];
let sampleRateArgs: string[] = [];
const isLosslessToLossy =
lossless.includes(inputFormat) && !lossless.includes(outputFormat);
if (userSetting !== "auto") {
// user's setting
audioBitrateArgs = ["-b:a", `${userSetting}k`];
log(
["converters", this.name],
`using user setting for audio bitrate: ${userSetting}`,
);
} else {
// detect bitrate of original file and use
if (isLosslessToLossy) {
// use safe default
audioBitrateArgs = ["-b:a", "128k"];
log(
["converters", this.name],
`converting from lossless to lossy, using default audio bitrate: 128k`,
);
} else {
const inputBitrate = await this.detectAudioBitrate(ffmpeg);
audioBitrateArgs = inputBitrate
? ["-b:a", `${inputBitrate}k`]
: [];
log(
["converters", this.name],
`using detected audio bitrate: ${inputBitrate}k`,
);
}
}
// sample rate setting
if (userSampleRate !== "auto") {
const rate =
userSampleRate === "custom"
? customSampleRate.toString()
: userSampleRate;
sampleRateArgs = ["-ar", rate];
log(
["converters", this.name],
`using user setting for sample rate: ${rate}`,
);
} else {
// detect sample rate of original file and use
if (isLosslessToLossy) {
// use safe default
sampleRateArgs = ["-ar", "44100"];
log(
["converters", this.name],
`converting from lossless to lossy, using default sample rate: 44100Hz`,
);
} else {
const inputSampleRate =
await this.detectAudioSampleRate(ffmpeg);
sampleRateArgs = inputSampleRate
? ["-ar", inputSampleRate.toString()]
: [];
log(
["converters", this.name],
`using detected audio sample rate: ${inputSampleRate}Hz`,
);
}
}
// video to audio
if (videoFormats.includes(inputFormat)) {
log(
["converters", this.name],
`Converting video ${input.from} to audio ${to}`,
);
return [
"-i",
"input",
"-map",
"0:a:0",
...audioBitrateArgs,
...sampleRateArgs,
"output" + to,
];
}
// audio to video
if (videoFormats.includes(outputFormat)) {
log(
["converters", this.name],
`Converting audio ${input.from} to video ${to}`,
);
const hasAlbumArt = await this.extractAlbumArt(ffmpeg);
const codecArgs = toArgs(to);
if (hasAlbumArt) {
log(
["converters", this.name],
"Using album art as video background",
);
return [
"-loop",
"1",
"-i",
"cover.jpg",
"-i",
"input",
"-vf",
"scale=trunc(iw/2)*2:trunc(ih/2)*2",
"-shortest",
"-pix_fmt",
"yuv420p",
"-r",
"1",
...codecArgs,
...audioBitrateArgs,
...sampleRateArgs,
"output" + to,
];
} else {
log(["converters", this.name], "Using solid color background");
return [
"-f",
"lavfi",
"-i",
"color=c=black:s=512x512:rate=1",
"-i",
"input",
"-shortest",
"-pix_fmt",
"yuv420p",
"-r",
"1",
...codecArgs,
...audioBitrateArgs,
...sampleRateArgs,
"output" + to,
];
}
}
// audio to audio
log(
["converters", this.name],
`Converting audio ${input.from} to audio ${to}`,
);
const { audio: audioCodec } = getCodecs(to);
return [
"-i",
"input",
"-c:a",
audioCodec,
...audioBitrateArgs,
...sampleRateArgs,
"output" + to,
];
}
private async extractAlbumArt(ffmpeg: FFmpeg): Promise<boolean> {
// extract using stream mapping (should work for most)
if (
await this.tryExtractAlbumArt(ffmpeg, [
"-i",
"input",
"-map",
"0:1",
"-c:v",
"copy",
"-update",
"1",
"cover.jpg",
])
) {
log(
["converters", this.name],
"Successfully extracted album art from stream 0:1",
);
return true;
}
// fallback: extract without stream mapping (this probably won't happen)
if (
await this.tryExtractAlbumArt(ffmpeg, [
"-i",
"input",
"-an",
"-c:v",
"copy",
"-update",
"1",
"cover.jpg",
])
) {
log(
["converters", this.name],
"Successfully extracted album art (fallback method)",
);
return true;
}
log(
["converters", this.name],
"No album art found, will create solid color background",
);
return false;
}
private async tryExtractAlbumArt(
ffmpeg: FFmpeg,
command: string[],
): Promise<boolean> {
try {
await ffmpeg.exec(command);
const coverData = await ffmpeg.readFile("cover.jpg");
return !!(coverData && (coverData as Uint8Array).length > 0);
} catch {
return false;
}
} }
} }
// and here i was, thinking i'd be done with ffmpeg after finishing vertd
// but OH NO we just HAD to have someone suggest to allow album art video generation.
//
// i hate you SO much.
// - love, maddie
const toArgs = (ext: string): string[] => {
const codecs = getCodecs(ext);
const args = ["-c:v", codecs.video];
switch (codecs.video) {
case "libx264": {
args.push(
"-preset",
"ultrafast",
"-crf",
"18",
"-tune",
"stillimage",
);
break;
}
case "libvpx": {
args.push("-c:v", "libvpx-vp9");
break;
}
case "mpeg2video": {
// for mpeg, mpg, vob, mxf
if (ext === ".mxf") args.push("-ar", "48000"); // force 48kHz sample rate
break;
}
}
args.push("-c:a", codecs.audio);
if (codecs.audio === "aac") args.push("-strict", "experimental");
if (ext === ".divx") args.unshift("-f", "avi");
if (ext === ".mxf") args.push("-strict", "unofficial");
return args;
};
const getCodecs = (ext: string): { video: string; audio: string } => {
switch (ext) {
// video <-> audio
case ".mp4":
case ".mkv":
case ".mov":
case ".mts":
case ".ts":
case ".m2ts":
case ".flv":
case ".f4v":
case ".m4v":
case ".3gp":
case ".3g2":
return { video: "libx264", audio: "aac" };
case ".wmv":
return { video: "wmv2", audio: "wmav2" };
case ".webm":
case ".ogv":
return {
video: ext === ".webm" ? "libvpx" : "libtheora",
audio: "libvorbis",
};
case ".avi":
case ".divx":
return { video: "mpeg4", audio: "libmp3lame" };
case ".mpg":
case ".mpeg":
case ".vob":
return { video: "mpeg2video", audio: "mp2" };
case ".mxf":
return { video: "mpeg2video", audio: "pcm_s16le" };
// audio
case ".mp3":
return { video: "libx264", audio: "libmp3lame" };
case ".flac":
return { video: "libx264", audio: "flac" };
case ".wav":
return { video: "libx264", audio: "pcm_s16le" };
case ".ogg":
case ".oga":
return { video: "libx264", audio: "libvorbis" };
case ".opus":
return { video: "libx264", audio: "libopus" };
case ".aac":
return { video: "libx264", audio: "aac" };
case ".m4a":
return { video: "libx264", audio: "aac" };
case ".alac":
return { video: "libx264", audio: "alac" };
case ".wma":
return { video: "libx264", audio: "wmav2" };
default:
return { video: "libx264", audio: "aac" };
}
};
export const CONVERSION_BITRATES = [
"auto",
320,
256,
192,
128,
96,
64,
32,
] as const;
export type ConversionBitrate = (typeof CONVERSION_BITRATES)[number];
export const SAMPLE_RATES = [
"auto",
"custom",
"48000",
"44100",
"32000",
"22050",
"16000",
"11025",
"8000",
] as const;
export type SampleRate = (typeof SAMPLE_RATES)[number];

View File

@ -1,4 +1,5 @@
import type { Categories } from "$lib/types"; import type { Categories } from "$lib/types";
import type { Converter } from "./converter.svelte";
import { FFmpegConverter } from "./ffmpeg.svelte"; import { FFmpegConverter } from "./ffmpeg.svelte";
import { PandocConverter } from "./pandoc.svelte"; import { PandocConverter } from "./pandoc.svelte";
import { VertdConverter } from "./vertd.svelte"; import { VertdConverter } from "./vertd.svelte";
@ -22,24 +23,40 @@ export function getConverterByFormat(format: string) {
export const categories: Categories = { export const categories: Categories = {
image: { formats: [""], canConvertTo: [] }, image: { formats: [""], canConvertTo: [] },
video: { formats: [""], canConvertTo: [] }, // add "audio" when "nullptr/experimental-audio-to-video" is implemented video: { formats: [""], canConvertTo: ["audio"] },
audio: { formats: [""], canConvertTo: [] }, // add "video" when "nullptr/experimental-audio-to-video" is implemented audio: { formats: [""], canConvertTo: ["video"] },
docs: { formats: [""], canConvertTo: [] }, doc: { formats: [""], canConvertTo: [] },
}; };
categories.audio.formats = categories.audio.formats =
converters converters
.find((c) => c.name === "ffmpeg") .find((c) => c.name === "ffmpeg")
?.formatStrings((f) => f.toSupported) || []; ?.supportedFormats.filter((f) => f.toSupported && f.isNative)
.map((f) => f.name) || [];
categories.video.formats = categories.video.formats =
converters converters
.find((c) => c.name === "vertd") .find((c) => c.name === "vertd")
?.formatStrings((f) => f.toSupported) || []; ?.supportedFormats.filter((f) => f.toSupported && f.isNative)
.map((f) => f.name) || [];
categories.image.formats = categories.image.formats =
converters converters
.find((c) => c.name === "imagemagick") .find((c) => c.name === "imagemagick")
?.formatStrings((f) => f.toSupported) || []; ?.formatStrings((f) => f.toSupported) || [];
categories.docs.formats = categories.doc.formats =
converters converters
.find((c) => c.name === "pandoc") .find((c) => c.name === "pandoc")
?.formatStrings((f) => f.toSupported) || []; ?.supportedFormats.filter((f) => f.toSupported && f.isNative)
.map((f) => f.name) || [];
export const byNative = (format: string) => {
return (a: Converter, b: Converter) => {
const aFormat = a.supportedFormats.find((f) => f.name === format);
const bFormat = b.supportedFormats.find((f) => f.name === format);
if (aFormat && bFormat) {
return aFormat.isNative ? -1 : 1;
}
return 0;
};
};

View File

@ -0,0 +1,141 @@
import { FormatInfo } from "./converter.svelte";
// formats added from maya's somewhat automated testing
// placed into this file to easily differentiate (and also clean up the main magick file)
// some formats also have a comment from what i saw during testing
export const imageFormats = [
// TODO: yaml, json, txt - not sure when these are used (just contains image info seemingly?), probably will remove cause its not very useful for 99.99% of people
new FormatInfo("a", false, true),
new FormatInfo("aai", true, true),
new FormatInfo("ai", false, true),
new FormatInfo("art", false, true),
new FormatInfo("avs", true, true),
new FormatInfo("b", false, true),
new FormatInfo("bgr", false, true),
new FormatInfo("bgra", false, true),
new FormatInfo("bgro", false, true),
new FormatInfo("bmp2", true, true),
new FormatInfo("bmp3", true, true),
new FormatInfo("brf", false, true),
new FormatInfo("cal", false, true),
new FormatInfo("cals", false, true),
new FormatInfo("cin", true, true), // not ideal (made the image more "shadowy"?)
new FormatInfo("cip", false, true),
new FormatInfo("cmyk", false, true),
new FormatInfo("cmyka", false, true),
new FormatInfo("dcx", true, true),
new FormatInfo("dds", true, true),
new FormatInfo("dpx", true, true),
new FormatInfo("dxt1", true, true),
new FormatInfo("dxt5", true, true),
new FormatInfo("epdf", false, true),
new FormatInfo("epi", false, true),
new FormatInfo("eps2", false, true),
new FormatInfo("eps3", false, true),
new FormatInfo("epsf", false, true),
new FormatInfo("epsi", false, true),
new FormatInfo("ept", false, true),
new FormatInfo("ept2", false, true),
new FormatInfo("ept3", false, true),
new FormatInfo("exr", true, true),
new FormatInfo("farbfeld", true, true),
new FormatInfo("fax", true, true), // not ideal (image became super long for some reason)
new FormatInfo("ff", true, true),
new FormatInfo("fit", true, true), // not ideal (grayscale)
new FormatInfo("fits", true, true), // not ideal (grayscale)
new FormatInfo("fl32", true, true),
new FormatInfo("fts", true, true), // not ideal (grayscale)
new FormatInfo("ftxt", false, true),
new FormatInfo("g", false, true),
new FormatInfo("g3", true, true), // not ideal (image became super long for some reason)
new FormatInfo("g4", false, true),
new FormatInfo("gif87", true, true),
new FormatInfo("gray", false, true),
new FormatInfo("graya", false, true),
new FormatInfo("group4", false, true),
new FormatInfo("hrz", true, true),
new FormatInfo("icb", true, true),
new FormatInfo("icon", true, true),
new FormatInfo("info", false, true),
new FormatInfo("ipl", true, true),
new FormatInfo("isobrl", false, true),
new FormatInfo("isobrl6", false, true),
new FormatInfo("j2c", true, true),
new FormatInfo("j2k", true, true),
new FormatInfo("jng", true, true),
new FormatInfo("jp2", true, true),
new FormatInfo("jpc", true, true),
new FormatInfo("jpm", true, true),
new FormatInfo("jps", true, true),
//new FormatInfo("json", false, true),
new FormatInfo("map", false, true),
new FormatInfo("miff", true, true),
new FormatInfo("mng", true, true),
new FormatInfo("mono", false, true),
new FormatInfo("mtv", true, true),
new FormatInfo("o", false, true),
new FormatInfo("otb", true, true), // not ideal (completely black and white - maybe format is like that)
new FormatInfo("pal", false, true),
new FormatInfo("palm", true, true), // not ideal (screwed up colours)
new FormatInfo("pam", true, true),
new FormatInfo("pcd", true, true), // not ideal (turned big, bg orange, and colour just shifted? - maybe format)
new FormatInfo("pcds", true, true), // not ideal (turned big, bg orange, and colour just shifted? - maybe format)
new FormatInfo("pcl", false, true),
new FormatInfo("pct", true, true),
new FormatInfo("pcx", true, true),
new FormatInfo("pdb", true, true), // not ideal (completely black and white - maybe format is like that)
// new FormatInfo("pdf", false, true),
// new FormatInfo("pdfa", false, true),
new FormatInfo("pgx", true, true), // not ideal (grayscale - maybe format is like that)
new FormatInfo("phm", true, true),
new FormatInfo("picon", true, true), // not ideal (smudged out colours - format probably)
new FormatInfo("pict", true, true),
new FormatInfo("pjpeg", true, true),
new FormatInfo("png00", true, true),
new FormatInfo("png24", true, true),
new FormatInfo("png32", true, true),
new FormatInfo("png48", true, true),
new FormatInfo("png64", true, true),
new FormatInfo("png8", true, true),
new FormatInfo("ps", false, true),
new FormatInfo("ps1", false, true),
new FormatInfo("ps2", false, true),
new FormatInfo("ps3", false, true),
new FormatInfo("psb", true, true),
new FormatInfo("ptif", true, true),
new FormatInfo("qoi", true, true),
new FormatInfo("r", false, true),
new FormatInfo("ras", true, true),
new FormatInfo("rgb", false, true),
new FormatInfo("rgba", false, true),
new FormatInfo("rgbo", false, true),
new FormatInfo("rgf", true, true), // not ideal (completely black and white - maybe format is like that)
new FormatInfo("sgi", true, true),
new FormatInfo("six", true, true),
new FormatInfo("sixel", true, true),
new FormatInfo("sparse-color", false, true),
new FormatInfo("strimg", false, true),
new FormatInfo("sun", true, true),
new FormatInfo("svgz", false, true),
new FormatInfo("tga", true, true),
new FormatInfo("tiff64", true, true),
//new FormatInfo("txt", true, true),
new FormatInfo("ubrl", false, true),
new FormatInfo("ubrl6", false, true),
new FormatInfo("uil", false, true),
new FormatInfo("uyvy", false, true),
new FormatInfo("vda", true, true),
new FormatInfo("vicar", true, true), // not ideal (grayscale - maybe format is like that)
new FormatInfo("viff", true, true),
new FormatInfo("vips", true, true),
new FormatInfo("vst", true, true),
new FormatInfo("wbmp", true, true), // not ideal (completely black and white - maybe format is like that)
new FormatInfo("wpg", true, true),
new FormatInfo("xbm", true, true), // not ideal (completely black and white - maybe format is like that)
new FormatInfo("xpm", true, true),
new FormatInfo("xv", true, true),
//new FormatInfo("yaml", false, true),
new FormatInfo("ycbcr", false, true),
new FormatInfo("ycbcra", false, true),
new FormatInfo("yuv", false, true),
];

View File

@ -1,10 +1,13 @@
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import { error, log } from "$lib/logger"; import { error, log } from "$lib/logger";
import { m } from "$lib/paraglide/messages";
import { addToast } from "$lib/store/ToastProvider"; import { addToast } from "$lib/store/ToastProvider";
import type { OmitBetterStrict, WorkerMessage } from "$lib/types"; import type { OmitBetterStrict, WorkerMessage } from "$lib/types";
import { VertFile } from "$lib/types"; import { VertFile } from "$lib/types";
import MagickWorker from "$lib/workers/magick?worker&url"; import MagickWorker from "$lib/workers/magick?worker&url";
import { Converter, FormatInfo } from "./converter.svelte"; import { Converter, FormatInfo } from "./converter.svelte";
import { imageFormats } from "./magick-automated";
import { Settings } from "$lib/sections/settings/index.svelte";
export class MagickConverter extends Converter { export class MagickConverter extends Converter {
private worker: Worker = browser private worker: Worker = browser
@ -17,33 +20,64 @@ export class MagickConverter extends Converter {
public ready = $state(false); public ready = $state(false);
public supportedFormats = [ public supportedFormats = [
// manually tested formats
new FormatInfo("png", true, true), new FormatInfo("png", true, true),
new FormatInfo("jpeg", true, true), new FormatInfo("jpeg", true, true),
new FormatInfo("jpg", true, true), new FormatInfo("jpg", true, true),
new FormatInfo("webp", true, true), new FormatInfo("webp", true, true),
new FormatInfo("gif", true, true), new FormatInfo("gif", true, true),
new FormatInfo("heic", true, false), new FormatInfo("svg", true, true),
new FormatInfo("jxl", true, true),
new FormatInfo("avif", true, true),
new FormatInfo("heic", true, false), // seems to be unreliable? HEIC/HEIF is very weird if it will actually work
new FormatInfo("heif", true, false),
// TODO: .ico files can encode multiple images at various // TODO: .ico files can encode multiple images at various
// sizes, bitdepths, etc. we should support that in future // sizes, bitdepths, etc. we should support that in future
new FormatInfo("ico", true, true), new FormatInfo("ico", true, true),
new FormatInfo("bmp", true, false), new FormatInfo("bmp", true, true),
new FormatInfo("cur", true, false), new FormatInfo("cur", true, true),
new FormatInfo("ani", true, false), new FormatInfo("ani", true, false),
new FormatInfo("icns", true, false), new FormatInfo("icns", true, false),
new FormatInfo("nef", true, false), new FormatInfo("nef", true, false),
new FormatInfo("cr2", true, false), new FormatInfo("cr2", true, false),
new FormatInfo("hdr", true, true), new FormatInfo("hdr", true, true),
new FormatInfo("jpe", true, true), new FormatInfo("jpe", true, true),
new FormatInfo("dng", true, false),
new FormatInfo("mat", true, true), new FormatInfo("mat", true, true),
new FormatInfo("pbm", true, true), new FormatInfo("pbm", true, true),
new FormatInfo("pfm", true, true), new FormatInfo("pfm", true, true),
new FormatInfo("pgm", true, true), new FormatInfo("pgm", true, true),
new FormatInfo("pnm", true, true), new FormatInfo("pnm", true, true),
new FormatInfo("ppm", false, true), new FormatInfo("ppm", true, true),
new FormatInfo("tif", true, true),
new FormatInfo("tiff", true, true), new FormatInfo("tiff", true, true),
new FormatInfo("jfif", true, true), new FormatInfo("jfif", true, true),
new FormatInfo("eps", false, true),
new FormatInfo("psd", true, true),
// raw camera formats
new FormatInfo("arw", true, false),
new FormatInfo("tif", true, true),
new FormatInfo("dng", true, false),
new FormatInfo("xcf", true, false),
new FormatInfo("rw2", true, false),
new FormatInfo("raf", true, false),
new FormatInfo("orf", true, false),
new FormatInfo("pef", true, false),
new FormatInfo("mos", true, false),
new FormatInfo("raw", true, false),
new FormatInfo("dcr", true, false),
new FormatInfo("crw", true, false),
new FormatInfo("cr3", true, false),
new FormatInfo("3fr", true, false),
new FormatInfo("erf", true, false),
new FormatInfo("mrw", true, false),
new FormatInfo("mef", true, false),
new FormatInfo("nrw", true, false),
new FormatInfo("srw", true, false),
new FormatInfo("sr2", true, false),
new FormatInfo("srf", true, false),
// formats added from maya's somewhat automated testing
...imageFormats,
]; ];
public readonly reportsProgress = false; public readonly reportsProgress = false;
@ -63,10 +97,7 @@ export class MagickConverter extends Converter {
["converters", this.name], ["converters", this.name],
`error in worker: ${message.error}`, `error in worker: ${message.error}`,
); );
addToast( addToast("error", m["workers.errors.magick"]());
"error",
`Error in Magick worker, image conversion may not work as expected.`,
);
throw new Error(message.error); throw new Error(message.error);
} }
}; };
@ -78,8 +109,33 @@ export class MagickConverter extends Converter {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
...args: any[] ...args: any[]
): Promise<VertFile> { ): Promise<VertFile> {
const compression: number | undefined = args.at(0); let compression: number | undefined = args.at(0);
if (!compression) {
compression = Settings.instance.settings.magickQuality ?? 100;
log(["converters", this.name], `using user setting for quality: ${compression}%`);
}
log(["converters", this.name], `converting ${input.name} to ${to}`); log(["converters", this.name], `converting ${input.name} to ${to}`);
// handle converting from SVG manually because magick-wasm doesn't support it
if (input.from === ".svg") {
try {
const blob = await this.svgToImage(input);
const pngFile = new VertFile(
new File([blob], input.name.replace(/\.svg$/i, ".png")),
input.to,
);
if (to === ".png") return pngFile; // if target is png, return it directly
return await this.convert(pngFile, to, ...args); // otherwise, recursively convert png to user's target format
} catch (err) {
error(
["converters", this.name],
`SVG conversion failed: ${err}`,
);
throw err;
}
}
// every other format handled by magick worker
const msg = { const msg = {
type: "convert", type: "convert",
input: { input: {
@ -138,4 +194,67 @@ export class MagickConverter extends Converter {
} }
}); });
} }
private async svgToImage(input: VertFile): Promise<Blob> {
log(["converters", this.name], `converting SVG to image (PNG)`);
const svgText = await input.file.text();
const svgBlob = new Blob([svgText], { type: "image/svg+xml" });
const svgUrl = URL.createObjectURL(svgBlob);
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Failed to get canvas context");
const img = new Image();
// try to extract dimensions from SVG, and if not fallback to default
let width = 512;
let height = 512;
const widthMatch = svgText.match(/width=["'](\d+)["']/);
const heightMatch = svgText.match(/height=["'](\d+)["']/);
const viewBoxMatch = svgText.match(
/viewBox=["'][^"']*\s+(\d+)\s+(\d+)["']/,
);
if (widthMatch && heightMatch) {
width = parseInt(widthMatch[1]);
height = parseInt(heightMatch[1]);
} else if (viewBoxMatch) {
width = parseInt(viewBoxMatch[1]);
height = parseInt(viewBoxMatch[2]);
}
return new Promise((resolve, reject) => {
img.onload = () => {
try {
canvas.width = img.naturalWidth || width;
canvas.height = img.naturalHeight || height;
ctx.drawImage(img, 0, 0);
canvas.toBlob((blob) => {
URL.revokeObjectURL(svgUrl);
if (blob) {
resolve(blob);
} else {
reject(
new Error("Failed to convert canvas to Blob"),
);
}
}, "image/png");
} catch (err) {
URL.revokeObjectURL(svgUrl);
reject(err);
}
};
img.onerror = () => {
URL.revokeObjectURL(svgUrl);
reject(new Error("Failed to load SVG image"));
};
img.src = svgUrl;
});
}
} }

View File

@ -212,6 +212,21 @@ export class VertdConverter extends Converter {
new FormatInfo("mts", true, true), new FormatInfo("mts", true, true),
new FormatInfo("ts", true, true), new FormatInfo("ts", true, true),
new FormatInfo("m2ts", true, true), new FormatInfo("m2ts", true, true),
new FormatInfo("mpg", true, true),
new FormatInfo("mpeg", true, true),
new FormatInfo("flv", true, true),
new FormatInfo("f4v", true, true),
new FormatInfo("vob", true, true),
new FormatInfo("m4v", true, true),
new FormatInfo("3gp", true, true),
new FormatInfo("3g2", true, true),
new FormatInfo("mxf", true, true),
new FormatInfo("ogv", true, true),
new FormatInfo("rm", true, false),
new FormatInfo("rmvb", true, false),
new FormatInfo("h264", true, true),
new FormatInfo("divx", true, true),
new FormatInfo("swf", true, true),
]; ];
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@ -374,6 +374,17 @@ body {
@apply w-full p-3 rounded-lg bg-panel border-2 border-button pl-3 pr-[4rem]; @apply w-full p-3 rounded-lg bg-panel border-2 border-button pl-3 pr-[4rem];
} }
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
appearance: textfield;
}
input[type="text"]::placeholder { input[type="text"]::placeholder {
@apply text-muted font-normal; @apply text-muted font-normal;
} }

View File

@ -2,6 +2,8 @@
import Panel from "$lib/components/visual/Panel.svelte"; import Panel from "$lib/components/visual/Panel.svelte";
import { HeartHandshakeIcon } from "lucide-svelte"; import { HeartHandshakeIcon } from "lucide-svelte";
import { GITHUB_URL_VERT } from "$lib/consts"; import { GITHUB_URL_VERT } from "$lib/consts";
import { m } from "$lib/paraglide/messages";
import { link } from "$lib/store/index.svelte";
let { mainContribs, notableContribs, ghContribs } = $props(); let { mainContribs, notableContribs, ghContribs } = $props();
</script> </script>
@ -51,12 +53,11 @@
<div class="rounded-full bg-blue-300 p-2 inline-block mr-3 w-10 h-10"> <div class="rounded-full bg-blue-300 p-2 inline-block mr-3 w-10 h-10">
<HeartHandshakeIcon color="black" /> <HeartHandshakeIcon color="black" />
</div> </div>
Credits {m["about.credits.title"]()}
</h2> </h2>
<p class="-mt-4 -mb-3 font-black text-lg"> <p class="-mt-4 -mb-3 font-black text-lg">
If you would like to contact the development team, please use the email {m["about.credits.contact_team"]()}
found on the "Resources" card.
</p> </p>
<!-- Main contributors --> <!-- Main contributors -->
@ -72,11 +73,12 @@
<!-- Notable contributors --> <!-- Notable contributors -->
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<h2 class="text-base font-bold">Notable contributors</h2> <h2 class="text-base font-bold">
{m["about.credits.notable_contributors"]()}
</h2>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p class="text-base text-muted font-normal"> <p class="text-base text-muted font-normal">
We'd like to thank these people for their major {m["about.credits.notable_description"]()}
contributions to VERT.
</p> </p>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
{#each notableContribs as contrib} {#each notableContribs as contrib}
@ -90,34 +92,26 @@
<!-- GitHub contributors --> <!-- GitHub contributors -->
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<h2 class="text-base font-bold">GitHub contributors</h2> <h2 class="text-base font-bold">
{m["about.credits.github_contributors"]()}
</h2>
{#if ghContribs && ghContribs.length > 0} {#if ghContribs && ghContribs.length > 0}
<p class="text-base text-muted font-normal"> <p class="text-base text-muted font-normal">
Big <a {@html link(
class="text-black dynadark:text-white" ["jpegify_link", "github_link"],
href="/jpegify">thanks</a m["about.credits.github_description"](),
> ["/jpegify", GITHUB_URL_VERT],
to all these people for helping out! [false, true],
<a ["text-black dynadark:text-white", "text-blue-500 font-normal hover:underline"]
class="text-blue-500 font-normal hover:underline" )}
href={GITHUB_URL_VERT}
target="_blank"
rel="noopener noreferrer"
>
Want to help too?
</a>
</p> </p>
{:else} {:else}
<p class="text-base text-muted font-normal italic"> <p class="text-base text-muted font-normal italic">
Seems like no one has contributed yet... {@html link(
<a "contribute_link",
class="text-blue-500 font-normal hover:underline" m["about.credits.no_contributors"](),
href={GITHUB_URL_VERT} GITHUB_URL_VERT,
target="_blank" )}
rel="noopener noreferrer"
>
be the first to contribute!
</a>
</p> </p>
{/if} {/if}
</div> </div>
@ -131,12 +125,9 @@
</div> </div>
{/if} {/if}
<h2 class="mt-2 -mb-2">Libraries</h2> <h2 class="mt-2 -mb-2">{m["about.credits.libraries"]()}</h2>
<p class="font-normal"> <p class="font-normal">
A big thanks to FFmpeg (audio, video), Imagemagick (images) and {m["about.credits.libraries_description"]()}
Pandoc (documents) for maintaining such excellent libraries for
so many years. VERT relies on them to provide you with your
conversions.
</p> </p>
</div> </div>
</div></Panel </div></Panel

View File

@ -23,11 +23,8 @@
import Panel from "$lib/components/visual/Panel.svelte"; import Panel from "$lib/components/visual/Panel.svelte";
import { effects } from "$lib/store/index.svelte"; import { effects } from "$lib/store/index.svelte";
import { addToast } from "$lib/store/ToastProvider"; import { addToast } from "$lib/store/ToastProvider";
import { import { loadStripe } from "@stripe/stripe-js/pure";
loadStripe, import { type Stripe, type StripeElements } from "@stripe/stripe-js";
type Stripe,
type StripeElements,
} from "@stripe/stripe-js";
import clsx from "clsx"; import clsx from "clsx";
import { import {
CalendarHeartIcon, CalendarHeartIcon,
@ -35,9 +32,10 @@
HeartIcon, HeartIcon,
WalletIcon, WalletIcon,
} from "lucide-svelte"; } from "lucide-svelte";
import { onMount, tick } from "svelte"; import { onMount } from "svelte";
import { Elements, PaymentElement } from "svelte-stripe"; import { Elements, PaymentElement } from "svelte-stripe";
import { quintOut } from "svelte/easing"; import { quintOut } from "svelte/easing";
import { m } from "$lib/paraglide/messages";
let amount = $state(1); let amount = $state(1);
let customAmount = $state(""); let customAmount = $state("");
@ -66,10 +64,7 @@
if (!res.ok) { if (!res.ok) {
paymentState = "prepay"; paymentState = "prepay";
addToast( addToast("error", m["about.donate.payment_error"]());
"error",
"Error fetching payment details. Please try again later.",
);
return; return;
} }
@ -87,10 +82,6 @@
const payDuration = 400; const payDuration = 400;
const transition = "cubic-bezier(0.23, 1, 0.320, 1)"; const transition = "cubic-bezier(0.23, 1, 0.320, 1)";
onMount(async () => {
stripe = await loadStripe(PUB_STRIPE_KEY);
});
const donate = async () => { const donate = async () => {
if (!stripe || !clientSecret || !elements) return; if (!stripe || !clientSecret || !elements) return;
@ -98,9 +89,13 @@
const submitResult = await elements.submit(); const submitResult = await elements.submit();
if (submitResult.error) { if (submitResult.error) {
const period = submitResult.error.message?.endsWith(".") ? "" : ".";
addToast( addToast(
"error", "error",
`Payment failed: ${submitResult.error.message}${submitResult.error.message?.endsWith(".") ? "" : "."} You have not been charged.`, m["about.donate.payment_failed"]({
message: submitResult.error.message || "",
period,
}),
); );
enablePay = true; enablePay = true;
return; return;
@ -116,12 +111,16 @@
}); });
if (res.error) { if (res.error) {
const period = res.error.message?.endsWith(".") ? "" : ".";
addToast( addToast(
"error", "error",
`Payment failed: ${res.error.message}${res.error.message?.endsWith(".") ? "" : "."} You have not been charged.`, m["about.donate.payment_failed"]({
message: res.error.message || "",
period,
}),
); );
} else { } else {
addToast("success", "Thank you for your donation!"); addToast("success", m["about.donate.thank_you"]());
} }
paymentState = "prepay"; paymentState = "prepay";
@ -140,13 +139,10 @@
if (status) { if (status) {
switch (status) { switch (status) {
case "succeeded": case "succeeded":
addToast("success", "Thank you for your donation!"); addToast("success", m["about.donate.thank_you"]());
break; break;
default: default:
addToast( addToast("error", m["about.donate.donation_error"]());
"error",
"An error occurred while processing your donation. Please try again later.",
);
} }
goto("/about"); goto("/about");
@ -162,10 +158,10 @@
> >
<HeartIcon color="black" /> <HeartIcon color="black" />
</div> </div>
Donate to VERT {m["about.donate.title"]()}
</h2> </h2>
<p class="text-base font-normal"> <p class="text-base font-normal">
With your support, we can keep maintaining and improving VERT. {m["about.donate.description"]()}
</p> </p>
</div> </div>
@ -192,7 +188,7 @@
)} )}
> >
<HandCoinsIcon size="24" class="inline-block mr-2" /> <HandCoinsIcon size="24" class="inline-block mr-2" />
One-time {m["about.donate.one_time"]()}
</button> </button>
<button <button
@ -207,7 +203,7 @@
)} )}
> >
<CalendarHeartIcon size="24" class="inline-block mr-2" /> <CalendarHeartIcon size="24" class="inline-block mr-2" />
Monthly {m["about.donate.monthly"]()}
</button> </button>
</div> </div>
<div class="grid grid-cols-4 gap-3 w-full"> <div class="grid grid-cols-4 gap-3 w-full">
@ -229,9 +225,10 @@
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<FancyInput <FancyInput
bind:value={customAmount} bind:value={customAmount}
placeholder="Custom" placeholder={m["about.donate.custom"]()}
prefix="$" prefix="$"
type="number" type="number"
class="h-full"
/> />
</div> </div>
</div> </div>
@ -288,7 +285,9 @@
class="btn w-full h-12 bg-accent-red text-black rounded-full mt-4" class="btn w-full h-12 bg-accent-red text-black rounded-full mt-4"
onclick={donate} onclick={donate}
> >
Donate ${amount.toFixed(2)} USD {m["about.donate.donate_amount"]({
amount: amount.toFixed(2),
})}
</button> </button>
</div> </div>
</div> </div>
@ -304,7 +303,7 @@
class="row-start-1 col-start-1 flex justify-center items-center" class="row-start-1 col-start-1 flex justify-center items-center"
> >
<WalletIcon size="24" class="inline-block mr-2" /> <WalletIcon size="24" class="inline-block mr-2" />
Pay now {m["about.donate.pay_now"]()}
</div> </div>
{/if} {/if}
</div> </div>

View File

@ -8,6 +8,7 @@
MailIcon, MailIcon,
MessageCircleMoreIcon, MessageCircleMoreIcon,
} from "lucide-svelte"; } from "lucide-svelte";
import { m } from "$lib/paraglide/messages";
</script> </script>
<Panel class="flex flex-col gap-4 p-6"> <Panel class="flex flex-col gap-4 p-6">
@ -17,7 +18,7 @@
> >
<LinkIcon color="black" /> <LinkIcon color="black" />
</div> </div>
Resources {m["about.resources.title"]()}
</h2> </h2>
<div class="flex gap-3"> <div class="flex gap-3">
<a <a
@ -29,7 +30,7 @@
: '!scale-100'} flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center" : '!scale-100'} flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center"
> >
<MessageCircleMoreIcon size="24" class="inline-block mr-2" /> <MessageCircleMoreIcon size="24" class="inline-block mr-2" />
Discord {m["about.resources.discord"]()}
</a> </a>
<a <a
href={GITHUB_URL_VERT} href={GITHUB_URL_VERT}
@ -40,7 +41,7 @@
: '!scale-100'} flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center" : '!scale-100'} flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center"
> >
<GithubIcon size="24" class="inline-block mr-2" /> <GithubIcon size="24" class="inline-block mr-2" />
Source {m["about.resources.source"]()}
</a> </a>
<a <a
href="mailto:{CONTACT_EMAIL}" href="mailto:{CONTACT_EMAIL}"
@ -51,7 +52,7 @@
: '!scale-100'} flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center" : '!scale-100'} flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center"
> >
<MailIcon size="24" class="inline-block mr-2" /> <MailIcon size="24" class="inline-block mr-2" />
Email {m["about.resources.email"]()}
</a> </a>
</div> </div>
</Panel> </Panel>

View File

@ -5,6 +5,8 @@
import { DISCORD_URL } from "$lib/consts"; import { DISCORD_URL } from "$lib/consts";
import { error } from "$lib/logger"; import { error } from "$lib/logger";
import { addToast } from "$lib/store/ToastProvider"; import { addToast } from "$lib/store/ToastProvider";
import { m } from "$lib/paraglide/messages";
import { link } from "$lib/store/index.svelte";
let copied = false; let copied = false;
let timeoutId: number | undefined; let timeoutId: number | undefined;
@ -13,7 +15,7 @@
try { try {
navigator.clipboard.writeText("hello@vert.sh"); navigator.clipboard.writeText("hello@vert.sh");
copied = true; copied = true;
addToast("success", "Email copied to clipboard!"); addToast("success", m["about.sponsors.email_copied"]());
if (timeoutId) clearTimeout(timeoutId); if (timeoutId) clearTimeout(timeoutId);
timeoutId = setTimeout(() => (copied = false), 2000); timeoutId = setTimeout(() => (copied = false), 2000);
@ -30,7 +32,7 @@
> >
<PiggyBankIcon color="black" /> <PiggyBankIcon color="black" />
</div> </div>
Sponsors {m["about.sponsors.title"]()}
</h2> </h2>
<div class="mt-2 [&>*]:font-normal h-full flex justify-between flex-col"> <div class="mt-2 [&>*]:font-normal h-full flex justify-between flex-col">
<div class="flex gap-3 justify-center text-lg"> <div class="flex gap-3 justify-center text-lg">
@ -43,11 +45,11 @@
</a> </a>
</div> </div>
<p class="text-muted"> <p class="text-muted">
Want to support us? Contact a developer in the <a {@html link(
href={DISCORD_URL} "discord_link",
target="_blank">Discord</a m["about.sponsors.description"](),
> DISCORD_URL,
server, or send an email to )}
<span class="inline-block mx-[2px] relative top-[2px]"> <span class="inline-block mx-[2px] relative top-[2px]">
<button <button
id="email" id="email"

View File

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import Panel from "$lib/components/visual/Panel.svelte"; import Panel from "$lib/components/visual/Panel.svelte";
import { MessageCircleQuestionIcon } from "lucide-svelte"; import { MessageCircleQuestionIcon } from "lucide-svelte";
import { m } from "$lib/paraglide/messages";
</script> </script>
<Panel class="flex flex-col gap-3 p-6"> <Panel class="flex flex-col gap-3 p-6">
@ -10,22 +11,9 @@
> >
<MessageCircleQuestionIcon color="black" /> <MessageCircleQuestionIcon color="black" />
</div> </div>
Why VERT? {m["about.why.title"]()}
</h2> </h2>
<p class="text-lg font-normal"> <p class="text-lg font-normal">
<b>File converters have always disappointed us.</b> They're ugly, {@html m["about.why.description"]()}
riddled with ads, and most importantly; slow. We decided to solve this
problem once and for all by making an alternative that solves all those
problems, and more.<br />
<br />
All non-video files are converted completely on-device; this means that there's
no delay between sending and receiving the files from a server, and we never
get to snoop on the files you convert.
<br />
<br />
Video files get uploaded to our lightning-fast RTX 4000 Ada server. Your
videos stay on there for an hour if you do not convert them. If you do convert
the file, the video will stay on the server for an hour, or until it is downloaded.
The file will then be deleted from our server.
</p> </p>
</Panel> </Panel>

View File

@ -5,6 +5,8 @@
effects, effects,
setEffects, setEffects,
setTheme, setTheme,
updateLocale,
availableLocales,
} from "$lib/store/index.svelte"; } from "$lib/store/index.svelte";
import { import {
MoonIcon, MoonIcon,
@ -14,6 +16,23 @@
SunIcon, SunIcon,
} from "lucide-svelte"; } from "lucide-svelte";
import { onMount, onDestroy } from "svelte"; import { onMount, onDestroy } from "svelte";
import { m } from "$lib/paraglide/messages";
import { getLocale } from "$lib/paraglide/runtime";
import Dropdown from "$lib/components/functional/Dropdown.svelte";
let currentLocale = $state("en");
const getLanguageDisplayName = (locale: string) => {
try {
return availableLocales[locale as keyof typeof availableLocales];
} catch {
return locale.toUpperCase();
}
};
const languageOptions = Object.keys(availableLocales).map((locale) =>
getLanguageDisplayName(locale),
);
let lightElement: HTMLButtonElement; let lightElement: HTMLButtonElement;
let darkElement: HTMLButtonElement; let darkElement: HTMLButtonElement;
@ -49,6 +68,8 @@
onMount(() => { onMount(() => {
effectsUnsubscribe = effects.subscribe(updateEffectsClasses); effectsUnsubscribe = effects.subscribe(updateEffectsClasses);
themeUnsubscribe = theme.subscribe(updateThemeClasses); themeUnsubscribe = theme.subscribe(updateThemeClasses);
currentLocale = localStorage.getItem("locale") || getLocale();
}); });
onDestroy(() => { onDestroy(() => {
@ -57,9 +78,20 @@
}); });
$effect(() => { $effect(() => {
updateEffectsClasses($effects); updateEffectsClasses($effects);
updateThemeClasses($theme); updateThemeClasses($theme);
}); });
function handleLanguageChange(selectedLanguage: string) {
const selectedLocale = Object.keys(availableLocales).find(
(locale) => getLanguageDisplayName(locale) === selectedLanguage,
);
if (selectedLocale && selectedLocale !== currentLocale) {
currentLocale = selectedLocale;
updateLocale(selectedLocale);
}
}
</script> </script>
<Panel class="flex flex-col gap-8 p-6"> <Panel class="flex flex-col gap-8 p-6">
@ -70,14 +102,16 @@
class="inline-block -mt-1 mr-2 bg-accent-purple p-2 rounded-full" class="inline-block -mt-1 mr-2 bg-accent-purple p-2 rounded-full"
color="black" color="black"
/> />
Appearance {m["settings.appearance.title"]()}
</h2> </h2>
<div class="flex flex-col gap-8"> <div class="flex flex-col gap-8">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p class="text-base font-bold">Brightness theme</p> <p class="text-base font-bold">
{m["settings.appearance.brightness_theme"]()}
</p>
<p class="text-sm text-muted font-normal italic"> <p class="text-sm text-muted font-normal italic">
Want a sunny flash-bang, or a quiet lonely night? {m["settings.appearance.brightness_description"]()}
</p> </p>
</div> </div>
<div class="flex flex-col gap-3 w-full"> <div class="flex flex-col gap-3 w-full">
@ -85,29 +119,34 @@
<button <button
bind:this={lightElement} bind:this={lightElement}
onclick={() => setTheme("light")} onclick={() => setTheme("light")}
class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" class="btn {$effects
? ''
: '!scale-100'} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center"
> >
<SunIcon size="24" class="inline-block mr-2" /> <SunIcon size="24" class="inline-block mr-2" />
Light {m["settings.appearance.light"]()}
</button> </button>
<button <button
bind:this={darkElement} bind:this={darkElement}
onclick={() => setTheme("dark")} onclick={() => setTheme("dark")}
class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg text-black flex items-center justify-center" class="btn {$effects
? ''
: '!scale-100'} flex-1 p-4 rounded-lg text-black flex items-center justify-center"
> >
<MoonIcon size="24" class="inline-block mr-2" /> <MoonIcon size="24" class="inline-block mr-2" />
Dark {m["settings.appearance.dark"]()}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p class="text-base font-bold">Effect settings</p> <p class="text-base font-bold">
{m["settings.appearance.effect_settings"]()}
</p>
<p class="text-sm text-muted font-normal italic"> <p class="text-sm text-muted font-normal italic">
Would you like fancy effects, or a more static {m["settings.appearance.effect_description"]()}
experience?
</p> </p>
</div> </div>
<div class="flex flex-col gap-3 w-full"> <div class="flex flex-col gap-3 w-full">
@ -115,23 +154,45 @@
<button <button
bind:this={enableEffectsElement} bind:this={enableEffectsElement}
onclick={() => setEffects(true)} onclick={() => setEffects(true)}
class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" class="btn {$effects
? ''
: '!scale-100'} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center"
> >
<PlayIcon size="24" class="inline-block mr-2" /> <PlayIcon size="24" class="inline-block mr-2" />
Enable {m["settings.appearance.enable"]()}
</button> </button>
<button <button
bind:this={disableEffectsElement} bind:this={disableEffectsElement}
onclick={() => setEffects(false)} onclick={() => setEffects(false)}
class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" class="btn {$effects
? ''
: '!scale-100'} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center"
> >
<PauseIcon size="24" class="inline-block mr-2" /> <PauseIcon size="24" class="inline-block mr-2" />
Disable {m["settings.appearance.disable"]()}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<p class="text-base font-bold">
{m["settings.language.title"]()}
</p>
<p class="text-sm text-muted font-normal italic">
{m["settings.language.description"]()}
</p>
</div>
<div class="flex flex-col gap-3 w-full">
<Dropdown
options={languageOptions}
settingsStyle
selected={getLanguageDisplayName(currentLocale)}
onselect={handleLanguageChange}
/>
</div>
</div>
</div> </div>
</div> </div>
</Panel> </Panel>

View File

@ -3,6 +3,15 @@
import Panel from "$lib/components/visual/Panel.svelte"; import Panel from "$lib/components/visual/Panel.svelte";
import { RefreshCwIcon } from "lucide-svelte"; import { RefreshCwIcon } from "lucide-svelte";
import type { ISettings } from "./index.svelte"; import type { ISettings } from "./index.svelte";
import {
CONVERSION_BITRATES,
type ConversionBitrate,
SAMPLE_RATES,
type SampleRate,
} from "$lib/converters/ffmpeg.svelte";
import { m } from "$lib/paraglide/messages";
import Dropdown from "$lib/components/functional/Dropdown.svelte";
import FancyInput from "$lib/components/functional/FancyInput.svelte";
const { settings }: { settings: ISettings } = $props(); const { settings }: { settings: ISettings } = $props();
</script> </script>
@ -15,34 +24,96 @@
class="inline-block -mt-1 mr-2 bg-accent p-2 rounded-full" class="inline-block -mt-1 mr-2 bg-accent p-2 rounded-full"
color="black" color="black"
/> />
Conversion {m["settings.conversion.title"]()}
</h2> </h2>
<div class="flex flex-col gap-8"> <div class="flex flex-col gap-8">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p class="text-base font-bold">File name format</p> <p class="text-base font-bold">
{m["settings.conversion.filename_format"]()}
</p>
<p class="text-sm text-muted font-normal"> <p class="text-sm text-muted font-normal">
This will determine the name of the file on download, <span {@html m["settings.conversion.filename_description"]()}
class="font-bold italic"
>not including the file extension.</span
>
You can put these following templates in the format, which
will be replaced with the relevant information:
<span class="font-bold">%name%</span>
for the original file name,
<span class="font-bold">%extension%</span>
for the original file extension, and
<span class="font-bold">%date%</span>
for a date string of when the file was converted.
</p> </p>
</div> </div>
<FancyTextInput <FancyTextInput
placeholder="VERT_%name%" placeholder="VERT_%name%"
bind:value={settings.filenameFormat} bind:value={settings.filenameFormat}
extension=".ext" extension={".ext"}
type="text" type="text"
/> />
</div> </div>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<p class="text-base font-bold">
{m["settings.conversion.quality"]()}
</p>
<p class="text-sm text-muted font-normal">
{m["settings.conversion.quality_description"]()}
</p>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="flex flex-col gap-2">
<p class="text-sm font-bold">
{m["settings.conversion.quality_images"]()}
</p>
<FancyInput
bind:value={
settings.magickQuality as unknown as string
}
type="number"
min={1}
max={100}
placeholder={"100"}
extension={"%"}
/>
</div>
<div class="flex flex-col gap-2">
<p class="text-sm font-bold">
{m["settings.conversion.quality_audio"]()}
</p>
<Dropdown
options={CONVERSION_BITRATES.map((b) =>
b.toString(),
)}
selected={settings.ffmpegQuality.toString()}
onselect={(option: string) =>
(settings.ffmpegQuality =
option as ConversionBitrate)}
settingsStyle
/>
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="flex flex-col gap-2">
<p class="text-sm font-bold">
{m["settings.conversion.rate"]()}
</p>
<Dropdown
options={SAMPLE_RATES.map((r) => r.toString())}
selected={settings.ffmpegSampleRate.toString()}
onselect={(option: string) => {
settings.ffmpegSampleRate =
option as SampleRate;
}}
settingsStyle
/>
</div>
<div class="flex flex-col gap-2">
<p class="text-sm font-bold select-none">&nbsp;&nbsp;</p>
<FancyInput
bind:value={
settings.ffmpegCustomSampleRate as unknown as string
}
type="number"
min={1}
placeholder={"44100"}
extension={"Hz"}
disabled={settings.ffmpegSampleRate !== "custom"}
/>
</div>
</div>
</div>
</div> </div>
</div> </div></Panel
</Panel> >

View File

@ -3,6 +3,8 @@
import { ChartColumnIcon, PauseIcon, PlayIcon } from "lucide-svelte"; import { ChartColumnIcon, PauseIcon, PlayIcon } from "lucide-svelte";
import type { ISettings } from "./index.svelte"; import type { ISettings } from "./index.svelte";
import { effects } from "$lib/store/index.svelte"; import { effects } from "$lib/store/index.svelte";
import { m } from "$lib/paraglide/messages";
import { link } from "$lib/store/index.svelte";
const { settings }: { settings: ISettings } = $props(); const { settings }: { settings: ISettings } = $props();
</script> </script>
@ -15,26 +17,23 @@
class="inline-block -mt-1 mr-2 bg-accent-blue p-2 rounded-full" class="inline-block -mt-1 mr-2 bg-accent-blue p-2 rounded-full"
color="black" color="black"
/> />
Privacy {m["settings.privacy.title"]()}
</h2> </h2>
<div class="flex flex-col gap-8"> <div class="flex flex-col gap-8">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p class="text-base font-bold">Plausible analytics</p> <p class="text-base font-bold">
{m["settings.privacy.plausible_title"]()}
</p>
<p class="text-sm text-muted font-normal"> <p class="text-sm text-muted font-normal">
We use <a {@html link(
href="https://plausible.io/privacy-focused-web-analytics" ["plausible_link", "analytics_link"],
target="_blank" m["settings.privacy.plausible_description"](),
rel="noopener noreferrer">Plausible</a [
>, a privacy-focused analytics tool, to gather "https://plausible.io/privacy-focused-web-analytics",
completely anonymous statistics. All data is anonymized "https://ats.vert.sh/vert.sh",
and aggregated, and no identifiable information is ever ],
sent or stored. You can view the analytics )}
<a
href="https://ats.vert.sh/vert.sh"
target="_blank"
rel="noopener noreferrer">here</a
> and choose to opt out below.
</p> </p>
</div> </div>
<div class="flex flex-col gap-3 w-full"> <div class="flex flex-col gap-3 w-full">
@ -48,7 +47,7 @@
: ''} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" : ''} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center"
> >
<PlayIcon size="24" class="inline-block mr-2" /> <PlayIcon size="24" class="inline-block mr-2" />
Opt-in {m["settings.privacy.opt_in"]()}
</button> </button>
<button <button
@ -60,7 +59,7 @@
: 'selected'} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" : 'selected'} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center"
> >
<PauseIcon size="24" class="inline-block mr-2" /> <PauseIcon size="24" class="inline-block mr-2" />
Opt-out {m["settings.privacy.opt_out"]()}
</button> </button>
</div> </div>
</div> </div>

View File

@ -6,6 +6,8 @@
import clsx from "clsx"; import clsx from "clsx";
import Dropdown from "$lib/components/functional/Dropdown.svelte"; import Dropdown from "$lib/components/functional/Dropdown.svelte";
import { vertdLoaded } from "$lib/store/index.svelte"; import { vertdLoaded } from "$lib/store/index.svelte";
import { m } from "$lib/paraglide/messages";
import { link } from "$lib/store/index.svelte";
let vertdCommit = $state<string | null>(null); let vertdCommit = $state<string | null>(null);
let abortController: AbortController | null = null; let abortController: AbortController | null = null;
@ -55,7 +57,7 @@
class="inline-block -mt-1 mr-2 bg-accent-red p-2 rounded-full overflow-visible" class="inline-block -mt-1 mr-2 bg-accent-red p-2 rounded-full overflow-visible"
color="black" color="black"
/> />
Video conversion {m["settings.vertd.title"]()}
</h2> </h2>
<p <p
class={clsx("text-sm font-normal", { class={clsx("text-sm font-normal", {
@ -64,90 +66,79 @@
"!text-muted": vertdCommit === "loading", "!text-muted": vertdCommit === "loading",
})} })}
> >
status: {vertdCommit {m["settings.vertd.status"]()} {vertdCommit
? vertdCommit === "loading" ? vertdCommit === "loading"
? "loading..." ? m["settings.vertd.loading"]()
: `available, commit id ${vertdCommit}` : m["settings.vertd.available"]({ commitId: vertdCommit })
: "unavailable (is the url right?)"} : m["settings.vertd.unavailable"]()}
</p> </p>
<div class="flex flex-col gap-8"> <div class="flex flex-col gap-8">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<p class="text-sm text-muted font-normal"> <p class="text-sm text-muted font-normal">
The <code>vertd</code> project is a server wrapper for FFmpeg. {@html m["settings.vertd.description"]()}
This allows you to convert videos through the convenience of
VERT's web interface, while still being able to harness the power
of your GPU to do it as quickly as possible.
</p> </p>
<p class="text-sm text-muted font-normal"> <p class="text-sm text-muted font-normal">
We host a public instance for your convenience, but it is {@html link("vertd_link", m["settings.vertd.hosting_info"](), GITHUB_URL_VERTD)}
quite easy to host your own on your PC or server if you know
what you are doing. You can download the server binaries <a
href={GITHUB_URL_VERTD}
target="_blank">here</a
> - the process of setting this up will become easier in the
future, so stay tuned!
</p> </p>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p class="text-base font-bold">Instance URL</p> <p class="text-base font-bold">{m["settings.vertd.instance_url"]()}</p>
<input <input
type="text" type="text"
placeholder="Example: http://localhost:24153" placeholder={m["settings.vertd.url_placeholder"]()}
bind:value={settings.vertdURL} bind:value={settings.vertdURL}
/> />
</div> </div>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p class="text-base font-bold">Conversion speed</p> <p class="text-base font-bold">{m["settings.vertd.conversion_speed"]()}</p>
<p class="text-sm text-muted font-normal"> <p class="text-sm text-muted font-normal">
This describes the tradeoff between speed and {m["settings.vertd.speed_description"]()}
quality. Faster speeds will result in lower quality,
but will get the job done quicker.
</p> </p>
</div> </div>
<Dropdown <Dropdown
options={[ options={[
"Very Slow", m["settings.vertd.speeds.very_slow"](),
"Slower", m["settings.vertd.speeds.slower"](),
"Slow", m["settings.vertd.speeds.slow"](),
"Medium", m["settings.vertd.speeds.medium"](),
"Fast", m["settings.vertd.speeds.fast"](),
"Ultra Fast", m["settings.vertd.speeds.ultra_fast"](),
]} ]}
settingsStyle settingsStyle
selected={(() => { selected={(() => {
switch (settings.vertdSpeed) { switch (settings.vertdSpeed) {
case "verySlow": case "verySlow":
return "Very Slow"; return m["settings.vertd.speeds.very_slow"]();
case "slower": case "slower":
return "Slower"; return m["settings.vertd.speeds.slower"]();
case "slow": case "slow":
return "Slow"; return m["settings.vertd.speeds.slow"]();
case "medium": case "medium":
return "Medium"; return m["settings.vertd.speeds.medium"]();
case "fast": case "fast":
return "Fast"; return m["settings.vertd.speeds.fast"]();
case "ultraFast": case "ultraFast":
return "Ultra Fast"; return m["settings.vertd.speeds.ultra_fast"]();
} }
})()} })()}
onselect={(selected) => { onselect={(selected) => {
switch (selected) { switch (selected) {
case "Very Slow": case m["settings.vertd.speeds.very_slow"]():
settings.vertdSpeed = "verySlow"; settings.vertdSpeed = "verySlow";
break; break;
case "Slower": case m["settings.vertd.speeds.slower"]():
settings.vertdSpeed = "slower"; settings.vertdSpeed = "slower";
break; break;
case "Slow": case m["settings.vertd.speeds.slow"]():
settings.vertdSpeed = "slow"; settings.vertdSpeed = "slow";
break; break;
case "Medium": case m["settings.vertd.speeds.medium"]():
settings.vertdSpeed = "medium"; settings.vertdSpeed = "medium";
break; break;
case "Fast": case m["settings.vertd.speeds.fast"]():
settings.vertdSpeed = "fast"; settings.vertdSpeed = "fast";
break; break;
case "Ultra Fast": case m["settings.vertd.speeds.ultra_fast"]():
settings.vertdSpeed = "ultraFast"; settings.vertdSpeed = "ultraFast";
break; break;
} }

View File

@ -1,4 +1,5 @@
import { PUB_VERTD_URL } from "$env/static/public"; import { PUB_VERTD_URL } from "$env/static/public";
import type { ConversionBitrate } from "$lib/converters/ffmpeg.svelte";
import type { ConversionSpeed } from "$lib/converters/vertd.svelte"; import type { ConversionSpeed } from "$lib/converters/vertd.svelte";
export { default as Appearance } from "./Appearance.svelte"; export { default as Appearance } from "./Appearance.svelte";
@ -10,7 +11,11 @@ export interface ISettings {
filenameFormat: string; filenameFormat: string;
plausible: boolean; plausible: boolean;
vertdURL: string; vertdURL: string;
vertdSpeed: ConversionSpeed; vertdSpeed: ConversionSpeed; // videos
magickQuality: number; // images
ffmpegQuality: ConversionBitrate; // audio (or audio <-> video)
ffmpegSampleRate: string; // audio (or audio <-> video)
ffmpegCustomSampleRate: number; // audio (or audio <-> video) - only used when ffmpegSampleRate is "custom"
} }
export class Settings { export class Settings {
@ -21,6 +26,10 @@ export class Settings {
plausible: true, plausible: true,
vertdURL: PUB_VERTD_URL, vertdURL: PUB_VERTD_URL,
vertdSpeed: "slow", vertdSpeed: "slow",
magickQuality: 100,
ffmpegQuality: "auto",
ffmpegSampleRate: "auto",
ffmpegCustomSampleRate: 44100,
}); });
public save() { public save() {

View File

@ -1,11 +1,12 @@
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import { converters } from "$lib/converters"; import { byNative, converters } from "$lib/converters";
import { error, log } from "$lib/logger"; import { error, log } from "$lib/logger";
import { VertFile } from "$lib/types"; import { VertFile } from "$lib/types";
import { parseBlob, selectCover } from "music-metadata"; import { parseBlob, selectCover } from "music-metadata";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import { addDialog } from "./DialogProvider"; import { addDialog } from "./DialogProvider";
import PQueue from "p-queue"; import PQueue from "p-queue";
import { getLocale, setLocale } from "$lib/paraglide/runtime";
class Files { class Files {
public files = $state<VertFile[]>([]); public files = $state<VertFile[]>([]);
@ -32,11 +33,13 @@ class Files {
this.thumbnailQueue.add(async () => { this.thumbnailQueue.add(async () => {
const isAudio = converters const isAudio = converters
.find((c) => c.name === "ffmpeg") .find((c) => c.name === "ffmpeg")
?.formatStrings() ?.supportedFormats.filter((f) => f.isNative)
.map((f) => f.name)
?.includes(file.from.toLowerCase()); ?.includes(file.from.toLowerCase());
const isVideo = converters const isVideo = converters
.find((c) => c.name === "vertd") .find((c) => c.name === "vertd")
?.formatStrings() ?.supportedFormats.filter((f) => f.isNative)
.map((f) => f.name)
?.includes(file.from.toLowerCase()); ?.includes(file.from.toLowerCase());
try { try {
@ -120,11 +123,11 @@ class Files {
log(["files"], `no extension found for ${file.name}`); log(["files"], `no extension found for ${file.name}`);
return; return;
} }
const converter = converters.find((c) => const converter = converters
c .sort(byNative(format))
.formatStrings() .find((converter) =>
.includes(format || ".somenonexistentextension"), converter.formatStrings().includes(format),
); );
if (!converter) { if (!converter) {
log(["files"], `no converter found for ${file.name}`); log(["files"], `no converter found for ${file.name}`);
this.files.push(new VertFile(file, format)); this.files.push(new VertFile(file, format));
@ -292,3 +295,47 @@ export const vertdLoaded = writable(false);
export const isMobile = writable(false); export const isMobile = writable(false);
export const effects = writable(true); export const effects = writable(true);
export const theme = writable<"light" | "dark">("light"); export const theme = writable<"light" | "dark">("light");
export const locale = writable(getLocale());
export const availableLocales = {
"en": "English",
"es": "Español",
}
export function updateLocale(newLocale: string) {
log(["locale"], `set to ${newLocale}`);
localStorage.setItem("locale", newLocale);
// @ts-expect-error shush
setLocale(newLocale, { reload: false });
// @ts-expect-error shush
locale.set(newLocale);
}
export function link(
tag: string | string[],
text: string,
links: string | string[],
newTab?: boolean | boolean[],
className?: string | string[]
) {
if (!text) return "";
const tags = Array.isArray(tag) ? tag : [tag];
const linksArr = Array.isArray(links) ? links : [links];
const newTabArr = Array.isArray(newTab) ? newTab : [newTab];
const classArr = Array.isArray(className) ? className : [className];
let result = text;
tags.forEach((t, i) => {
const link = linksArr[i] ?? "#";
const target = newTabArr[i] ? 'target="_blank" rel="noopener noreferrer"' : "";
const cls = classArr[i] ? `class="${classArr[i]}"` : "";
const regex = new RegExp(`\\[${t}\\](.*?)\\[\\/${t}\\]`, "g");
result = result.replace(regex, (_, inner) =>
`<a href="${link}" ${target} ${cls} >${inner}</a>`
);
});
return result;
}

View File

@ -1,6 +1,7 @@
import { converters } from "$lib/converters"; import { byNative, converters } from "$lib/converters";
import type { Converter } from "$lib/converters/converter.svelte"; import type { Converter } from "$lib/converters/converter.svelte";
import { error } from "$lib/logger"; import { error } from "$lib/logger";
import { m } from "$lib/paraglide/messages";
import { addToast } from "$lib/store/ToastProvider"; import { addToast } from "$lib/store/ToastProvider";
export class VertFile { export class VertFile {
@ -27,18 +28,35 @@ export class VertFile {
public converters: Converter[] = []; public converters: Converter[] = [];
public findConverters(supportedFormats: string[] = [this.from]) { public findConverters(supportedFormats: string[] = [this.from]) {
const converter = this.converters.filter((converter) => const converter = this.converters
converter.formatStrings().map((f) => supportedFormats.includes(f)), .filter((converter) =>
); converter
.formatStrings()
.map((f) => supportedFormats.includes(f)),
)
.sort(byNative(this.from));
return converter; return converter;
} }
public findConverter() { public findConverter() {
const converter = this.converters.find( const converter = this.converters.find((converter) => {
(converter) => if (
converter.formatStrings().includes(this.from) && !converter.formatStrings().includes(this.from) ||
converter.formatStrings().includes(this.to), !converter.formatStrings().includes(this.to)
); ) {
return false;
}
const theirFrom = converter.supportedFormats.find(
(f) => f.name === this.from,
);
const theirTo = converter.supportedFormats.find(
(f) => f.name === this.to,
);
if (!theirFrom || !theirTo) return false;
if (!theirFrom.isNative && !theirTo.isNative) return false;
return true;
});
return converter; return converter;
} }
@ -75,7 +93,10 @@ export class VertFile {
error(["files"], castedErr.message); error(["files"], castedErr.message);
addToast( addToast(
"error", "error",
`Error converting file ${this.file.name}: ${castedErr.message || castedErr}`, m["workers.errors.general"]({
file: this.file.name,
message: castedErr.message || castedErr,
}),
); );
this.result = null; this.result = null;
} }
@ -105,7 +126,8 @@ export class VertFile {
const blob = URL.createObjectURL( const blob = URL.createObjectURL(
new Blob([await this.result.file.arrayBuffer()], { new Blob([await this.result.file.arrayBuffer()], {
type: to.slice(1), // type: to.slice(1),
type: "application/octet-stream", // use generic type to prevent browsers changing extension
}), }),
); );
const a = document.createElement("a"); const a = document.createElement("a");

View File

@ -26,15 +26,12 @@ magickPromise
const handleMessage = async (message: any): Promise<any> => { const handleMessage = async (message: any): Promise<any> => {
switch (message.type) { switch (message.type) {
case "convert": { case "convert": {
const compression: number | undefined = message.compression;
if (!message.to.startsWith(".")) message.to = `.${message.to}`; if (!message.to.startsWith(".")) message.to = `.${message.to}`;
message.to = message.to.toLowerCase(); message.to = message.to.toLowerCase();
if (message.to === ".jfif") { if (message.to === ".jfif") message.to = ".jpeg";
message.to = ".jpeg"; if (message.input.from === ".jfif") message.input.from = ".jpeg";
} if (message.input.from === ".fit") message.input.from = ".fits";
if (message.input.from === ".jfif") {
message.input.from = ".jpeg";
}
const buffer = await message.input.file.arrayBuffer(); const buffer = await message.input.file.arrayBuffer();
// only wait when we need to // only wait when we need to
@ -70,7 +67,7 @@ const handleMessage = async (message: any): Promise<any> => {
const convertedImgs: Uint8Array[] = []; const convertedImgs: Uint8Array[] = [];
await Promise.all( await Promise.all(
imgs.map(async (img, i) => { imgs.map(async (img, i) => {
const output = await magickConvert(img, message.to); const output = await magickConvert(img, message.to, compression);
convertedImgs[i] = output; convertedImgs[i] = output;
}), }),
); );
@ -78,7 +75,10 @@ const handleMessage = async (message: any): Promise<any> => {
const zip = makeZip( const zip = makeZip(
convertedImgs.map( convertedImgs.map(
(img, i) => (img, i) =>
new File([img], `image${i}.${message.to.slice(1)}`), new File(
[new Uint8Array(img)],
`image${i}.${message.to.slice(1)}`,
),
), ),
"images.zip", "images.zip",
); );
@ -108,9 +108,13 @@ const handleMessage = async (message: any): Promise<any> => {
}), }),
), ),
message.to, message.to,
compression
); );
files.push( files.push(
new File([blob], `image${i}${message.to}`), new File(
[new Uint8Array(blob)],
`image${i}${message.to}`,
),
); );
}), }),
); );
@ -154,6 +158,7 @@ const handleMessage = async (message: any): Promise<any> => {
const converted = await magickConvert( const converted = await magickConvert(
img, img,
message.to, message.to,
compression
); );
outputs.push(converted); outputs.push(converted);
break; break;
@ -167,7 +172,10 @@ const handleMessage = async (message: any): Promise<any> => {
const zip = makeZip( const zip = makeZip(
outputs.map( outputs.map(
(img, i) => (img, i) =>
new File([img], `image${i}.${message.to.slice(1)}`), new File(
[new Uint8Array(img)],
`image${i}.${message.to.slice(1)}`,
),
), ),
"images.zip", "images.zip",
); );
@ -179,6 +187,32 @@ const handleMessage = async (message: any): Promise<any> => {
}; };
} }
// build frames of animated formats (webp/gif)
// APNG does not work on magick-wasm since it needs ffmpeg built-in (not in magick-wasm) - handle in ffmpeg
if (
(message.input.from === ".webp" ||
message.input.from === ".gif") &&
(message.to === ".gif" || message.to === ".webp")
) {
const collection = MagickImageCollection.create(
new Uint8Array(buffer),
);
const format =
message.to === ".gif"
? MagickFormat.Gif
: MagickFormat.WebP;
const result = await new Promise<Uint8Array>((resolve) => {
collection.write(format, (output) => {
resolve(structuredClone(output));
});
});
collection.dispose();
return {
type: "finished",
output: result,
};
}
const img = MagickImage.create( const img = MagickImage.create(
new Uint8Array(buffer), new Uint8Array(buffer),
new MagickReadSettings({ new MagickReadSettings({
@ -188,7 +222,7 @@ const handleMessage = async (message: any): Promise<any> => {
}), }),
); );
const converted = await magickConvert(img, message.to); const converted = await magickConvert(img, message.to, compression);
return { return {
type: "finished", type: "finished",
@ -206,7 +240,10 @@ const readToEnd = async (reader: ReadableStreamDefaultReader<Uint8Array>) => {
if (value) chunks.push(value); if (value) chunks.push(value);
done = d; done = d;
} }
const blob = new Blob(chunks, { type: "application/zip" }); const blob = new Blob(
chunks.map((chunk) => new Uint8Array(chunk)),
{ type: "application/zip" },
);
const arrayBuffer = await blob.arrayBuffer(); const arrayBuffer = await blob.arrayBuffer();
return new Uint8Array(arrayBuffer); return new Uint8Array(arrayBuffer);
}; };
@ -250,8 +287,12 @@ const magickToBlob = async (img: IMagickImage): Promise<Blob> => {
return; return;
} }
if (!data) {
reject(new Error("Pixel data is null"));
return;
}
const imageData = new ImageData( const imageData = new ImageData(
new Uint8ClampedArray(data?.buffer || new ArrayBuffer(0)), new Uint8ClampedArray(data),
img.width, img.width,
img.height, img.height,
); );
@ -266,7 +307,11 @@ const magickToBlob = async (img: IMagickImage): Promise<Blob> => {
); );
}; };
const magickConvert = async (img: IMagickImage, to: string) => { const magickConvert = async (
img: IMagickImage,
to: string,
compression?: number,
) => {
const intermediary = await magickToBlob(img); const intermediary = await magickToBlob(img);
const buf = new Uint8Array(await intermediary.arrayBuffer()); const buf = new Uint8Array(await intermediary.arrayBuffer());
let fmt = to.slice(1).toUpperCase(); let fmt = to.slice(1).toUpperCase();
@ -274,6 +319,8 @@ const magickConvert = async (img: IMagickImage, to: string) => {
const result = await new Promise<Uint8Array>((resolve) => { const result = await new Promise<Uint8Array>((resolve) => {
ImageMagick.read(buf, MagickFormat.Png, (image) => { ImageMagick.read(buf, MagickFormat.Png, (image) => {
// magick-wasm automatically clamps (https://github.com/dlemstra/magick-wasm/blob/76fc6f2b0c0497d2ddc251bbf6174b4dc92ac3ea/src/magick-image.ts#L2480)
if (compression) image.quality = compression;
image.write(fmt as unknown as MagickFormat, (o) => { image.write(fmt as unknown as MagickFormat, (o) => {
resolve(structuredClone(o)); resolve(structuredClone(o));
}); });

View File

@ -113,8 +113,6 @@ const formatToReader = (format: Format): string => {
return "rtf"; return "rtf";
case ".rst": case ".rst":
return "rst"; return "rst";
case ".xml":
return "xml";
} }
throw new Error(`Unsupported format: ${format}`); throw new Error(`Unsupported format: ${format}`);

View File

@ -2,11 +2,7 @@
import { onMount } from "svelte"; import { onMount } from "svelte";
import { goto, beforeNavigate, afterNavigate } from "$app/navigation"; import { goto, beforeNavigate, afterNavigate } from "$app/navigation";
import { import { PUB_PLAUSIBLE_URL, PUB_HOSTNAME } from "$env/static/public";
PUB_PLAUSIBLE_URL,
PUB_HOSTNAME,
PUB_DONATION_URL,
} from "$env/static/public";
import { VERT_NAME } from "$lib/consts"; import { VERT_NAME } from "$lib/consts";
import * as Layout from "$lib/components/layout"; import * as Layout from "$lib/components/layout";
import * as Navbar from "$lib/components/layout/Navbar"; import * as Navbar from "$lib/components/layout/Navbar";
@ -19,11 +15,13 @@
theme, theme,
dropping, dropping,
vertdLoaded, vertdLoaded,
locale,
} from "$lib/store/index.svelte"; } from "$lib/store/index.svelte";
import "$lib/css/app.scss"; import "$lib/css/app.scss";
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import { page } from "$app/state"; import { page } from "$app/state";
import { initStores as initAnimStores } from "$lib/animation/index.js"; import { initStores as initAnimStores } from "$lib/animation/index.js";
import { locales, localizeHref } from "$lib/paraglide/runtime";
let { children, data } = $props(); let { children, data } = $props();
let enablePlausible = $state(false); let enablePlausible = $state(false);
@ -72,7 +70,6 @@
theme.set( theme.set(
(localStorage.getItem("theme") as "light" | "dark") || "light", (localStorage.getItem("theme") as "light" | "dark") || "light",
); );
Settings.instance.load(); Settings.instance.load();
fetch(`${Settings.instance.settings.vertdURL}/api/version`).then( fetch(`${Settings.instance.settings.vertdURL}/api/version`).then(
@ -83,7 +80,6 @@
}); });
$effect(() => { $effect(() => {
// Enable plausible if enabled
enablePlausible = enablePlausible =
!!PUB_PLAUSIBLE_URL && Settings.instance.settings.plausible; !!PUB_PLAUSIBLE_URL && Settings.instance.settings.plausible;
if (!enablePlausible && browser) { if (!enablePlausible && browser) {
@ -142,35 +138,44 @@
</svelte:head> </svelte:head>
<!-- FIXME: if user resizes between desktop/mobile, highlight of page disappears (only shows on original size) --> <!-- FIXME: if user resizes between desktop/mobile, highlight of page disappears (only shows on original size) -->
<div {#key $locale}
class="flex flex-col min-h-screen h-full w-full overflow-x-hidden" <div
ondrop={dropFiles} class="flex flex-col min-h-screen h-full w-full overflow-x-hidden"
ondragenter={(e) => handleDrag(e, true)} ondrop={dropFiles}
ondragover={(e) => handleDrag(e, true)} ondragenter={(e) => handleDrag(e, true)}
ondragleave={(e) => handleDrag(e, false)} ondragover={(e) => handleDrag(e, true)}
role="region" ondragleave={(e) => handleDrag(e, false)}
> role="region"
<Layout.UploadRegion /> >
<Layout.UploadRegion />
<div> <div>
<Layout.MobileLogo /> <Layout.MobileLogo />
<Navbar.Desktop /> <Navbar.Desktop />
</div> </div>
<!-- <!--
SvelteKit throws the following warning when developing - safe to ignore as we render the children in this component: SvelteKit throws the following warning when developing - safe to ignore as we render the children in this component:
`<slot />` or `{@render ...}` tag missing — inner content will not be rendered `<slot />` or `{@render ...}` tag missing — inner content will not be rendered
--> -->
<Layout.PageContent {children} /> <Layout.PageContent {children} />
<div style="display:none">
{#each locales as locale}
<a href={localizeHref(page.url.pathname, { locale })}
>{locale}</a
>
{/each}
</div>
<Layout.Toasts /> <Layout.Toasts />
<Layout.Dialogs /> <Layout.Dialogs />
<div> <div>
<Layout.Footer /> <Layout.Footer />
<Navbar.Mobile /> <Navbar.Mobile />
</div>
</div> </div>
</div> {/key}
<!-- Gradients placed here to prevent it overlapping in transitions --> <!-- Gradients placed here to prevent it overlapping in transitions -->
<Layout.Gradients /> <Layout.Gradients />

View File

@ -5,6 +5,11 @@
import { vertdLoaded } from "$lib/store/index.svelte"; import { vertdLoaded } from "$lib/store/index.svelte";
import clsx from "clsx"; import clsx from "clsx";
import { AudioLines, BookText, Check, Film, Image } from "lucide-svelte"; import { AudioLines, BookText, Check, Film, Image } from "lucide-svelte";
import { m } from "$lib/paraglide/messages";
import { OverlayScrollbarsComponent } from "overlayscrollbars-svelte";
import { browser } from "$app/environment";
import "overlayscrollbars/overlayscrollbars.css";
import { onMount } from "svelte";
const getSupportedFormats = (name: string) => const getSupportedFormats = (name: string) =>
converters converters
@ -20,6 +25,7 @@
ready: boolean; ready: boolean;
formats: string; formats: string;
icon: typeof Image; icon: typeof Image;
title: string;
}; };
} = $derived({ } = $derived({
Images: { Images: {
@ -28,16 +34,19 @@
false, false,
formats: getSupportedFormats("imagemagick"), formats: getSupportedFormats("imagemagick"),
icon: Image, icon: Image,
title: m["upload.cards.images"](),
}, },
Audio: { Audio: {
ready: converters.find((c) => c.name === "ffmpeg")?.ready || false, ready: converters.find((c) => c.name === "ffmpeg")?.ready || false,
formats: getSupportedFormats("ffmpeg"), formats: getSupportedFormats("ffmpeg"),
icon: AudioLines, icon: AudioLines,
title: m["upload.cards.audio"](),
}, },
Documents: { Documents: {
ready: converters.find((c) => c.name === "pandoc")?.ready || false, ready: converters.find((c) => c.name === "pandoc")?.ready || false,
formats: getSupportedFormats("pandoc"), formats: getSupportedFormats("pandoc"),
icon: BookText, icon: BookText,
title: m["upload.cards.documents"](),
}, },
Video: { Video: {
ready: ready:
@ -45,6 +54,7 @@
(false && $vertdLoaded), (false && $vertdLoaded),
formats: getSupportedFormats("vertd"), formats: getSupportedFormats("vertd"),
icon: Film, icon: Film,
title: m["upload.cards.video"](),
}, },
}); });
@ -58,12 +68,35 @@
); );
if (formatInfo) { if (formatInfo) {
return `This format can only be converted as ${ const direction = formatInfo.fromSupported
formatInfo.fromSupported ? "input (from)" : "output (to)" ? m["upload.tooltip.direction_input"]()
}.`; : m["upload.tooltip.direction_output"]();
return m["upload.tooltip.partial_support"]({ direction });
} }
return ""; return "";
}; };
let scrollContainers: HTMLElement[] = $state([]);
// svelte-ignore state_referenced_locally
let showBlur = $state(Array(Object.keys(status).length).fill(false));
onMount(() => {
const handleResize = () => {
for (let i = 0; i < scrollContainers.length; i++) {
// show bottom blur if scrollable
const container = scrollContainers[i];
if (!container) return;
showBlur[i] = container.scrollHeight > container.clientHeight;
}
};
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
});
</script> </script>
<div class="max-w-6xl w-full mx-auto px-6 md:px-8"> <div class="max-w-6xl w-full mx-auto px-6 md:px-8">
@ -75,14 +108,12 @@
<h1 <h1
class="text-4xl px-12 md:p-0 md:text-6xl flex-wrap tracking-tight leading-tight md:leading-[72px] mb-4 md:mb-6" class="text-4xl px-12 md:p-0 md:text-6xl flex-wrap tracking-tight leading-tight md:leading-[72px] mb-4 md:mb-6"
> >
The file converter you'll love. {m["upload.title"]()}
</h1> </h1>
<p <p
class="font-normal px-5 md:p-0 text-lg md:text-xl text-black text-muted dynadark:text-muted" class="font-normal px-5 md:p-0 text-lg md:text-xl text-black text-muted dynadark:text-muted"
> >
All image, audio, and document processing is done on your {m["upload.subtitle"]()}
device. Videos are converted on our lightning-fast servers.
No file size limit, no ads, and completely open source.
</p> </p>
</div> </div>
<div class="flex-grow w-full h-72"> <div class="flex-grow w-full h-72">
@ -94,84 +125,150 @@
<hr /> <hr />
<div class="mt-10 md:mt-16"> <div class="mt-10 md:mt-16">
<h2 class="text-center text-4xl">VERT supports...</h2> <h2 class="text-center text-4xl">{m["upload.cards.title"]()}</h2>
<div class="flex gap-4 mt-8 md:flex-row flex-col"> <div class="flex gap-4 mt-8 md:flex-row flex-col">
{#each Object.entries(status) as [key, s]} {#if browser}
{@const Icon = s.icon} {#each Object.entries(status) as [key, s], i}
<div class="file-category-card w-full flex flex-col gap-4"> {@const Icon = s.icon}
<div class="file-category-card-inner"> <div class="file-category-card w-full flex flex-col gap-4">
<div <div class="file-category-card-inner">
class={clsx("icon-container", { <div
"bg-accent-blue": key === "Images", class={clsx("icon-container", {
"bg-accent-purple": key === "Audio", "bg-accent-blue": key === "Images",
"bg-accent-green": key === "Documents", "bg-accent-purple": key === "Audio",
"bg-accent-red": key === "Video", "bg-accent-green": key === "Documents",
})} "bg-accent-red": key === "Video",
> })}
<Icon size="20" /> >
<Icon size="20" />
</div>
<span>{s.title}</span>
</div> </div>
<span>{key}</span>
</div>
<div class="file-category-card-content flex-grow gap-4"> <div
{#if key === "Video"} class="file-category-card-content flex-grow relative"
<p> >
Video uploads to a server for processing by <OverlayScrollbarsComponent
default, learn how to set it up locally <a options={{
target="_blank" scrollbars: {
href="https://github.com/VERT-sh/VERT/wiki/How-to-convert-video-with-VERT" autoHide: "move",
>here</a autoHideDelay: 1500,
>. },
</p> }}
{:else} defer
<p class="flex tems-center justify-center gap-2"> >
<Check size="20" /> Local fully supported <div
</p> class="flex flex-col gap-4 h-[12.25rem] relative"
{/if} bind:this={scrollContainers[i]}
<p> >
<b>Status: </b> {#if key === "Video"}
{s.ready ? "ready" : "not ready"} <p
</p> class="flex tems-center justify-center gap-2"
<div> >
<span class="flex flex-wrap justify-center"> <Check size="20" />
<b>Supported formats:&nbsp;</b>
{#each s.formats.split(", ") as format, index}
{@const isPartial = format.endsWith("*")}
{@const formatName = isPartial
? format.slice(0, -1)
: format}
<span
class="text-sm font-normal flex items-center"
>
{#if isPartial}
<Tooltip <Tooltip
text={getTooltip(formatName)} text={m[
"upload.tooltip.video_server_processing"
]()}
> >
{formatName}<span <span>
class="text-red-500">*</span <a
> href="https://github.com/VERT-sh/VERT/blob/main/docs/VIDEO_CONVERSION.md"
target="_blank"
rel="noopener noreferrer"
>
{m[
"upload.cards.video_server_processing"
]()}
</a>
<span
class="text-red-500 -ml-0.5"
>*</span
>
</span>
</Tooltip> </Tooltip>
{:else} </p>
{formatName} {:else}
{/if} <p
{#if index < s.formats.split(", ").length - 1} class="flex tems-center justify-center gap-2"
<span>,&nbsp;</span> >
{/if} <Check size="20" />
</span> {m[
{/each} "upload.cards.local_supported"
</span> ]()}
</p>
{/if}
<p>
{@html m["upload.cards.status.text"]({
status: s.ready
? m[
"upload.cards.status.ready"
]()
: m[
"upload.cards.status.not_ready"
](),
})}
</p>
<div class="flex flex-col items-center relative">
<b
>{m[
"upload.cards.supported_formats"
]()}&nbsp;</b
>
<p
class="flex flex-wrap justify-center leading-tight px-2"
>
{#each s.formats.split(", ") as format, index}
{@const isPartial =
format.endsWith("*")}
{@const formatName = isPartial
? format.slice(0, -1)
: format}
<span
class="text-sm font-normal flex items-center relative"
>
{#if isPartial}
<Tooltip
text={getTooltip(
formatName,
)}
>
{formatName}<span
class="text-red-500"
>*</span
>
</Tooltip>
{:else}
{formatName}
{/if}
{#if index < s.formats.split(", ").length - 1}
<span>,&nbsp;</span>
{/if}
</span>
{/each}
</p>
</div>
</div>
</OverlayScrollbarsComponent>
<!-- blur at bottom if scrollable - positioned relative to the card container -->
{#if showBlur[i]}
<div
class="absolute left-0 bottom-0 w-full h-10 pointer-events-none"
style={`background: linear-gradient(to top, var(--bg-panel), transparent 100%);`}
></div>
{/if}
</div> </div>
</div> </div>
</div> {/each}
{/each} {/if}
</div> </div>
</div> </div>
</div> </div>
<style> <style>
.file-category-card { .file-category-card {
@apply bg-panel rounded-2xl p-5 shadow-panel; @apply bg-panel rounded-2xl p-5 shadow-panel relative;
} }
.file-category-card p { .file-category-card p {

View File

@ -12,6 +12,7 @@
import { addToast } from "$lib/store/ToastProvider"; import { addToast } from "$lib/store/ToastProvider";
import { dev } from "$app/environment"; import { dev } from "$app/environment";
import { page } from "$app/state"; import { page } from "$app/state";
import { m } from "$lib/paraglide/messages";
// import { dev } from "$app/environment"; // import { dev } from "$app/environment";
// import { page } from "$app/state"; // import { page } from "$app/state";
@ -34,19 +35,19 @@
{ {
name: "nullptr", name: "nullptr",
github: "https://github.com/not-nullptr", github: "https://github.com/not-nullptr",
role: "Lead developer; conversion backend, UI implementation", role: m["about.credits.roles.lead_developer"](),
avatar: avatarNullptr, avatar: avatarNullptr,
}, },
{ {
name: "JovannMC", name: "JovannMC",
github: "https://github.com/JovannMC", github: "https://github.com/JovannMC",
role: "Developer; UI implementation", role: m["about.credits.roles.developer"](),
avatar: avatarJovannMC, avatar: avatarJovannMC,
}, },
{ {
name: "Liam", name: "Liam",
github: "https://x.com/z2rMC", github: "https://x.com/z2rMC",
role: "Designer; UX, branding, marketing", role: m["about.credits.roles.designer"](),
avatar: avatarLiam, avatar: avatarLiam,
}, },
]; ];
@ -55,13 +56,13 @@
{ {
name: "azurejelly", name: "azurejelly",
github: "https://github.com/azurejelly", github: "https://github.com/azurejelly",
role: "Maintaining Docker & CI support", role: m["about.credits.roles.docker_ci"](),
avatar: avatarAzurejelly, avatar: avatarAzurejelly,
}, },
{ {
name: "Realmy", name: "Realmy",
github: "https://github.com/RealmyTheMan", github: "https://github.com/RealmyTheMan",
role: "Former co-founder & designer", role: m["about.credits.roles.former_cofounder"](),
avatar: avatarRealmy, avatar: avatarRealmy,
}, },
]; ];
@ -80,7 +81,7 @@
try { try {
const response = await fetch(`${GITHUB_API_URL}/contributors`); const response = await fetch(`${GITHUB_API_URL}/contributors`);
if (!response.ok) { if (!response.ok) {
addToast("error", "Error fetching GitHub contributors"); addToast("error", m["about.errors.github_contributors"]());
throw new Error(`HTTP error, status: ${response.status}`); throw new Error(`HTTP error, status: ${response.status}`);
} }
const allContribs = await response.json(); const allContribs = await response.json();
@ -89,6 +90,7 @@
const excludedNames = new Set([ const excludedNames = new Set([
...mainContribs.map((c) => c.github.split("/").pop()), ...mainContribs.map((c) => c.github.split("/").pop()),
...notableContribs.map((c) => c.github.split("/").pop()), ...notableContribs.map((c) => c.github.split("/").pop()),
"Z2r-YT"
]); ]);
const filteredContribs = allContribs.filter( const filteredContribs = allContribs.filter(
@ -135,7 +137,7 @@
<div class="flex flex-col h-full items-center"> <div class="flex flex-col h-full items-center">
<h1 class="hidden md:block text-[40px] tracking-tight leading-[72px] mb-6"> <h1 class="hidden md:block text-[40px] tracking-tight leading-[72px] mb-6">
<InfoIcon size="40" class="inline-block -mt-2 mr-2" /> <InfoIcon size="40" class="inline-block -mt-2 mr-2" />
About {m["about.title"]()}
</h1> </h1>
<div <div

View File

@ -5,7 +5,7 @@
import Panel from "$lib/components/visual/Panel.svelte"; import Panel from "$lib/components/visual/Panel.svelte";
import ProgressBar from "$lib/components/visual/ProgressBar.svelte"; import ProgressBar from "$lib/components/visual/ProgressBar.svelte";
import Tooltip from "$lib/components/visual/Tooltip.svelte"; import Tooltip from "$lib/components/visual/Tooltip.svelte";
import { categories, converters } from "$lib/converters"; import { categories, converters, byNative } from "$lib/converters";
import { import {
effects, effects,
files, files,
@ -28,6 +28,7 @@
XIcon, XIcon,
} from "lucide-svelte"; } from "lucide-svelte";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { m } from "$lib/paraglide/messages";
onMount(() => { onMount(() => {
// depending on format, select right category and format // depending on format, select right category and format
@ -51,21 +52,30 @@
$effect(() => { $effect(() => {
// Set gradient color depending on the file types // Set gradient color depending on the file types
// TODO: if more file types added, add a "fileType" property to the file object // TODO: if more file types added, add a "fileType" property to the file object
const allAudio = files.files.every( const allAudio = files.files.every((file) => {
(file) => file.findConverter()?.name === "ffmpeg", const converter = file
); .findConverters()
const allImages = files.files.every( .sort(byNative(file.from))[0];
(file) => return converter?.name === "ffmpeg";
file.findConverter()?.name !== "ffmpeg" && });
file.findConverter()?.name !== "vertd", const allImages = files.files.every((file) => {
); const converter = file
const allVideos = files.files.every( .findConverters()
(file) => file.findConverter()?.name === "vertd", .sort(byNative(file.from))[0];
); return converter?.name === "libvips";
});
const allDocuments = files.files.every( const allVideos = files.files.every((file) => {
(file) => file.findConverter()?.name === "pandoc", const converter = file
); .findConverters()
.sort(byNative(file.from))[0];
return converter?.name === "vertd";
});
const allDocuments = files.files.every((file) => {
const converter = file
.findConverters()
.sort(byNative(file.from))[0];
return converter?.name === "pandoc";
});
if (files.files.length === 1 && files.files[0].blobUrl && !allVideos) { if (files.files.length === 1 && files.files[0].blobUrl && !allVideos) {
showGradient.set(false); showGradient.set(false);
@ -75,7 +85,7 @@
if ( if (
files.files.length === 0 || files.files.length === 0 ||
(!allAudio && !allImages && !allVideos) (!allAudio && !allImages && !allVideos && !allDocuments)
) { ) {
gradientColor.set(""); gradientColor.set("");
} else { } else {
@ -95,7 +105,6 @@
</script> </script>
{#snippet fileItem(file: VertFile, index: number)} {#snippet fileItem(file: VertFile, index: number)}
{@const availableConverters = file.findConverters()}
{@const currentConverter = converters.find( {@const currentConverter = converters.find(
(c) => (c) =>
c.formatStrings((f) => f.fromSupported).includes(file.from) && c.formatStrings((f) => f.fromSupported).includes(file.from) &&
@ -103,11 +112,13 @@
)} )}
{@const isAudio = converters {@const isAudio = converters
.find((c) => c.name === "ffmpeg") .find((c) => c.name === "ffmpeg")
?.formatStrings((f) => f.fromSupported) ?.supportedFormats.filter((f) => f.isNative)
.map((f) => f.name)
.includes(file.from)} .includes(file.from)}
{@const isVideo = converters {@const isVideo = converters
.find((c) => c.name === "vertd") .find((c) => c.name === "vertd")
?.formatStrings((f) => f.fromSupported) ?.supportedFormats.filter((f) => f.isNative)
.map((f) => f.name)
.includes(file.from)} .includes(file.from)}
{@const isImage = converters {@const isImage = converters
.find((c) => c.name === "imagemagick") .find((c) => c.name === "imagemagick")
@ -115,28 +126,29 @@
.includes(file.from)} .includes(file.from)}
{@const isDocument = converters {@const isDocument = converters
.find((c) => c.name === "pandoc") .find((c) => c.name === "pandoc")
?.formatStrings((f) => f.fromSupported) ?.supportedFormats.filter((f) => f.isNative)
.map((f) => f.name)
.includes(file.from)} .includes(file.from)}
<Panel class="p-5 flex flex-col min-w-0 gap-4 relative"> <Panel class="p-5 flex flex-col min-w-0 gap-4 relative">
<div class="flex-shrink-0 h-8 w-full flex items-center gap-2"> <div class="flex-shrink-0 h-8 w-full flex items-center gap-2">
{#if !converters.length} {#if !converters.length}
<Tooltip text="Unknown file type" position="bottom"> <Tooltip text={m["convert.tooltips.unknown_file"]()} position="bottom">
<FileQuestionIcon size="24" class="flex-shrink-0" /> <FileQuestionIcon size="24" class="flex-shrink-0" />
</Tooltip> </Tooltip>
{:else if isAudio} {:else if isAudio}
<Tooltip text="Audio file" position="bottom"> <Tooltip text={m["convert.tooltips.audio_file"]()} position="bottom">
<AudioLines size="24" class="flex-shrink-0" /> <AudioLines size="24" class="flex-shrink-0" />
</Tooltip> </Tooltip>
{:else if isVideo} {:else if isVideo}
<Tooltip text="Video file" position="bottom"> <Tooltip text={m["convert.tooltips.video_file"]()} position="bottom">
<FilmIcon size="24" class="flex-shrink-0" /> <FilmIcon size="24" class="flex-shrink-0" />
</Tooltip> </Tooltip>
{:else if isDocument} {:else if isDocument}
<Tooltip text="Document file" position="bottom"> <Tooltip text={m["convert.tooltips.document_file"]()} position="bottom">
<BookText size="24" class="flex-shrink-0" /> <BookText size="24" class="flex-shrink-0" />
</Tooltip> </Tooltip>
{:else} {:else}
<Tooltip text="Image file" position="bottom"> <Tooltip text={m["convert.tooltips.image_file"]()} position="bottom">
<ImageIcon size="24" class="flex-shrink-0" /> <ImageIcon size="24" class="flex-shrink-0" />
</Tooltip> </Tooltip>
{/if} {/if}
@ -172,11 +184,10 @@
class="h-full flex flex-col text-center justify-center text-failure" class="h-full flex flex-col text-center justify-center text-failure"
> >
<p class="font-body font-bold"> <p class="font-body font-bold">
We can't convert this file. {m["convert.errors.cant_convert"]()}
</p> </p>
<p class="font-normal"> <p class="font-normal">
what are you doing..? you're supposed to run the vertd {m["convert.errors.vertd_server"]()}
server!
</p> </p>
</div> </div>
{:else} {:else}
@ -184,11 +195,10 @@
class="h-full flex flex-col text-center justify-center text-failure" class="h-full flex flex-col text-center justify-center text-failure"
> >
<p class="font-body font-bold"> <p class="font-body font-bold">
We can't convert this file. {m["convert.errors.cant_convert"]()}
</p> </p>
<p class="font-normal"> <p class="font-normal">
Only image, video, audio, and document files are {m["convert.errors.unsupported_format"]()}
supported
</p> </p>
</div> </div>
{/if} {/if}
@ -196,10 +206,9 @@
<div <div
class="h-full flex flex-col text-center justify-center text-failure" class="h-full flex flex-col text-center justify-center text-failure"
> >
<p class="font-body font-bold">We can't convert this file.</p> <p class="font-body font-bold">{m["convert.errors.cant_convert"]()}</p>
<p class="font-normal"> <p class="font-normal">
Could not find the vertd instance to start video conversion. {m["convert.errors.vertd_not_found"]()}
Are you sure the instance URL is set correctly?
</p> </p>
</div> </div>
{:else} {:else}
@ -251,7 +260,7 @@
onselect={(option) => handleSelect(option, file)} onselect={(option) => handleSelect(option, file)}
/> />
<div class="w-full flex items-center justify-between"> <div class="w-full flex items-center justify-between">
<Tooltip text="Convert this file" position="bottom"> <Tooltip text={m["convert.tooltips.convert_file"]()} position="bottom">
<button <button
class="btn {$effects class="btn {$effects
? '' ? ''
@ -269,7 +278,7 @@
</button> </button>
</Tooltip> </Tooltip>
<Tooltip <Tooltip
text="Download this file" text={m["convert.tooltips.download_file"]()}
position="bottom" position="bottom"
> >
<button <button

View File

@ -5,6 +5,7 @@
import { files } from "$lib/store/index.svelte"; import { files } from "$lib/store/index.svelte";
import { quintOut } from "svelte/easing"; import { quintOut } from "svelte/easing";
import { blur } from "svelte/transition"; import { blur } from "svelte/transition";
import { m } from "$lib/paraglide/messages";
const images = $derived( const images = $derived(
files.files.filter((f) => files.files.filter((f) =>
@ -31,9 +32,9 @@
</script> </script>
<div class="mx-auto w-full max-w-[778px] flex flex-col gap-8"> <div class="mx-auto w-full max-w-[778px] flex flex-col gap-8">
<h1 class="text-5xl text-center">SECRET JPEGIFY!!!</h1> <h1 class="text-5xl text-center">{m["jpegify.title"]()}</h1>
<p class="text-muted text-center -mt-4 font-normal italic"> <p class="text-muted text-center -mt-4 font-normal italic">
(shh... don't tell anyone!) {m["jpegify.subtitle"]()}
</p> </p>
<Uploader class="w-full h-64" jpegify={true} /> <Uploader class="w-full h-64" jpegify={true} />
<input <input
@ -49,7 +50,7 @@
onclick={jpegify} onclick={jpegify}
disabled={processing} disabled={processing}
class="btn bg-accent text-black rounded-2xl text-2xl w-full mx-auto" class="btn bg-accent text-black rounded-2xl text-2xl w-full mx-auto"
>JPEGIFY {compressionInverted}%!!!</button >{m["jpegify.button"]({ compression: compressionInverted })}</button
> >
<div class="flex flex-wrap flex-row justify-center gap-4"> <div class="flex flex-wrap flex-row justify-center gap-4">
{#each images as file, i (file.id)} {#each images as file, i (file.id)}
@ -89,7 +90,7 @@
disabled={!!!file.result} disabled={!!!file.result}
class="btn bg-accent text-black rounded-2xl text-2xl w-full mx-auto" class="btn bg-accent text-black rounded-2xl text-2xl w-full mx-auto"
> >
Download {m["jpegify.download"]()}
</button> </button>
<button <button
onclick={() => { onclick={() => {
@ -103,7 +104,7 @@
}} }}
class="btn border-accent-red border-2 bg-transparent text-black dynadark:text-white rounded-2xl text-2xl w-full mx-auto" class="btn border-accent-red border-2 bg-transparent text-black dynadark:text-white rounded-2xl text-2xl w-full mx-auto"
> >
Delete {m["jpegify.delete"]()}
</button> </button>
</div> </div>
</Panel> </Panel>

View File

@ -6,6 +6,7 @@
import { PUB_PLAUSIBLE_URL } from "$env/static/public"; import { PUB_PLAUSIBLE_URL } from "$env/static/public";
import { SettingsIcon } from "lucide-svelte"; import { SettingsIcon } from "lucide-svelte";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { m } from "$lib/paraglide/messages";
let settings = $state(Settings.Settings.instance.settings); let settings = $state(Settings.Settings.instance.settings);
@ -31,7 +32,7 @@
log(["settings"], "saving settings"); log(["settings"], "saving settings");
} catch (error) { } catch (error) {
log(["settings", "error"], `failed to save settings: ${error}`); log(["settings", "error"], `failed to save settings: ${error}`);
addToast("error", "Failed to save settings!"); addToast("error", m["settings.errors.save_failed"]());
} }
}); });
@ -51,7 +52,7 @@
<div class="flex flex-col h-full items-center"> <div class="flex flex-col h-full items-center">
<h1 class="hidden md:block text-[40px] tracking-tight leading-[72px] mb-6"> <h1 class="hidden md:block text-[40px] tracking-tight leading-[72px] mb-6">
<SettingsIcon size="40" class="inline-block -mt-2 mr-2" /> <SettingsIcon size="40" class="inline-block -mt-2 mr-2" />
Settings {m["settings.title"]()}
</h1> </h1>
<div <div

View File

@ -1,3 +1,4 @@
import { paraglideVitePlugin } from "@inlang/paraglide-js";
import { sveltekit } from "@sveltejs/kit/vite"; import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig, type PluginOption } from "vite"; import { defineConfig, type PluginOption } from "vite";
import svg from "@poppanator/sveltekit-svg"; import svg from "@poppanator/sveltekit-svg";
@ -6,6 +7,11 @@ import wasm from "vite-plugin-wasm";
export default defineConfig(({ command }) => { export default defineConfig(({ command }) => {
const plugins: PluginOption[] = [ const plugins: PluginOption[] = [
sveltekit(), sveltekit(),
paraglideVitePlugin({
project: "./project.inlang",
outdir: "./src/lib/paraglide",
strategy: ["localStorage", "preferredLanguage", "baseLocale"],
}),
svg({ svg({
includePaths: ["./src/lib/assets"], includePaths: ["./src/lib/assets"],
svgoOptions: { svgoOptions: {