diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d41a9404 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.gif filter=lfs diff=lfs merge=lfs -text diff --git a/package.json b/package.json index d2ed7734..e4cdb623 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "format": "prettier . --write .", "clean": "rm -rf ./node_modules/ && rm -rf ./.svelte-kit/ && ni && echo 'Project cleaned!'", "update-ctx-length": "jiti scripts/update-ctx-length.ts", + "download-images": "jiti scripts/download-model-images.ts", "test:unit": "vitest", "test": "npm run test:unit -- --run && npm run test:e2e", "test:e2e": "playwright test" @@ -26,7 +27,9 @@ "@huggingface/tasks": "^0.19.8", "@huggingface/transformers": "^3.5.1", "@iconify-json/carbon": "^1.2.8", + "@iconify-json/lucide": "^1.2.45", "@iconify-json/material-symbols": "^1.2.15", + "@iconify-json/tabler": "^1.2.18", "@playwright/test": "^1.49.1", "@ryoppippi/unplugin-typia": "^1.0.0", "@samchon/openapi": "^3.0.0", @@ -35,9 +38,10 @@ "@sveltejs/kit": "^2.5.27", "@sveltejs/vite-plugin-svelte": "^4.0.0", "@tailwindcss/container-queries": "^0.1.1", - "@tailwindcss/postcss": "^4.0.9", + "@tailwindcss/postcss": "^4.1.8", "@testing-library/jest-dom": "^6.6.3", "@testing-library/svelte": "^5.2.4", + "@types/chroma-js": "^3.1.1", "@types/node": "^22.14.1", "@vitest/browser": "^3.1.4", "clsx": "^2.1.1", @@ -45,12 +49,13 @@ "eslint": "^9.22.0", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-svelte": "^3.6.0", "fake-indexeddb": "^6.0.1", "globals": "^16.0.0", "highlight.js": "^11.10.0", "jiti": "^2.4.2", "jsdom": "^26.0.0", - "melt": "^0.30.1", + "melt": "^0.36.0", "openai": "^4.90.0", "playwright": "^1.52.0", "postcss": "^8.4.38", @@ -59,10 +64,10 @@ "prettier-plugin-tailwindcss": "^0.6.11", "runed": "^0.25.0", "shiki": "^3.4.0", - "svelte": "^5.30.1", + "svelte": "^5.34.8", "svelte-check": "^4.0.0", "tailwind-merge": "^3.0.2", - "tailwindcss": "^4.0.9", + "tailwindcss": "^4.1.8", "ts-patch": "^3.3.0", "tslib": "^2.4.1", "typescript": "^5.8.2", @@ -74,10 +79,12 @@ }, "type": "module", "dependencies": { + "chroma-js": "^3.1.2", "dequal": "^2.0.3", - "eslint-plugin-svelte": "^3.6.0", + "node-vibrant": "^4.0.3", "remult": "^3.0.2", - "typia": "^8.0.0" + "typia": "^8.0.0", + "vaul-svelte": "https://pkg.vc/-/@melt-ui/vaul-svelte@9195c05" }, "pnpm": { "onlyBuiltDependencies": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ec5cdf0..44d195c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,18 +8,24 @@ importers: .: dependencies: + chroma-js: + specifier: ^3.1.2 + version: 3.1.2 dequal: specifier: ^2.0.3 version: 2.0.3 - eslint-plugin-svelte: - specifier: ^3.6.0 - version: 3.6.0(eslint@9.22.0(jiti@2.4.2))(svelte@5.30.1) + node-vibrant: + specifier: ^4.0.3 + version: 4.0.3 remult: specifier: ^3.0.2 version: 3.0.2 typia: specifier: ^8.0.0 version: 8.0.0(@samchon/openapi@3.0.0)(typescript@5.8.2) + vaul-svelte: + specifier: https://pkg.vc/-/@melt-ui/vaul-svelte@9195c05 + version: https://pkg.vc/-/@melt-ui/vaul-svelte@9195c05(svelte@5.34.8) devDependencies: '@eslint/eslintrc': specifier: ^3.3.0 @@ -45,48 +51,57 @@ importers: '@iconify-json/carbon': specifier: ^1.2.8 version: 1.2.8 + '@iconify-json/lucide': + specifier: ^1.2.45 + version: 1.2.45 '@iconify-json/material-symbols': specifier: ^1.2.15 version: 1.2.15 + '@iconify-json/tabler': + specifier: ^1.2.18 + version: 1.2.18 '@playwright/test': specifier: ^1.49.1 version: 1.52.0 '@ryoppippi/unplugin-typia': specifier: ^1.0.0 - version: 1.2.0(@samchon/openapi@3.0.0)(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.34.9)(yaml@2.7.0) + version: 1.2.0(@samchon/openapi@3.0.0)(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.34.9)(yaml@2.7.0) '@samchon/openapi': specifier: ^3.0.0 version: 3.0.0 '@sveltejs/adapter-auto': specifier: ^3.2.2 - version: 3.3.1(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))) + version: 3.3.1(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))) '@sveltejs/adapter-node': specifier: ^5.2.0 - version: 5.2.12(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))) + version: 5.2.12(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))) '@sveltejs/kit': specifier: ^2.5.27 - version: 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + version: 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)) '@sveltejs/vite-plugin-svelte': specifier: ^4.0.0 - version: 4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + version: 4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)) '@tailwindcss/container-queries': specifier: ^0.1.1 - version: 0.1.1(tailwindcss@4.0.9) + version: 0.1.1(tailwindcss@4.1.8) '@tailwindcss/postcss': - specifier: ^4.0.9 - version: 4.0.9 + specifier: ^4.1.8 + version: 4.1.8 '@testing-library/jest-dom': specifier: ^6.6.3 version: 6.6.3 '@testing-library/svelte': specifier: ^5.2.4 - version: 5.2.8(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4) + version: 5.2.8(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))(vitest@3.1.4) + '@types/chroma-js': + specifier: ^3.1.1 + version: 3.1.1 '@types/node': specifier: ^22.14.1 version: 22.14.1 '@vitest/browser': specifier: ^3.1.4 - version: 3.1.4(playwright@1.52.0)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4) + version: 3.1.4(playwright@1.52.0)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))(vitest@3.1.4) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -102,6 +117,9 @@ importers: eslint-plugin-prettier: specifier: ^5.2.3 version: 5.2.3(eslint-config-prettier@10.1.1(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))(prettier@3.5.3) + eslint-plugin-svelte: + specifier: ^3.6.0 + version: 3.6.0(eslint@9.22.0(jiti@2.4.2))(svelte@5.34.8) fake-indexeddb: specifier: ^6.0.1 version: 6.0.1 @@ -118,8 +136,8 @@ importers: specifier: ^26.0.0 version: 26.1.0 melt: - specifier: ^0.30.1 - version: 0.30.1(@floating-ui/dom@1.6.13)(svelte@5.30.1) + specifier: ^0.36.0 + version: 0.36.0(@floating-ui/dom@1.6.13)(svelte@5.34.8) openai: specifier: ^4.90.0 version: 4.90.0(ws@8.18.2) @@ -134,28 +152,28 @@ importers: version: 3.5.3 prettier-plugin-svelte: specifier: ^3.4.0 - version: 3.4.0(prettier@3.5.3)(svelte@5.30.1) + version: 3.4.0(prettier@3.5.3)(svelte@5.34.8) prettier-plugin-tailwindcss: specifier: ^0.6.11 - version: 0.6.11(prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.30.1))(prettier@3.5.3) + version: 0.6.11(prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.34.8))(prettier@3.5.3) runed: specifier: ^0.25.0 - version: 0.25.0(svelte@5.30.1) + version: 0.25.0(svelte@5.34.8) shiki: specifier: ^3.4.0 version: 3.4.0 svelte: - specifier: ^5.30.1 - version: 5.30.1 + specifier: ^5.34.8 + version: 5.34.8 svelte-check: specifier: ^4.0.0 - version: 4.1.5(picomatch@4.0.2)(svelte@5.30.1)(typescript@5.8.2) + version: 4.1.5(picomatch@4.0.2)(svelte@5.34.8)(typescript@5.8.2) tailwind-merge: specifier: ^3.0.2 version: 3.0.2 tailwindcss: - specifier: ^4.0.9 - version: 4.0.9 + specifier: ^4.1.8 + version: 4.1.8 ts-patch: specifier: ^3.3.0 version: 3.3.0 @@ -170,16 +188,16 @@ importers: version: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) unplugin-icons: specifier: ^22.1.0 - version: 22.1.0(svelte@5.30.1) + version: 22.1.0(svelte@5.34.8) vite: specifier: ^5.4.4 - version: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) + version: 5.4.19(@types/node@22.14.1)(lightningcss@1.30.1) vitest: specifier: ^3.0.0 - version: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.29.1) + version: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.30.1) vitest-browser-svelte: specifier: ^0.1.0 - version: 0.1.0(@vitest/browser@3.1.4)(svelte@5.30.1)(vitest@3.1.4) + version: 0.1.0(@vitest/browser@3.1.4)(svelte@5.34.8)(vitest@3.1.4) packages: @@ -627,9 +645,15 @@ packages: '@iconify-json/carbon@1.2.8': resolution: {integrity: sha512-6xh4YiFBz6qoSnB3XMe23WvjTJroDFXB17J1MbiT7nATFe+70+em1acRXr8hgP/gYpwFMHFc4IvjA/IPTPnTzg==} + '@iconify-json/lucide@1.2.45': + resolution: {integrity: sha512-izW3wk7Ll5HgNMc/m41eOKXb7nL1zFxXZdMyBOtqdwusHXPr8IWSC/ReSgWgxUF3xyNwiQsso5Ppkta2wsUrPg==} + '@iconify-json/material-symbols@1.2.15': resolution: {integrity: sha512-KkHRnMh1s08N1Olf3xk+z3ZIrke/7Ys3uUIMfKuSkZPbNssG4IApKkJOV5po6mg6oxMooXdNpab4PS0S5LMSOA==} + '@iconify-json/tabler@1.2.18': + resolution: {integrity: sha512-W+8qiJhJpb4dmBw3P7JSM9QhGsFG8GIS3BJWAmrJ/92rzK6NPGUOPfGmswoO+/MuPzQV96ColY9lcUktUKv0pg==} + '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -752,6 +776,9 @@ packages: cpu: [x64] os: [win32] + '@internationalized/date@3.8.2': + resolution: {integrity: sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==} + '@isaacs/fs-minipass@4.0.1': resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} @@ -760,6 +787,50 @@ packages: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jimp/bmp@0.22.12': + resolution: {integrity: sha512-aeI64HD0npropd+AR76MCcvvRaa+Qck6loCOS03CkkxGHN5/r336qTM5HPUdHKMDOGzqknuVPA8+kK1t03z12g==} + peerDependencies: + '@jimp/custom': '>=0.3.5' + + '@jimp/core@0.22.12': + resolution: {integrity: sha512-l0RR0dOPyzMKfjUW1uebzueFEDtCOj9fN6pyTYWWOM/VS4BciXQ1VVrJs8pO3kycGYZxncRKhCoygbNr8eEZQA==} + + '@jimp/custom@0.22.12': + resolution: {integrity: sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==} + + '@jimp/gif@0.22.12': + resolution: {integrity: sha512-y6BFTJgch9mbor2H234VSjd9iwAhaNf/t3US5qpYIs0TSbAvM02Fbc28IaDETj9+4YB4676sz4RcN/zwhfu1pg==} + peerDependencies: + '@jimp/custom': '>=0.3.5' + + '@jimp/jpeg@0.22.12': + resolution: {integrity: sha512-Rq26XC/uQWaQKyb/5lksCTCxXhtY01NJeBN+dQv5yNYedN0i7iYu+fXEoRsfaJ8xZzjoANH8sns7rVP4GE7d/Q==} + peerDependencies: + '@jimp/custom': '>=0.3.5' + + '@jimp/plugin-resize@0.22.12': + resolution: {integrity: sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==} + peerDependencies: + '@jimp/custom': '>=0.3.5' + + '@jimp/png@0.22.12': + resolution: {integrity: sha512-Mrp6dr3UTn+aLK8ty/dSKELz+Otdz1v4aAXzV5q53UDD2rbB5joKVJ/ChY310B+eRzNxIovbUF1KVrUsYdE8Hg==} + peerDependencies: + '@jimp/custom': '>=0.3.5' + + '@jimp/tiff@0.22.12': + resolution: {integrity: sha512-E1LtMh4RyJsoCAfAkBRVSYyZDTtLq9p9LUiiYP0vPtXyxX4BiYBUYihTLSBlCQg5nF2e4OpQg7SPrLdJ66u7jg==} + peerDependencies: + '@jimp/custom': '>=0.3.5' + + '@jimp/types@0.22.12': + resolution: {integrity: sha512-wwKYzRdElE1MBXFREvCto5s699izFHNVvALUv79GXNbsOVqlwlOxlWJ8DuyOGIXoLP4JW/m30YyuTtfUJgMRMA==} + peerDependencies: + '@jimp/custom': '>=0.3.5' + + '@jimp/utils@0.22.12': + resolution: {integrity: sha512-yJ5cWUknGnilBq97ZXOyOS0HhsHOyAyjHwYfHxGbSyMTohgQI6sVyE8KPgDwH8HHW/nMKXk8TrSwAE71zt716Q==} + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -1039,86 +1110,101 @@ packages: svelte: ^5.0.0-next.96 || ^5.0.0 vite: ^5.0.0 + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + '@tailwindcss/container-queries@0.1.1': resolution: {integrity: sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==} peerDependencies: tailwindcss: '>=3.2.0' - '@tailwindcss/node@4.0.9': - resolution: {integrity: sha512-tOJvdI7XfJbARYhxX+0RArAhmuDcczTC46DGCEziqxzzbIaPnfYaIyRT31n4u8lROrsO7Q6u/K9bmQHL2uL1bQ==} + '@tailwindcss/node@4.1.8': + resolution: {integrity: sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==} - '@tailwindcss/oxide-android-arm64@4.0.9': - resolution: {integrity: sha512-YBgy6+2flE/8dbtrdotVInhMVIxnHJPbAwa7U1gX4l2ThUIaPUp18LjB9wEH8wAGMBZUb//SzLtdXXNBHPUl6Q==} + '@tailwindcss/oxide-android-arm64@4.1.8': + resolution: {integrity: sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.0.9': - resolution: {integrity: sha512-pWdl4J2dIHXALgy2jVkwKBmtEb73kqIfMpYmcgESr7oPQ+lbcQ4+tlPeVXaSAmang+vglAfFpXQCOvs/aGSqlw==} + '@tailwindcss/oxide-darwin-arm64@4.1.8': + resolution: {integrity: sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.0.9': - resolution: {integrity: sha512-4Dq3lKp0/C7vrRSkNPtBGVebEyWt9QPPlQctxJ0H3MDyiQYvzVYf8jKow7h5QkWNe8hbatEqljMj/Y0M+ERYJg==} + '@tailwindcss/oxide-darwin-x64@4.1.8': + resolution: {integrity: sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.0.9': - resolution: {integrity: sha512-k7U1RwRODta8x0uealtVt3RoWAWqA+D5FAOsvVGpYoI6ObgmnzqWW6pnVwz70tL8UZ/QXjeMyiICXyjzB6OGtQ==} + '@tailwindcss/oxide-freebsd-x64@4.1.8': + resolution: {integrity: sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.9': - resolution: {integrity: sha512-NDDjVweHz2zo4j+oS8y3KwKL5wGCZoXGA9ruJM982uVJLdsF8/1AeKvUwKRlMBpxHt1EdWJSAh8a0Mfhl28GlQ==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.8': + resolution: {integrity: sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.0.9': - resolution: {integrity: sha512-jk90UZ0jzJl3Dy1BhuFfRZ2KP9wVKMXPjmCtY4U6fF2LvrjP5gWFJj5VHzfzHonJexjrGe1lMzgtjriuZkxagg==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.8': + resolution: {integrity: sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.0.9': - resolution: {integrity: sha512-3eMjyTC6HBxh9nRgOHzrc96PYh1/jWOwHZ3Kk0JN0Kl25BJ80Lj9HEvvwVDNTgPg154LdICwuFLuhfgH9DULmg==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.8': + resolution: {integrity: sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.0.9': - resolution: {integrity: sha512-v0D8WqI/c3WpWH1kq/HP0J899ATLdGZmENa2/emmNjubT0sWtEke9W9+wXeEoACuGAhF9i3PO5MeyditpDCiWQ==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.8': + resolution: {integrity: sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.0.9': - resolution: {integrity: sha512-Kvp0TCkfeXyeehqLJr7otsc4hd/BUPfcIGrQiwsTVCfaMfjQZCG7DjI+9/QqPZha8YapLA9UoIcUILRYO7NE1Q==} + '@tailwindcss/oxide-linux-x64-musl@4.1.8': + resolution: {integrity: sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-win32-arm64-msvc@4.0.9': - resolution: {integrity: sha512-m3+60T/7YvWekajNq/eexjhV8z10rswcz4BC9bioJ7YaN+7K8W2AmLmG0B79H14m6UHE571qB0XsPus4n0QVgQ==} + '@tailwindcss/oxide-wasm32-wasi@4.1.8': + resolution: {integrity: sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.8': + resolution: {integrity: sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.0.9': - resolution: {integrity: sha512-dpc05mSlqkwVNOUjGu/ZXd5U1XNch1kHFJ4/cHkZFvaW1RzbHmRt24gvM8/HC6IirMxNarzVw4IXVtvrOoZtxA==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.8': + resolution: {integrity: sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.0.9': - resolution: {integrity: sha512-eLizHmXFqHswJONwfqi/WZjtmWZpIalpvMlNhTM99/bkHtUs6IqgI1XQ0/W5eO2HiRQcIlXUogI2ycvKhVLNcA==} + '@tailwindcss/oxide@4.1.8': + resolution: {integrity: sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==} engines: {node: '>= 10'} - '@tailwindcss/postcss@4.0.9': - resolution: {integrity: sha512-BT/E+pdMqulavEAVM5NCpxmGEwHiLDPpkmg/c/X25ZBW+izTe+aZ+v1gf/HXTrihRoCxrUp5U4YyHsBTzspQKQ==} + '@tailwindcss/postcss@4.1.8': + resolution: {integrity: sha512-vB/vlf7rIky+w94aWMw34bWW1ka6g6C3xIOdICKX2GC0VcLtL6fhlLiafF0DVIwa9V6EHz8kbWMkS2s2QvvNlw==} '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} @@ -1147,9 +1233,15 @@ packages: peerDependencies: '@testing-library/dom': '>=7.21.4' + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/chroma-js@3.1.1': + resolution: {integrity: sha512-SFCr4edNkZ1bGaLzGz7rgR1bRzVX4MmMxwsIa3/Bh6ose8v+hRpneoizHv0KChdjxaXyjRtaMq7sCuZSzPomQA==} + '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -1168,6 +1260,9 @@ packages: '@types/node-fetch@2.6.12': resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + '@types/node@16.9.1': + resolution: {integrity: sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==} + '@types/node@18.19.84': resolution: {integrity: sha512-ACYy2HGcZPHxEeWTqowTF7dhXN+JU1o7Gr4b41klnn6pj2LD6rsiGqSZojMdk1Jh2ys3m76ap+ae1vvE4+5+vg==} @@ -1230,6 +1325,39 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@vibrant/color@4.0.0': + resolution: {integrity: sha512-S9ItdqS1135wTXoIIqAJu8df9dqlOo6Boc5Y4MGsBTu9UmUOvOwfj5b4Ga6S5yrLAKmKYIactkz7zYJdMddkig==} + + '@vibrant/core@4.0.0': + resolution: {integrity: sha512-fqlVRUTDjEws9VNKvI3cDXM4wUT7fMFS+cVqEjJk3im+R5EvjJzPF6OAbNhfPzW04NvHNE555eY9FfhYuX3PRw==} + + '@vibrant/generator-default@4.0.3': + resolution: {integrity: sha512-HZlfp19sDokODEkZF4p70QceARHgjP3a1Dmxg+dlblYMJM98jPq+azA0fzqKNR7R17JJNHxexpJEepEsNlG0gw==} + + '@vibrant/generator@4.0.0': + resolution: {integrity: sha512-CqKAjmgHVDXJVo3Q5+9pUJOvksR7cN3bzx/6MbURYh7lA4rhsIewkUK155M6q0vfcUN3ETi/eTneCi0tLuM2Sg==} + + '@vibrant/image-browser@4.0.0': + resolution: {integrity: sha512-mXckzvJWiP575Y/wNtP87W/TPgyJoGlPBjW4E9YmNS6n4Jb6RqyHQA0ZVulqDslOxjSsihDzY7gpAORRclaoLg==} + + '@vibrant/image-node@4.0.0': + resolution: {integrity: sha512-m7yfnQtmo2y8z+tOjRFBx6q/qGnhl/ax2uCaj4TBkm4TtXfR4Dsn90wT6OWXmCFFzxIKHXKKEBShkxR+4RHseA==} + + '@vibrant/image@4.0.0': + resolution: {integrity: sha512-Asv/7R/L701norosgvbjOVkodFiwcFihkXixA/gbAd6C+5GCts1Wm1NPk14FNKnM7eKkfAN+0wwPkdOH+PY/YA==} + + '@vibrant/quantizer-mmcq@4.0.0': + resolution: {integrity: sha512-TZqNiRoGGyCP8fH1XE6rvhFwLNv9D8MP1Xhz3K8tsuUweC6buWax3qLfrfEnkhtQnPJHaqvTfTOlIIXVMfRpow==} + + '@vibrant/quantizer@4.0.0': + resolution: {integrity: sha512-YDGxmCv/RvHFtZghDlVRwH5GMxdGGozWS1JpUOUt73/F5zAKGiiier8F31K1npIXARn6/Gspvg/Rbg7qqyEr2A==} + + '@vibrant/types@4.0.0': + resolution: {integrity: sha512-tA5TAbuROXcPkt+PWjmGfoaiEXyySVaNnCZovf6vXhCbMdrTTCQXvNCde2geiVl6YwtuU/Qrj9iZxS5jZ6yVIw==} + + '@vibrant/worker@4.0.0': + resolution: {integrity: sha512-nSaZZwWQKOgN/nPYUAIRF0/uoa7KpK91A+gjLmZZDgfN1enqxaiihmn+75ayNadW0c6cxAEpEFEHTONR5u9tMw==} + '@vitest/browser@3.1.4': resolution: {integrity: sha512-2L4vR/tuUZBxKU72Qe+unIp1P8lZ0T5nlqPegkXxyZFR5gWqItV8VPPR261GOzl49Zw2AhzMABzMMHJagQ0a2g==} peerDependencies: @@ -1315,6 +1443,9 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + any-base@1.1.0: + resolution: {integrity: sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1349,9 +1480,18 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + bits-ui@1.8.0: + resolution: {integrity: sha512-CXD6Orp7l8QevNDcRPLXc/b8iMVgxDWT2LyTwsdLzJKh9CxesOmPuNePSPqAxKoT59FIdU4aFPS1k7eBdbaCxg==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.11.0 + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bmp-js@0.1.0: + resolution: {integrity: sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==} + boolean@3.2.0: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. @@ -1369,6 +1509,9 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1417,6 +1560,9 @@ packages: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} + chroma-js@3.1.2: + resolution: {integrity: sha512-IJnETTalXbsLx1eKEgx19d5L6SRM7cH4vINw/99p/M11HCuXGRWL+6YmCm7FWFGIo6dtWuQoQi1dc5yQ7ESIHg==} + cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -1557,11 +1703,6 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - detect-libc@1.0.3: - resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} - engines: {node: '>=0.10'} - hasBin: true - detect-libc@2.0.4: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} @@ -1719,8 +1860,8 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} - esrap@1.4.6: - resolution: {integrity: sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==} + esrap@1.4.9: + resolution: {integrity: sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -1744,6 +1885,13 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + exif-parser@0.1.12: + resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==} + expect-type@1.2.1: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} @@ -1802,6 +1950,10 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + file-type@16.5.4: + resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} + engines: {node: '>=10'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1860,6 +2012,9 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + gifwrap@0.10.1: + resolution: {integrity: sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1971,6 +2126,9 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + image-q@4.0.0: + resolution: {integrity: sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1993,6 +2151,9 @@ packages: resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + inquirer@8.2.6: resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} @@ -2047,6 +2208,9 @@ packages: resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} engines: {node: '>=16'} + isomorphic-fetch@3.0.0: + resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} + jest-axe@9.0.0: resolution: {integrity: sha512-Xt7O0+wIpW31lv0SO1wQZUTyJE7DEmnDEZeTt9/S9L5WUywxrv8BrgvTuQEqujtfaQOcJ70p4wg7UUgK1E2F5g==} engines: {node: '>= 16.0.0'} @@ -2067,6 +2231,9 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true + jpeg-js@0.4.4: + resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2116,68 +2283,68 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lightningcss-darwin-arm64@1.29.1: - resolution: {integrity: sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==} + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.29.1: - resolution: {integrity: sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==} + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.29.1: - resolution: {integrity: sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==} + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.29.1: - resolution: {integrity: sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==} + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.29.1: - resolution: {integrity: sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==} + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-arm64-musl@1.29.1: - resolution: {integrity: sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==} + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-x64-gnu@1.29.1: - resolution: {integrity: sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==} + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-linux-x64-musl@1.29.1: - resolution: {integrity: sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==} + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-win32-arm64-msvc@1.29.1: - resolution: {integrity: sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==} + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.29.1: - resolution: {integrity: sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==} + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.29.1: - resolution: {integrity: sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==} + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} lilconfig@2.1.0: @@ -2236,11 +2403,11 @@ packages: mdast-util-to-hast@13.2.0: resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} - melt@0.30.1: - resolution: {integrity: sha512-Z3X3IMknWSbXFlzQA6On18kdGf1a+Kgqu/TxxvchjGGiS3RINd96PrlLU2Bl/SOxF+UWLLYmH1fohwiMz9UsQQ==} + melt@0.36.0: + resolution: {integrity: sha512-lJdUuPvsCZs7zpcL2iSvxerHxv3QuM91FoTbdsliOQ2+J3fR4ADqUN878J4kkQSzzHlWqyedQmEBDP6U3iEWgA==} peerDependencies: '@floating-ui/dom': ^1.6.0 - svelte: ^5.0.0 + svelte: ^5.30.1 merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -2347,6 +2514,9 @@ packages: encoding: optional: true + node-vibrant@4.0.3: + resolution: {integrity: sha512-kzoIuJK90BH/k65Avt077JCX4Nhqz1LNc8cIOm2rnYEvFdJIYd8b3SQwU1MTpzcHtr8z8jxkl1qdaCfbP3olFg==} + nwsapi@2.2.20: resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} @@ -2354,6 +2524,9 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + omggif@1.0.10: + resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -2420,6 +2593,9 @@ packages: package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2452,6 +2628,10 @@ packages: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} + peek-readable@4.1.0: + resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} + engines: {node: '>=8'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2463,6 +2643,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + pixelmatch@4.0.2: + resolution: {integrity: sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==} + hasBin: true + pkg-dir@7.0.0: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} @@ -2486,6 +2670,14 @@ packages: engines: {node: '>=18'} hasBin: true + pngjs@3.4.0: + resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} + engines: {node: '>=4.0.0'} + + pngjs@6.0.0: + resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} + engines: {node: '>=12.13.0'} + postcss-load-config@3.1.4: resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} @@ -2600,6 +2792,10 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + property-information@7.0.0: resolution: {integrity: sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==} @@ -2631,6 +2827,14 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readable-web-to-node-stream@3.0.4: + resolution: {integrity: sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==} + engines: {node: '>=8'} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -2642,6 +2846,9 @@ packages: reflect-metadata@0.1.14: resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -2811,6 +3018,13 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strtok3@6.3.0: + resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} + engines: {node: '>=10'} + + style-to-object@1.0.9: + resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2836,8 +3050,14 @@ packages: svelte: optional: true - svelte@5.30.1: - resolution: {integrity: sha512-QIYtKnJGkubWXtNkrUBKVCvyo9gjcccdbnvXfwsGNhvbeNNdQjRDTa/BiQcJ2kWXbXPQbWKyT7CUu53KIj1rfw==} + svelte-toolbelt@0.7.1: + resolution: {integrity: sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.0.0 + + svelte@5.34.8: + resolution: {integrity: sha512-TF+8irl7rpj3+fpaLuPRX5BqReTAqckp0Fumxa/mCeK3fo0/MnBb9W/Z2bLwtqj3C3r5Lm6NKIAw7YrgIv1Fwg==} engines: {node: '>=18'} symbol-tree@3.2.4: @@ -2847,11 +3067,14 @@ packages: resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} engines: {node: ^14.18.0 || >=16.0.0} + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tailwind-merge@3.0.2: resolution: {integrity: sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==} - tailwindcss@4.0.9: - resolution: {integrity: sha512-12laZu+fv1ONDRoNR9ipTOpUD7RN9essRVkX36sjxuRUInpN7hIiHN4lBd/SIFjbISvnXzp8h/hXzmU8SQQYhw==} + tailwindcss@4.1.8: + resolution: {integrity: sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==} tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} @@ -2864,9 +3087,15 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + timm@1.7.1: + resolution: {integrity: sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} @@ -2901,6 +3130,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + token-types@4.2.1: + resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} + engines: {node: '>=10'} + totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -3037,6 +3270,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + utif2@4.1.0: + resolution: {integrity: sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -3044,6 +3280,13 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true + vaul-svelte@https://pkg.vc/-/@melt-ui/vaul-svelte@9195c05: + resolution: {tarball: https://pkg.vc/-/@melt-ui/vaul-svelte@9195c05} + version: 1.0.0-next.7 + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.0.0 + vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} @@ -3055,8 +3298,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@5.4.14: - resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} + vite@5.4.19: + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -3195,6 +3438,9 @@ packages: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} @@ -3574,10 +3820,18 @@ snapshots: dependencies: '@iconify/types': 2.0.0 + '@iconify-json/lucide@1.2.45': + dependencies: + '@iconify/types': 2.0.0 + '@iconify-json/material-symbols@1.2.15': dependencies: '@iconify/types': 2.0.0 + '@iconify-json/tabler@1.2.18': + dependencies: + '@iconify/types': 2.0.0 + '@iconify/types@2.0.0': {} '@iconify/utils@2.3.0': @@ -3674,6 +3928,10 @@ snapshots: '@img/sharp-win32-x64@0.34.2': optional: true + '@internationalized/date@3.8.2': + dependencies: + '@swc/helpers': 0.5.17 + '@isaacs/fs-minipass@4.0.1': dependencies: minipass: 7.1.2 @@ -3682,6 +3940,74 @@ snapshots: dependencies: '@sinclair/typebox': 0.27.8 + '@jimp/bmp@0.22.12(@jimp/custom@0.22.12)': + dependencies: + '@jimp/custom': 0.22.12 + '@jimp/utils': 0.22.12 + bmp-js: 0.1.0 + + '@jimp/core@0.22.12': + dependencies: + '@jimp/utils': 0.22.12 + any-base: 1.1.0 + buffer: 5.7.1 + exif-parser: 0.1.12 + file-type: 16.5.4 + isomorphic-fetch: 3.0.0 + pixelmatch: 4.0.2 + tinycolor2: 1.6.0 + transitivePeerDependencies: + - encoding + + '@jimp/custom@0.22.12': + dependencies: + '@jimp/core': 0.22.12 + transitivePeerDependencies: + - encoding + + '@jimp/gif@0.22.12(@jimp/custom@0.22.12)': + dependencies: + '@jimp/custom': 0.22.12 + '@jimp/utils': 0.22.12 + gifwrap: 0.10.1 + omggif: 1.0.10 + + '@jimp/jpeg@0.22.12(@jimp/custom@0.22.12)': + dependencies: + '@jimp/custom': 0.22.12 + '@jimp/utils': 0.22.12 + jpeg-js: 0.4.4 + + '@jimp/plugin-resize@0.22.12(@jimp/custom@0.22.12)': + dependencies: + '@jimp/custom': 0.22.12 + '@jimp/utils': 0.22.12 + + '@jimp/png@0.22.12(@jimp/custom@0.22.12)': + dependencies: + '@jimp/custom': 0.22.12 + '@jimp/utils': 0.22.12 + pngjs: 6.0.0 + + '@jimp/tiff@0.22.12(@jimp/custom@0.22.12)': + dependencies: + '@jimp/custom': 0.22.12 + utif2: 4.1.0 + + '@jimp/types@0.22.12(@jimp/custom@0.22.12)': + dependencies: + '@jimp/bmp': 0.22.12(@jimp/custom@0.22.12) + '@jimp/custom': 0.22.12 + '@jimp/gif': 0.22.12(@jimp/custom@0.22.12) + '@jimp/jpeg': 0.22.12(@jimp/custom@0.22.12) + '@jimp/png': 0.22.12(@jimp/custom@0.22.12) + '@jimp/tiff': 0.22.12(@jimp/custom@0.22.12) + timm: 1.7.1 + + '@jimp/utils@0.22.12': + dependencies: + regenerator-runtime: 0.13.11 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 @@ -3841,7 +4167,7 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.34.9': optional: true - '@ryoppippi/unplugin-typia@1.2.0(@samchon/openapi@3.0.0)(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.34.9)(yaml@2.7.0)': + '@ryoppippi/unplugin-typia@1.2.0(@samchon/openapi@3.0.0)(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.34.9)(yaml@2.7.0)': dependencies: '@rollup/pluginutils': 5.1.4(rollup@4.34.9) consola: 3.4.0 @@ -3855,7 +4181,7 @@ snapshots: typescript: 5.6.3 typia: 7.6.4(@samchon/openapi@3.0.0)(typescript@5.6.3) unplugin: 1.16.1 - vite: 6.2.1(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0) + vite: 6.2.1(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.7.0) transitivePeerDependencies: - '@samchon/openapi' - '@types/node' @@ -3912,22 +4238,22 @@ snapshots: dependencies: acorn: 8.14.0 - '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))': + '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))': dependencies: - '@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)) import-meta-resolve: 4.1.0 - '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))': + '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))': dependencies: '@rollup/plugin-commonjs': 28.0.2(rollup@4.34.9) '@rollup/plugin-json': 6.1.0(rollup@4.34.9) '@rollup/plugin-node-resolve': 16.0.0(rollup@4.34.9) - '@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)) rollup: 4.34.9 - '@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))': + '@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)) '@types/cookie': 0.6.0 cookie: 0.6.0 devalue: 5.1.1 @@ -3939,96 +4265,110 @@ snapshots: sade: 1.8.1 set-cookie-parser: 2.7.1 sirv: 3.0.1 - svelte: 5.30.1 - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) + svelte: 5.34.8 + vite: 5.4.19(@types/node@22.14.1)(lightningcss@1.30.1) - '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)) debug: 4.4.0 - svelte: 5.30.1 - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) + svelte: 5.34.8 + vite: 5.4.19(@types/node@22.14.1)(lightningcss@1.30.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))': + '@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)))(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)))(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 - svelte: 5.30.1 - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) - vitefu: 1.0.6(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + svelte: 5.34.8 + vite: 5.4.19(@types/node@22.14.1)(lightningcss@1.30.1) + vitefu: 1.0.6(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)) transitivePeerDependencies: - supports-color - '@tailwindcss/container-queries@0.1.1(tailwindcss@4.0.9)': + '@swc/helpers@0.5.17': dependencies: - tailwindcss: 4.0.9 + tslib: 2.8.1 - '@tailwindcss/node@4.0.9': + '@tailwindcss/container-queries@0.1.1(tailwindcss@4.1.8)': dependencies: + tailwindcss: 4.1.8 + + '@tailwindcss/node@4.1.8': + dependencies: + '@ampproject/remapping': 2.3.0 enhanced-resolve: 5.18.1 jiti: 2.4.2 - tailwindcss: 4.0.9 + lightningcss: 1.30.1 + magic-string: 0.30.17 + source-map-js: 1.2.1 + tailwindcss: 4.1.8 + + '@tailwindcss/oxide-android-arm64@4.1.8': + optional: true - '@tailwindcss/oxide-android-arm64@4.0.9': + '@tailwindcss/oxide-darwin-arm64@4.1.8': optional: true - '@tailwindcss/oxide-darwin-arm64@4.0.9': + '@tailwindcss/oxide-darwin-x64@4.1.8': optional: true - '@tailwindcss/oxide-darwin-x64@4.0.9': + '@tailwindcss/oxide-freebsd-x64@4.1.8': optional: true - '@tailwindcss/oxide-freebsd-x64@4.0.9': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.8': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.9': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.8': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.0.9': + '@tailwindcss/oxide-linux-arm64-musl@4.1.8': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.0.9': + '@tailwindcss/oxide-linux-x64-gnu@4.1.8': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.0.9': + '@tailwindcss/oxide-linux-x64-musl@4.1.8': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.0.9': + '@tailwindcss/oxide-wasm32-wasi@4.1.8': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.0.9': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.8': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.0.9': + '@tailwindcss/oxide-win32-x64-msvc@4.1.8': optional: true - '@tailwindcss/oxide@4.0.9': + '@tailwindcss/oxide@4.1.8': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.0.9 - '@tailwindcss/oxide-darwin-arm64': 4.0.9 - '@tailwindcss/oxide-darwin-x64': 4.0.9 - '@tailwindcss/oxide-freebsd-x64': 4.0.9 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.9 - '@tailwindcss/oxide-linux-arm64-gnu': 4.0.9 - '@tailwindcss/oxide-linux-arm64-musl': 4.0.9 - '@tailwindcss/oxide-linux-x64-gnu': 4.0.9 - '@tailwindcss/oxide-linux-x64-musl': 4.0.9 - '@tailwindcss/oxide-win32-arm64-msvc': 4.0.9 - '@tailwindcss/oxide-win32-x64-msvc': 4.0.9 - - '@tailwindcss/postcss@4.0.9': + '@tailwindcss/oxide-android-arm64': 4.1.8 + '@tailwindcss/oxide-darwin-arm64': 4.1.8 + '@tailwindcss/oxide-darwin-x64': 4.1.8 + '@tailwindcss/oxide-freebsd-x64': 4.1.8 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.8 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.8 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.8 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.8 + '@tailwindcss/oxide-linux-x64-musl': 4.1.8 + '@tailwindcss/oxide-wasm32-wasi': 4.1.8 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.8 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.8 + + '@tailwindcss/postcss@4.1.8': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.0.9 - '@tailwindcss/oxide': 4.0.9 - lightningcss: 1.29.1 + '@tailwindcss/node': 4.1.8 + '@tailwindcss/oxide': 4.1.8 postcss: 8.5.3 - tailwindcss: 4.0.9 + tailwindcss: 4.1.8 '@testing-library/dom@10.4.0': dependencies: @@ -4051,20 +4391,24 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/svelte@5.2.8(svelte@5.30.1)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4)': + '@testing-library/svelte@5.2.8(svelte@5.34.8)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))(vitest@3.1.4)': dependencies: '@testing-library/dom': 10.4.0 - svelte: 5.30.1 + svelte: 5.34.8 optionalDependencies: - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) - vitest: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.29.1) + vite: 5.4.19(@types/node@22.14.1)(lightningcss@1.30.1) + vitest: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.30.1) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': dependencies: '@testing-library/dom': 10.4.0 + '@tokenizer/token@0.3.0': {} + '@types/aria-query@5.0.4': {} + '@types/chroma-js@3.1.1': {} + '@types/cookie@0.6.0': {} '@types/estree@1.0.6': {} @@ -4084,6 +4428,8 @@ snapshots: '@types/node': 22.14.1 form-data: 4.0.2 + '@types/node@16.9.1': {} + '@types/node@18.19.84': dependencies: undici-types: 5.26.5 @@ -4175,16 +4521,71 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitest/browser@3.1.4(playwright@1.52.0)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4)': + '@vibrant/color@4.0.0': {} + + '@vibrant/core@4.0.0': + dependencies: + '@vibrant/color': 4.0.0 + '@vibrant/generator': 4.0.0 + '@vibrant/image': 4.0.0 + '@vibrant/quantizer': 4.0.0 + '@vibrant/worker': 4.0.0 + + '@vibrant/generator-default@4.0.3': + dependencies: + '@vibrant/color': 4.0.0 + '@vibrant/generator': 4.0.0 + + '@vibrant/generator@4.0.0': + dependencies: + '@vibrant/color': 4.0.0 + '@vibrant/types': 4.0.0 + + '@vibrant/image-browser@4.0.0': + dependencies: + '@vibrant/image': 4.0.0 + + '@vibrant/image-node@4.0.0': + dependencies: + '@jimp/custom': 0.22.12 + '@jimp/plugin-resize': 0.22.12(@jimp/custom@0.22.12) + '@jimp/types': 0.22.12(@jimp/custom@0.22.12) + '@vibrant/image': 4.0.0 + transitivePeerDependencies: + - encoding + + '@vibrant/image@4.0.0': + dependencies: + '@vibrant/color': 4.0.0 + + '@vibrant/quantizer-mmcq@4.0.0': + dependencies: + '@vibrant/color': 4.0.0 + '@vibrant/image': 4.0.0 + '@vibrant/quantizer': 4.0.0 + + '@vibrant/quantizer@4.0.0': + dependencies: + '@vibrant/color': 4.0.0 + '@vibrant/image': 4.0.0 + '@vibrant/types': 4.0.0 + + '@vibrant/types@4.0.0': {} + + '@vibrant/worker@4.0.0': + dependencies: + '@vibrant/types': 4.0.0 + + '@vitest/browser@3.1.4(playwright@1.52.0)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))(vitest@3.1.4)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) - '@vitest/mocker': 3.1.4(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@vitest/mocker': 3.1.4(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)) '@vitest/utils': 3.1.4 magic-string: 0.30.17 sirv: 3.0.1 tinyrainbow: 2.0.0 - vitest: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.29.1) + vitest: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.30.1) ws: 8.18.2 optionalDependencies: playwright: 1.52.0 @@ -4201,13 +4602,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.4(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))': + '@vitest/mocker@3.1.4(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))': dependencies: '@vitest/spy': 3.1.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) + vite: 5.4.19(@types/node@22.14.1)(lightningcss@1.30.1) '@vitest/pretty-format@3.1.4': dependencies: @@ -4269,6 +4670,8 @@ snapshots: ansi-styles@5.2.0: {} + any-base@1.1.0: {} + argparse@2.0.1: {} aria-query@5.3.0: @@ -4291,12 +4694,26 @@ snapshots: base64-js@1.5.1: {} + bits-ui@1.8.0(svelte@5.34.8): + dependencies: + '@floating-ui/core': 1.6.9 + '@floating-ui/dom': 1.6.13 + '@internationalized/date': 3.8.2 + css.escape: 1.5.1 + esm-env: 1.2.2 + runed: 0.23.4(svelte@5.34.8) + svelte: 5.34.8 + svelte-toolbelt: 0.7.1(svelte@5.34.8) + tabbable: 6.2.0 + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 + bmp-js@0.1.0: {} + boolean@3.2.0: {} brace-expansion@1.1.11: @@ -4317,6 +4734,11 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -4360,6 +4782,8 @@ snapshots: chownr@3.0.0: {} + chroma-js@3.1.2: {} + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 @@ -4474,8 +4898,6 @@ snapshots: dequal@2.0.3: {} - detect-libc@1.0.3: {} - detect-libc@2.0.4: {} detect-node@2.1.0: {} @@ -4603,7 +5025,7 @@ snapshots: optionalDependencies: eslint-config-prettier: 10.1.1(eslint@9.22.0(jiti@2.4.2)) - eslint-plugin-svelte@3.6.0(eslint@9.22.0(jiti@2.4.2))(svelte@5.30.1): + eslint-plugin-svelte@3.6.0(eslint@9.22.0(jiti@2.4.2))(svelte@5.34.8): dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0(jiti@2.4.2)) '@jridgewell/sourcemap-codec': 1.5.0 @@ -4614,9 +5036,9 @@ snapshots: postcss-load-config: 3.1.4(postcss@8.5.3) postcss-safe-parser: 7.0.1(postcss@8.5.3) semver: 7.7.1 - svelte-eslint-parser: 1.2.0(svelte@5.30.1) + svelte-eslint-parser: 1.2.0(svelte@5.34.8) optionalDependencies: - svelte: 5.30.1 + svelte: 5.34.8 transitivePeerDependencies: - ts-node @@ -4685,7 +5107,7 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@1.4.6: + esrap@1.4.9: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -4705,6 +5127,10 @@ snapshots: event-target-shim@5.0.1: {} + events@3.3.0: {} + + exif-parser@0.1.12: {} + expect-type@1.2.1: {} exsolve@1.0.4: {} @@ -4753,6 +5179,12 @@ snapshots: dependencies: flat-cache: 4.0.1 + file-type@16.5.4: + dependencies: + readable-web-to-node-stream: 3.0.4 + strtok3: 6.3.0 + token-types: 4.2.1 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -4821,6 +5253,11 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + gifwrap@0.10.1: + dependencies: + image-q: 4.0.0 + omggif: 1.0.10 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -4937,6 +5374,10 @@ snapshots: ignore@5.3.2: {} + image-q@4.0.0: + dependencies: + '@types/node': 16.9.1 + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -4952,6 +5393,8 @@ snapshots: ini@4.1.3: {} + inline-style-parser@0.2.4: {} + inquirer@8.2.6: dependencies: ansi-escapes: 4.3.2 @@ -5006,6 +5449,13 @@ snapshots: isexe@3.1.1: {} + isomorphic-fetch@3.0.0: + dependencies: + node-fetch: 2.7.0 + whatwg-fetch: 3.6.20 + transitivePeerDependencies: + - encoding + jest-axe@9.0.0: dependencies: axe-core: 4.9.1 @@ -5031,6 +5481,8 @@ snapshots: jiti@2.4.2: {} + jpeg-js@0.4.4: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -5089,50 +5541,50 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lightningcss-darwin-arm64@1.29.1: + lightningcss-darwin-arm64@1.30.1: optional: true - lightningcss-darwin-x64@1.29.1: + lightningcss-darwin-x64@1.30.1: optional: true - lightningcss-freebsd-x64@1.29.1: + lightningcss-freebsd-x64@1.30.1: optional: true - lightningcss-linux-arm-gnueabihf@1.29.1: + lightningcss-linux-arm-gnueabihf@1.30.1: optional: true - lightningcss-linux-arm64-gnu@1.29.1: + lightningcss-linux-arm64-gnu@1.30.1: optional: true - lightningcss-linux-arm64-musl@1.29.1: + lightningcss-linux-arm64-musl@1.30.1: optional: true - lightningcss-linux-x64-gnu@1.29.1: + lightningcss-linux-x64-gnu@1.30.1: optional: true - lightningcss-linux-x64-musl@1.29.1: + lightningcss-linux-x64-musl@1.30.1: optional: true - lightningcss-win32-arm64-msvc@1.29.1: + lightningcss-win32-arm64-msvc@1.30.1: optional: true - lightningcss-win32-x64-msvc@1.29.1: + lightningcss-win32-x64-msvc@1.30.1: optional: true - lightningcss@1.29.1: + lightningcss@1.30.1: dependencies: - detect-libc: 1.0.3 + detect-libc: 2.0.4 optionalDependencies: - lightningcss-darwin-arm64: 1.29.1 - lightningcss-darwin-x64: 1.29.1 - lightningcss-freebsd-x64: 1.29.1 - lightningcss-linux-arm-gnueabihf: 1.29.1 - lightningcss-linux-arm64-gnu: 1.29.1 - lightningcss-linux-arm64-musl: 1.29.1 - lightningcss-linux-x64-gnu: 1.29.1 - lightningcss-linux-x64-musl: 1.29.1 - lightningcss-win32-arm64-msvc: 1.29.1 - lightningcss-win32-x64-msvc: 1.29.1 + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 lilconfig@2.1.0: {} @@ -5191,14 +5643,14 @@ snapshots: unist-util-visit: 5.0.0 vfile: 6.0.3 - melt@0.30.1(@floating-ui/dom@1.6.13)(svelte@5.30.1): + melt@0.36.0(@floating-ui/dom@1.6.13)(svelte@5.34.8): dependencies: '@floating-ui/dom': 1.6.13 dequal: 2.0.3 jest-axe: 9.0.0 nanoid: 5.1.5 - runed: 0.23.4(svelte@5.30.1) - svelte: 5.30.1 + runed: 0.23.4(svelte@5.34.8) + svelte: 5.34.8 merge2@1.4.1: {} @@ -5279,10 +5731,23 @@ snapshots: dependencies: whatwg-url: 5.0.0 + node-vibrant@4.0.3: + dependencies: + '@types/node': 18.19.84 + '@vibrant/core': 4.0.0 + '@vibrant/generator-default': 4.0.3 + '@vibrant/image-browser': 4.0.0 + '@vibrant/image-node': 4.0.0 + '@vibrant/quantizer-mmcq': 4.0.0 + transitivePeerDependencies: + - encoding + nwsapi@2.2.20: {} object-keys@1.1.1: {} + omggif@1.0.10: {} + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -5371,6 +5836,8 @@ snapshots: dependencies: quansync: 0.2.8 + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -5393,12 +5860,18 @@ snapshots: pathval@2.0.0: {} + peek-readable@4.1.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} picomatch@4.0.2: {} + pixelmatch@4.0.2: + dependencies: + pngjs: 3.4.0 + pkg-dir@7.0.0: dependencies: find-up: 6.3.0 @@ -5425,6 +5898,10 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + pngjs@3.4.0: {} + + pngjs@6.0.0: {} + postcss-load-config@3.1.4(postcss@8.5.3): dependencies: lilconfig: 2.1.0 @@ -5457,16 +5934,16 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.30.1): + prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.34.8): dependencies: prettier: 3.5.3 - svelte: 5.30.1 + svelte: 5.34.8 - prettier-plugin-tailwindcss@0.6.11(prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.30.1))(prettier@3.5.3): + prettier-plugin-tailwindcss@0.6.11(prettier-plugin-svelte@3.4.0(prettier@3.5.3)(svelte@5.34.8))(prettier@3.5.3): dependencies: prettier: 3.5.3 optionalDependencies: - prettier-plugin-svelte: 3.4.0(prettier@3.5.3)(svelte@5.30.1) + prettier-plugin-svelte: 3.4.0(prettier@3.5.3)(svelte@5.34.8) prettier@3.5.3: {} @@ -5482,6 +5959,8 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + process@0.11.10: {} + property-information@7.0.0: {} protobufjs@7.4.0: @@ -5520,6 +5999,18 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readable-web-to-node-stream@3.0.4: + dependencies: + readable-stream: 4.7.0 + readdirp@4.1.2: {} redent@3.0.0: @@ -5529,6 +6020,8 @@ snapshots: reflect-metadata@0.1.14: {} + regenerator-runtime@0.13.11: {} + regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 @@ -5607,15 +6100,15 @@ snapshots: dependencies: queue-microtask: 1.2.3 - runed@0.23.4(svelte@5.30.1): + runed@0.23.4(svelte@5.34.8): dependencies: esm-env: 1.2.2 - svelte: 5.30.1 + svelte: 5.34.8 - runed@0.25.0(svelte@5.30.1): + runed@0.25.0(svelte@5.34.8): dependencies: esm-env: 1.2.2 - svelte: 5.30.1 + svelte: 5.34.8 rxjs@7.8.2: dependencies: @@ -5739,25 +6232,34 @@ snapshots: strip-json-comments@3.1.1: {} + strtok3@6.3.0: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 4.1.0 + + style-to-object@1.0.9: + dependencies: + inline-style-parser: 0.2.4 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.1.5(picomatch@4.0.2)(svelte@5.30.1)(typescript@5.8.2): + svelte-check@4.1.5(picomatch@4.0.2)(svelte@5.34.8)(typescript@5.8.2): dependencies: '@jridgewell/trace-mapping': 0.3.25 chokidar: 4.0.3 fdir: 6.4.3(picomatch@4.0.2) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.30.1 + svelte: 5.34.8 typescript: 5.8.2 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.2.0(svelte@5.30.1): + svelte-eslint-parser@1.2.0(svelte@5.34.8): dependencies: eslint-scope: 8.3.0 eslint-visitor-keys: 4.2.0 @@ -5766,9 +6268,16 @@ snapshots: postcss-scss: 4.0.9(postcss@8.5.3) postcss-selector-parser: 7.1.0 optionalDependencies: - svelte: 5.30.1 + svelte: 5.34.8 - svelte@5.30.1: + svelte-toolbelt@0.7.1(svelte@5.34.8): + dependencies: + clsx: 2.1.1 + runed: 0.23.4(svelte@5.34.8) + style-to-object: 1.0.9 + svelte: 5.34.8 + + svelte@5.34.8: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.0 @@ -5779,7 +6288,7 @@ snapshots: axobject-query: 4.1.0 clsx: 2.1.1 esm-env: 1.2.2 - esrap: 1.4.6 + esrap: 1.4.9 is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.17 @@ -5792,9 +6301,11 @@ snapshots: '@pkgr/core': 0.1.1 tslib: 2.8.1 + tabbable@6.2.0: {} + tailwind-merge@3.0.2: {} - tailwindcss@4.0.9: {} + tailwindcss@4.1.8: {} tapable@2.2.1: {} @@ -5809,8 +6320,12 @@ snapshots: through@2.3.8: {} + timm@1.7.1: {} + tinybench@2.9.0: {} + tinycolor2@1.6.0: {} + tinyexec@0.3.2: {} tinyglobby@0.2.13: @@ -5838,6 +6353,11 @@ snapshots: dependencies: is-number: 7.0.0 + token-types@4.2.1: + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + totalist@3.0.1: {} tough-cookie@5.1.2: @@ -5940,7 +6460,7 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - unplugin-icons@22.1.0(svelte@5.30.1): + unplugin-icons@22.1.0(svelte@5.34.8): dependencies: '@antfu/install-pkg': 1.0.0 '@iconify/utils': 2.3.0 @@ -5948,7 +6468,7 @@ snapshots: local-pkg: 1.1.1 unplugin: 2.2.0 optionalDependencies: - svelte: 5.30.1 + svelte: 5.34.8 transitivePeerDependencies: - supports-color @@ -5966,10 +6486,21 @@ snapshots: dependencies: punycode: 2.3.1 + utif2@4.1.0: + dependencies: + pako: 1.0.11 + util-deprecate@1.0.2: {} uuid@8.3.2: {} + vaul-svelte@https://pkg.vc/-/@melt-ui/vaul-svelte@9195c05(svelte@5.34.8): + dependencies: + bits-ui: 1.8.0(svelte@5.34.8) + runed: 0.23.4(svelte@5.34.8) + svelte: 5.34.8 + svelte-toolbelt: 0.7.1(svelte@5.34.8) + vfile-message@4.0.2: dependencies: '@types/unist': 3.0.3 @@ -5980,13 +6511,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@3.1.4(@types/node@22.14.1)(lightningcss@1.29.1): + vite-node@3.1.4(@types/node@22.14.1)(lightningcss@1.30.1): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) + vite: 5.4.19(@types/node@22.14.1)(lightningcss@1.30.1) transitivePeerDependencies: - '@types/node' - less @@ -5998,7 +6529,7 @@ snapshots: - supports-color - terser - vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1): + vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1): dependencies: esbuild: 0.21.5 postcss: 8.5.3 @@ -6006,9 +6537,9 @@ snapshots: optionalDependencies: '@types/node': 22.14.1 fsevents: 2.3.3 - lightningcss: 1.29.1 + lightningcss: 1.30.1 - vite@6.2.1(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0): + vite@6.2.1(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.7.0): dependencies: esbuild: 0.25.1 postcss: 8.5.3 @@ -6017,23 +6548,23 @@ snapshots: '@types/node': 22.14.1 fsevents: 2.3.3 jiti: 2.4.2 - lightningcss: 1.29.1 + lightningcss: 1.30.1 yaml: 2.7.0 - vitefu@1.0.6(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)): + vitefu@1.0.6(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)): optionalDependencies: - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) + vite: 5.4.19(@types/node@22.14.1)(lightningcss@1.30.1) - vitest-browser-svelte@0.1.0(@vitest/browser@3.1.4)(svelte@5.30.1)(vitest@3.1.4): + vitest-browser-svelte@0.1.0(@vitest/browser@3.1.4)(svelte@5.34.8)(vitest@3.1.4): dependencies: - '@vitest/browser': 3.1.4(playwright@1.52.0)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4) - svelte: 5.30.1 - vitest: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.29.1) + '@vitest/browser': 3.1.4(playwright@1.52.0)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))(vitest@3.1.4) + svelte: 5.34.8 + vitest: 3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.30.1) - vitest@3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.29.1): + vitest@3.1.4(@types/node@22.14.1)(@vitest/browser@3.1.4)(jsdom@26.1.0)(lightningcss@1.30.1): dependencies: '@vitest/expect': 3.1.4 - '@vitest/mocker': 3.1.4(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1)) + '@vitest/mocker': 3.1.4(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1)) '@vitest/pretty-format': 3.1.4 '@vitest/runner': 3.1.4 '@vitest/snapshot': 3.1.4 @@ -6050,12 +6581,12 @@ snapshots: tinyglobby: 0.2.13 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.1) - vite-node: 3.1.4(@types/node@22.14.1)(lightningcss@1.29.1) + vite: 5.4.19(@types/node@22.14.1)(lightningcss@1.30.1) + vite-node: 3.1.4(@types/node@22.14.1)(lightningcss@1.30.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.14.1 - '@vitest/browser': 3.1.4(playwright@1.52.0)(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.1))(vitest@3.1.4) + '@vitest/browser': 3.1.4(playwright@1.52.0)(vite@5.4.19(@types/node@22.14.1)(lightningcss@1.30.1))(vitest@3.1.4) jsdom: 26.1.0 transitivePeerDependencies: - less @@ -6088,6 +6619,8 @@ snapshots: dependencies: iconv-lite: 0.6.3 + whatwg-fetch@3.6.20: {} + whatwg-mimetype@4.0.0: {} whatwg-url@14.2.0: diff --git a/scripts/download-model-images.ts b/scripts/download-model-images.ts new file mode 100644 index 00000000..d698e225 --- /dev/null +++ b/scripts/download-model-images.ts @@ -0,0 +1,226 @@ +import { writeFileSync, mkdirSync, existsSync } from "fs"; +import { join } from "path"; +import { PipelineTag } from "../src/lib/types.js"; + +// Manual blacklist of models to skip (add model IDs here) +const MODEL_BLACKLIST: string[] = [ + // Add model IDs you want to skip, e.g.: + // 'some-org/inappropriate-model', + // 'another-org/unwanted-model', + "uriel353/flux-pawg", // it's nsfw +]; + +function isBlacklisted(modelId: string): boolean { + return MODEL_BLACKLIST.includes(modelId); +} + +async function getModelPreviewImages(modelId: string): Promise { + try { + const hfPage = `https://huggingface.co/${modelId}`; + const res = await fetch(hfPage, { + headers: { + "User-Agent": "Mozilla/5.0 (compatible; InferencePlayground/1.0)", + }, + }); + + if (!res.ok) { + console.log(`❌ Failed to fetch ${modelId}: ${res.status}`); + return []; + } + + const html = await res.text(); + const allImages = html.match(/]+src="([^"]+)"/g); + + if (!allImages) { + return []; + } + + const modelImages = allImages + .map(img => { + const match = img.match(/src="([^"]+)"/); + return match ? match[1] : null; + }) + .filter((src): src is string => Boolean(src)) + .filter(src => { + // Check if the URL contains the model ID in the path + return src.includes(`/${modelId}/`) || src.includes(`/${modelId}/resolve/`); + }) + .map(src => { + // Convert relative URLs to absolute + if (src.startsWith("/")) { + return `https://huggingface.co${src}`; + } + return src; + }); + return modelImages; + } catch (error) { + console.error(`❌ Error fetching images for ${modelId}:`, error); + return []; + } +} + +async function downloadImage(url: string, outputPath: string): Promise { + try { + const response = await fetch(url); + if (!response.ok) { + console.log(`❌ Failed to download ${url}: ${response.status}`); + return false; + } + + const buffer = await response.arrayBuffer(); + writeFileSync(outputPath, new Uint8Array(buffer)); + console.log(`✅ Downloaded: ${outputPath}`); + return true; + } catch (error) { + console.error(`❌ Error downloading ${url}:`, error); + return false; + } +} + +async function getModelsForTag(tag: PipelineTag): Promise { + const requestInit: RequestInit = { + headers: { + "User-Agent": "Mozilla/5.0 (compatible; InferencePlayground/1.0)", + }, + method: "GET", + }; + + const url = new URL("https://huggingface.co/api/models"); + url.searchParams.append("inference_provider", "all"); + url.searchParams.append("limit", "100"); + url.searchParams.append("pipeline_tag", tag); + ["inferenceProviderMapping", "config", "library_name", "pipeline_tag", "tags", "mask_token", "trendingScore"].forEach( + s => url.searchParams.append("expand[]", s) + ); + + if ([PipelineTag.TextGeneration, PipelineTag.ImageTextToText].includes(tag)) { + url.searchParams.append("filter", "conversational"); + } + + const res = await fetch(url, requestInit); + + if (!res.ok) { + console.error(`Failed to fetch models for ${tag}: ${res.status}`); + return []; + } + + return await res.json(); +} + +function sanitizeFilename(filename: string): string { + return filename.replace(/[^a-z0-9.-]/gi, "_"); +} + +async function main() { + console.log("🚀 Starting model image download..."); + + // Create static directory structure + const staticDir = join(process.cwd(), "static"); + const imagesDir = join(staticDir, "model-images"); + + if (!existsSync(staticDir)) { + mkdirSync(staticDir, { recursive: true }); + } + if (!existsSync(imagesDir)) { + mkdirSync(imagesDir, { recursive: true }); + } + + // Visual pipeline tags that need images + const visualTags = [PipelineTag.TextToImage, PipelineTag.TextToVideo, PipelineTag.ImageTextToText]; + + const modelImageMap: Record = {}; + let totalDownloaded = 0; + + for (const tag of visualTags) { + console.log(`\n📋 Processing ${tag} models...`); + const models = await getModelsForTag(tag); + console.log(`Found ${models.length} models for ${tag}`); + + // Filter out blacklisted models + const validModels = models.filter(model => { + if (isBlacklisted(model.id)) { + console.log(`🚫 Skipping blacklisted model: ${model.id}`); + return false; + } + return true; + }); + + console.log(`Processing ${validModels.length} valid models...`); + + // Process models in parallel batches of 10 + const BATCH_SIZE = 10; + for (let i = 0; i < validModels.length; i += BATCH_SIZE) { + const batch = validModels.slice(i, i + BATCH_SIZE); + console.log( + `\n🔄 Processing batch ${Math.floor(i / BATCH_SIZE) + 1}/${Math.ceil(validModels.length / BATCH_SIZE)} (${batch.length} models)` + ); + + const batchPromises = batch.map(async model => { + try { + console.log(`🔍 Processing ${model.id}...`); + const imageUrls = await getModelPreviewImages(model.id); + + if (imageUrls.length === 0) { + console.log(`⚠️ No images found for ${model.id}`); + return null; + } + + // Try to download the first image + const firstImageUrl = imageUrls[0]; + const extension = firstImageUrl.split(".").pop()?.split("?")[0] || "jpg"; + const filename = `${sanitizeFilename(model.id.replace("/", "_"))}.${extension}`; + const outputPath = join(imagesDir, filename); + + // Skip if already exists + if (existsSync(outputPath)) { + console.log(`⏭️ Image already exists: ${filename}`); + return { modelId: model.id, path: `/model-images/${filename}`, downloaded: false }; + } + + const success = await downloadImage(firstImageUrl, outputPath); + if (success) { + return { modelId: model.id, path: `/model-images/${filename}`, downloaded: true }; + } + return null; + } catch (error) { + console.error(`❌ Error processing ${model.id}:`, error); + return null; + } + }); + + // Wait for batch to complete + const results = await Promise.all(batchPromises); + + // Update mapping and count + results.forEach(result => { + if (result) { + modelImageMap[result.modelId] = result.path; + if (result.downloaded) { + totalDownloaded++; + } + } + }); + + // Small delay between batches to be nice to HuggingFace + if (i + BATCH_SIZE < validModels.length) { + console.log(`⏸️ Waiting 2s before next batch...`); + await new Promise(resolve => setTimeout(resolve, 2000)); + } + } + } + + // Save the mapping to a JSON file + const mappingPath = join(staticDir, "model-image-mapping.json"); + writeFileSync(mappingPath, JSON.stringify(modelImageMap, null, 2)); + + console.log(`\n🎉 Download complete!`); + console.log(`📊 Total images downloaded: ${totalDownloaded}`); + console.log(`📁 Images saved to: ${imagesDir}`); + console.log(`🗺️ Mapping saved to: ${mappingPath}`); +} + +if (import.meta.url === `file://${process.argv[1]}`) { + main().catch(console.error); +} + +export { main as downloadModelImages }; diff --git a/src/app.css b/src/app.css index 1a9936de..2af93e48 100644 --- a/src/app.css +++ b/src/app.css @@ -28,6 +28,82 @@ --text-2xs: 0.625rem; --text-3xs: 0.5rem; + --color-lemon-punch-1: oklch(0 0.1747506522354832 92.57964255604236); + --color-lemon-punch-2: oklch(0.053911940357218684 0.1747506522354832 92.57964255604236); + --color-lemon-punch-3: oklch(0.1127354697689834 0.1747506522354832 92.57964255604236); + --color-lemon-punch-4: oklch(0.1715589991807481 0.1747506522354832 92.57964255604236); + --color-lemon-punch-5: oklch(0.2303825285925128 0.1747506522354832 92.57964255604236); + --color-lemon-punch-6: oklch(0.28920605800427757 0.1747506522354832 92.57964255604236); + --color-lemon-punch-7: oklch(0.34802958741604223 0.1747506522354832 92.57964255604236); + --color-lemon-punch-8: oklch(0.406853116827807 0.1747506522354832 92.57964255604236); + --color-lemon-punch-9: oklch(0.4656766462395716 0.1747506522354832 92.57964255604236); + --color-lemon-punch-10: oklch(0.5245001756513363 0.1747506522354832 92.57964255604236); + --color-lemon-punch-11: oklch(0.583323705063101 0.1747506522354832 92.57964255604236); + --color-lemon-punch-12: oklch(0.6421472344748657 0.1747506522354832 92.57964255604236); + --color-lemon-punch-13: oklch(0.7009707638866304 0.1747506522354832 92.57964255604236); + --color-lemon-punch-14: oklch(0.7597942932983952 0.1747506522354832 92.57964255604236); + --color-lemon-punch-15: oklch(0.81861782271016 0.1747506522354832 92.57964255604236); + --color-lemon-punch-16: oklch(0.8774413521219246 0.1747506522354832 92.57964255604236); + --color-lemon-punch-17: oklch(0.9362648815336893 0.1747506522354832 92.57964255604236); + --color-lemon-punch-18: oklch(0.995088410945454 0.1747506522354832 92.57964255604236); + + --color-mandarin-peel-1: oklch(0.01405984187971887 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-2: oklch(0.07288337129148359 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-3: oklch(0.1317069007032483 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-4: oklch(0.190530430115013 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-5: oklch(0.2493539595267777 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-6: oklch(0.30817748893854247 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-7: oklch(0.36700101835030713 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-8: oklch(0.4258245477620719 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-9: oklch(0.4846480771738365 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-10: oklch(0.5434716065856012 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-11: oklch(0.602295135997366 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-12: oklch(0.6611186654091306 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-13: oklch(0.7199421948208953 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-14: oklch(0.7787657242326601 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-15: oklch(0.8375892536444249 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-16: oklch(0.8964127830561895 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-17: oklch(0.9552363124679542 0.1726501951311444 66.56527686213349); + --color-mandarin-peel-18: oklch(1 0.1726501951311444 66.56527686213349); + + --color-encore-1: oklch(0.02160733926495073 0.023360896938901806 264.36374217448486); + --color-encore-2: oklch(0.08043086867671545 0.023360896938901806 264.36374217448486); + --color-encore-3: oklch(0.13925439808848017 0.023360896938901806 264.36374217448486); + --color-encore-4: oklch(0.19807792750024486 0.023360896938901806 264.36374217448486); + --color-encore-5: oklch(0.25690145691200955 0.023360896938901806 264.36374217448486); + --color-encore-6: oklch(0.3157249863237743 0.023360896938901806 264.36374217448486); + --color-encore-7: oklch(0.374548515735539 0.023360896938901806 264.36374217448486); + --color-encore-8: oklch(0.43337204514730376 0.023360896938901806 264.36374217448486); + --color-encore-9: oklch(0.49219557455906837 0.023360896938901806 264.36374217448486); + --color-encore-10: oklch(0.5510191039708331 0.023360896938901806 264.36374217448486); + --color-encore-11: oklch(0.6098426333825978 0.023360896938901806 264.36374217448486); + --color-encore-12: oklch(0.6686661627943624 0.023360896938901806 264.36374217448486); + --color-encore-13: oklch(0.7274896922061271 0.023360896938901806 264.36374217448486); + --color-encore-14: oklch(0.786313221617892 0.023360896938901806 264.36374217448486); + --color-encore-15: oklch(0.8451367510296568 0.023360896938901806 264.36374217448486); + --color-encore-16: oklch(0.9039602804414214 0.023360896938901806 264.36374217448486); + --color-encore-17: oklch(0.962783809853186 0.023360896938901806 264.36374217448486); + --color-encore-18: oklch(1 0.023360896938901806 264.36374217448486); + + --color-pasilla-1: oklch(0.02085092653391274 0.010009355395935343 73.5936583596527); + --color-pasilla-2: oklch(0.07967445594567746 0.010009355395935343 73.5936583596527); + --color-pasilla-3: oklch(0.13849798535744218 0.010009355395935343 73.5936583596527); + --color-pasilla-4: oklch(0.19732151476920687 0.010009355395935343 73.5936583596527); + --color-pasilla-5: oklch(0.25614504418097156 0.010009355395935343 73.5936583596527); + --color-pasilla-6: oklch(0.31496857359273633 0.010009355395935343 73.5936583596527); + --color-pasilla-7: oklch(0.373792103004501 0.010009355395935343 73.5936583596527); + --color-pasilla-8: oklch(0.43261563241626577 0.010009355395935343 73.5936583596527); + --color-pasilla-9: oklch(0.4914391618280304 0.010009355395935343 73.5936583596527); + --color-pasilla-10: oklch(0.5502626912397951 0.010009355395935343 73.5936583596527); + --color-pasilla-11: oklch(0.6090862206515598 0.010009355395935343 73.5936583596527); + --color-pasilla-12: oklch(0.6679097500633244 0.010009355395935343 73.5936583596527); + --color-pasilla-13: oklch(0.7267332794750891 0.010009355395935343 73.5936583596527); + --color-pasilla-14: oklch(0.785556808886854 0.010009355395935343 73.5936583596527); + --color-pasilla-15: oklch(0.8443803382986188 0.010009355395935343 73.5936583596527); + --color-pasilla-16: oklch(0.9032038677103834 0.010009355395935343 73.5936583596527); + --color-pasilla-17: oklch(0.962027397122148 0.010009355395935343 73.5936583596527); + --color-pasilla-18: oklch(1 0.010009355395935343 73.5936583596527); + --animate-fade-in: fade-in 0.15s ease; @keyframes fade-in { 0% { @@ -79,15 +155,6 @@ @apply flex h-[24px] items-center justify-center gap-0.5 rounded-sm border border-gray-200 bg-white px-1.5 py-1 text-[10px] font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:ring-2 focus:ring-gray-100 focus:outline-hidden dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700; } -@utility custom-outline { - @apply outline-hidden; - @apply border-blue-500 ring ring-blue-500; -} - -@utility focus-outline { - @apply focus-visible:custom-outline; -} - @utility input { @apply rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400; @apply focus-outline; @@ -101,6 +168,36 @@ mask-image: linear-gradient(to bottom, transparent, black var(--start), black var(--end), transparent); } +@utility border-gradient { + --border-gradient-before: linear-gradient(180deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0) 100%); + --border-gradient-after: linear-gradient(180deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0) 125.11%); + --border-radius: 0.5rem; + position: relative; + + &::before, + &::after { + content: ""; + position: absolute; + inset: 0; + border-radius: var(--border-radius); + border: 1px solid transparent; + mask: + linear-gradient(#fff 0 0) padding-box, + linear-gradient(#fff 0 0); + mask-composite: exclude; + pointer-events: none; + z-index: 1; + } + + &::before { + background: var(--border-gradient-before) border-box; + } + + &::after { + background: var(--border-gradient-after) border-box; + } +} + /* Elements & Classes */ html { font-size: 15px; @@ -114,13 +211,151 @@ body.dark { color-scheme: dark; } +@utility custom-outline { + outline-width: 2px; + outline-style: solid; + outline-offset: 2px; + + outline-color: var(--color-blue-400); + + @variant dark { + outline-color: var(--color-blue-500); + } +} + +@utility focus-outline { + @apply focus-visible:custom-outline; +} + +@custom-variant hover-nd (&:where(:hover:enabled)); +@custom-variant active-nd (&:where(:active:enabled)); + @layer base { - :focus-visible { - outline: 3px solid var(--color-blue-400); - outline-offset: 2px; + * { + outline-color: var(--color-blue-400); @variant dark { outline-color: var(--color-blue-500); } } + + :focus-visible { + @apply custom-outline; + } +} + +.btn-depth { + --bg-from: var(--color-mandarin-peel-11); + --bg-to: var(--color-mandarin-peel-14); + --bg-to-hover: var(--color-mandarin-peel-15); + --bg-to-active: var(--color-mandarin-peel-13); + --border-color: var(--color-mandarin-peel-8); + + --pi: 4; + --pb: 2; + + border-radius: var(--radius-lg, 0.5rem); + border-style: var(--tw-border-style); + border-width: 1px; + border-color: var(--border-color); + background: linear-gradient(to top, var(--bg-from), var(--bg-to)); + padding-inline: calc(var(--spacing, 0.25rem) * var(--pi)); + padding-block: calc(var(--spacing, 0.25rem) * var(--pb)); + line-height: var(--tw-leading, var(--text-base--line-height, calc(1.5 / 1))); + --tw-font-weight: var(--font-weight-medium, 500); + font-weight: var(--font-weight-medium, 500); + color: var(--color-white, #fff); + --tw-shadow: var(--ds-shadow-border-medium); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + transition-property: all; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function, cubic-bezier(0.4, 0, 0.2, 1))); + transition-duration: var(--tw-duration, var(--default-transition-duration, 0.15s)); + + position: relative; + z-index: 0; + box-shadow: + inset 0 1px 0 oklch(1 0 0/0.2), + inset 0 -1px 0 oklch(0 0 0/0.2), + 0 1px 2px oklch(0 0 0/0.2); + color: oklch(1 0 0); + text-shadow: 0 1px rgb(0 11 15/40%); + will-change: transform; + transition: 0.125s ease; +} + +.btn-depth > svg { + filter: drop-shadow(0 1px rgb(0 11 15/40%)); +} + +.btn-depth::before { + content: ""; + position: absolute; + inset: 0px; + background: linear-gradient(to top, var(--bg-from), var(--bg-to-hover)); + border-radius: inherit; + opacity: 0; + transition: opacity 0.125s ease; + z-index: -1; +} + +.btn-depth:hover::before:not(:disabled) { + opacity: 1; +} + +.btn-depth::after { + content: ""; + position: absolute; + inset: 0px; + background: linear-gradient(to top, var(--bg-from), var(--bg-to-active)); + border-radius: inherit; + opacity: 0; + transition: opacity 0.125s ease; + z-index: -1; +} + +.btn-depth:active:not(:disabled) { + scale: 0.99; +} + +.btn-depth:active::after:not(:disabled) { + opacity: 1; +} + +.btn-depth-neutral { + --bg-from: var(--color-neutral-700); + --bg-to: var(--color-neutral-600); + --bg-to-hover: var(--color-neutral-500); + --bg-to-active: var(--color-neutral-600); + --border-color: var(--color-neutral-800); +} + +.btn-depth-stone { + --bg-from: var(--color-stone-700); + --bg-to: var(--color-stone-600); + --bg-to-hover: var(--color-stone-500); + --bg-to-active: var(--color-stone-600); + --border-color: var(--color-stone-800); +} + +.btn-depth-gray { + --bg-from: var(--color-gray-700); + --bg-to: var(--color-gray-600); + --bg-to-hover: var(--color-gray-500); + --bg-to-active: var(--color-gray-600); + --border-color: var(--color-gray-800); +} + +.btn-depth-red { + --bg-from: var(--color-red-600); + --bg-to: var(--color-red-500); + --bg-to-hover: var(--color-red-400); + --bg-to-active: var(--color-red-700); + --border-color: var(--color-red-600); +} + +.btn-depth-sm { + --pi: 3; + --pb: 1.5; } diff --git a/src/lib/attachments/masonry.ts b/src/lib/attachments/masonry.ts new file mode 100644 index 00000000..6ee58579 --- /dev/null +++ b/src/lib/attachments/masonry.ts @@ -0,0 +1,109 @@ +import type { Attachment } from "svelte/attachments"; + +function noop() {} + +function isHTMLElement(element: ChildNode): element is HTMLElement { + return element.nodeType === 1; +} + +function getStyleInfo( + el: HTMLElement, + property: "grid-template-columns" | "grid-template-rows", + attribute: "gridTemplateColumns" | "gridTemplateRows" +) { + const inline = el.style[attribute]; + const computedValue = getComputedStyle(el)[attribute]; + + // First check inline style + if (inline) return computedValue; + + // Then check stylesheets + const isExplicit = (() => { + for (const sheet of document.styleSheets) { + let rules; + try { + rules = sheet.cssRules; + } catch { + continue; // CORS-restricted stylesheet + } + if (!rules) continue; + + for (const rule of rules) { + if (!(rule instanceof CSSStyleRule)) continue; + if (!el.matches(rule.selectorText)) continue; + + const val = rule.style.getPropertyValue(property); + if (val) return true; + } + } + return false; + })(); + + if (isExplicit) return computedValue; + return "masonry"; +} + +function adjustGridItems(grid: HTMLElement) { + const items = Array.from(grid.childNodes).filter(isHTMLElement); + + items.forEach(({ style }) => { + style.removeProperty("margin-top"); + style.removeProperty("margin-left"); + }); + + const style = getComputedStyle(grid); + const columnsRule = style.gridTemplateColumns; + const columns = columnsRule.split(" ").length; + + const rowsRule = getStyleInfo(grid, "grid-template-rows", "gridTemplateRows"); + const rows = rowsRule.split(" ").length; + + if (rowsRule !== "masonry" && columnsRule !== "masonry") return; + + if (rowsRule === "masonry") { + if (columns <= 1) return; + if (items.length <= columns) return; + + const gap = parseFloat(style.rowGap); + + items.slice(columns).forEach((item, index) => { + const { bottom: prevBottom } = items[index]!.getBoundingClientRect(); + const { top } = item.getBoundingClientRect(); + + item.style.setProperty("margin-top", `${prevBottom + gap - top}px`); + }); + } else { + if (rows <= 1) return; + if (items.length <= rows) return; + + const gap = parseFloat(style.columnGap); + + items.slice(rows).forEach((item, index) => { + const { right: prevRight } = items[index]!.getBoundingClientRect(); + const { left } = item.getBoundingClientRect(); + + item.style.setProperty("margin-left", `${prevRight + gap - left}px`); + }); + } +} + +export const masonry: Attachment = node => { + if (!node) return noop; + + const style = getComputedStyle(node); + + // exit if masonry is supported + if (style.gridTemplateRows === "masonry") return noop; + if (style.gridTemplateColumns === "masonry") return noop; + + function onFrame() { + if (node.isConnected) adjustGridItems(node); + frame = requestAnimationFrame(onFrame); + } + + let frame = requestAnimationFrame(onFrame); + + return () => { + cancelAnimationFrame(frame); + }; +}; diff --git a/src/lib/components/avatar.svelte b/src/lib/components/avatar.svelte index 69fa8689..501b14f5 100644 --- a/src/lib/components/avatar.svelte +++ b/src/lib/components/avatar.svelte @@ -1,5 +1,6 @@ + +
+ + + + +
+ {#each providers as { provider, providerId } (provider + providerId)} +
+
+ + {formatName(provider)} +
+
+ {/each} +
+
diff --git a/src/lib/utils/business.svelte.ts b/src/lib/utils/business.svelte.ts index 5f3b99e7..e2fd8c3f 100644 --- a/src/lib/utils/business.svelte.ts +++ b/src/lib/utils/business.svelte.ts @@ -10,9 +10,8 @@ import ctxLengthData from "$lib/data/context_length.json"; import { InferenceClient, snippets } from "@huggingface/inference"; import { ConversationClass, type ConversationEntityMembers } from "$lib/state/conversations.svelte"; import { token } from "$lib/state/token.svelte"; +import { isCustomModel, isHFModel } from "$lib/utils/is.js"; import { - isCustomModel, - isHFModel, Provider, type Conversation, type ConversationMessage, @@ -338,6 +337,7 @@ export function getInferenceSnippet( const providerMapping = model.inferenceProviderMapping.find(p => p.provider === provider); if (!providerMapping) return []; + const allSnippets = snippets.getInferenceSnippets( { ...model, inference: "" }, accessToken, @@ -346,21 +346,6 @@ export function getInferenceSnippet( opts ); - if (opts?.structured_output && !structuredForbiddenProviders.includes(provider as Provider)) { - allSnippets.forEach(s => { - const modified = modifySnippet(s.content, { prop: "hi" }); - if (s.content === modified) { - console.log("Failed for", s.language, "\n"); - } else { - console.log("Original snippet"); - console.log(s.content); - console.log("\nModified"); - console.log(modified); - console.log(); - } - }); - } - return allSnippets .filter(s => s.language === language) .map(s => { diff --git a/src/lib/utils/file.ts b/src/lib/utils/file.ts index b192dd8b..0e2fe284 100644 --- a/src/lib/utils/file.ts +++ b/src/lib/utils/file.ts @@ -1,4 +1,4 @@ -export function fileToDataURL(file: File): Promise { +export function fileToDataURL(file: File | Blob): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); @@ -100,3 +100,59 @@ export function getBase64ImageSize(base64: string): { bytes: number; kilobytes: megabytes: parseFloat(megabytes.toFixed(2)), }; } + +/** + * Converts a Data URL string into a Blob object. + * + * @param dataurl The Data URL string (e.g., "data:image/png;base64,..."). + * @returns A Blob object, or null if the Data URL is invalid or conversion fails. + */ +export function dataURLtoBlob(dataurl: string): Blob | null { + // 1. Separate header and data + const parts = dataurl.split(","); + if (parts.length < 2) { + console.error("Invalid Data URL format."); + return null; + } + + const header = parts[0]; + const data = parts[1]; + + if (!header || !data) return null; + + // Extract MIME type from the header + const mimeMatch = header.match(/:(.*?)(;|$)/); + const mime = mimeMatch ? mimeMatch[1] : ""; + + // 2. Decode base64 if present, otherwise assume plain text or URL-encoded + let decodedData: string; + if (header.includes(";base64")) { + try { + decodedData = atob(data); // Decode base64 string + } catch (e) { + console.error("Error decoding base64 data:", e); + return null; + } + } else { + try { + decodedData = decodeURIComponent(data); // Decode URL-encoded string + } catch (e) { + console.error("Error decoding URL-encoded data:", e); + return null; + } + } + + // 3. Create a Uint8Array from the decoded binary string + const uint8Array = new Uint8Array(decodedData.length); + for (let i = 0; i < decodedData.length; i++) { + uint8Array[i] = decodedData.charCodeAt(i); + } + + // 4. Create and return the Blob + try { + return new Blob([uint8Array], { type: mime }); + } catch (e) { + console.error("Error creating Blob:", e); + return null; + } +} diff --git a/src/lib/utils/is.ts b/src/lib/utils/is.ts index 56f09df2..f29333be 100644 --- a/src/lib/utils/is.ts +++ b/src/lib/utils/is.ts @@ -1,5 +1,6 @@ import { SvelteSet } from "svelte/reactivity"; import typia from "typia"; +import type { Model, CustomModel } from "$lib/types.js"; export function isHtmlElement(element: unknown): element is HTMLElement { return element instanceof HTMLElement; @@ -38,3 +39,6 @@ export function isPromise(value: unknown): value is Promise { } export const isNumber = typia.createIs(); + +export const isHFModel = typia.createIs(); +export const isCustomModel = typia.createIs(); diff --git a/src/lib/utils/number.ts b/src/lib/utils/number.ts new file mode 100644 index 00000000..cc7213ed --- /dev/null +++ b/src/lib/utils/number.ts @@ -0,0 +1,3 @@ +export function clamp(min: number, value: number, max: number): number { + return Math.max(min, Math.min(value, max)); +} diff --git a/src/lib/utils/string.ts b/src/lib/utils/string.ts index 8305dfab..32e61cd0 100644 --- a/src/lib/utils/string.ts +++ b/src/lib/utils/string.ts @@ -4,3 +4,7 @@ export function pluralize(word: string, count: number): string { } return word + "s"; } + +export function capitalize(word: string): string { + return word.charAt(0).toUpperCase() + word.slice(1); +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 1e70f53d..8819db5c 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,10 +1,4 @@ {@render children?.()} - - - - - - diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 12c196ae..a1d2ab2c 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,5 +1,19 @@ + + + + + + diff --git a/src/routes/+page.ts b/src/routes/+page.ts index 63885194..ff481cd8 100644 --- a/src/routes/+page.ts +++ b/src/routes/+page.ts @@ -1,8 +1,13 @@ +import { PipelineTag } from "$lib/types.js"; import type { PageLoad } from "./$types.js"; import type { ApiModelsResponse } from "./api/models/+server.js"; export const load: PageLoad = async ({ fetch }) => { - const res = await fetch("/api/models"); + const params = new URLSearchParams(); + params.append("pipeline_tag", PipelineTag.TextGeneration); + params.append("pipeline_tag", PipelineTag.ImageTextToText); + + const res = await fetch(`/api/models?${params.toString()}`); const json: ApiModelsResponse = await res.json(); return json; }; diff --git a/src/routes/api/db-image/+server.ts b/src/routes/api/db-image/+server.ts new file mode 100644 index 00000000..d6567c99 --- /dev/null +++ b/src/routes/api/db-image/+server.ts @@ -0,0 +1,49 @@ +import { json } from "@sveltejs/kit"; +import type { RequestHandler } from "./$types.js"; + +export const GET: RequestHandler = async ({ fetch }) => { + try { + // Fetch from Danbooru API + const apiResponse = await fetch("https://danbooru.donmai.us/posts/random.json?tags=rating:explicit", { + headers: { + "Accept": "application/json", + "Accept-Encoding": "identity", + "User-Agent": "Mozilla/5.0 (compatible; proxy)", + }, + }); + + if (!apiResponse.ok) { + throw new Error(`Danbooru API failed: ${apiResponse.status}`); + } + + const bodyString = await apiResponse.text(); + console.log("Danbooru API response:", bodyString); + + const data = JSON.parse(bodyString); + + if (data && data.file_url) { + // Fetch the actual image + const imageUrl = data.file_url; + const imageResponse = await fetch(imageUrl); + + if (!imageResponse.ok) { + throw new Error(`Failed to fetch image: ${imageResponse.status}`); + } + + const imageBuffer = await imageResponse.arrayBuffer(); + const contentType = imageResponse.headers.get("content-type") || "image/jpeg"; + + return new Response(imageBuffer, { + headers: { + "Content-Type": contentType, + "Content-Length": imageBuffer.byteLength.toString(), + }, + }); + } else { + return json({ error: "No image found" }, { status: 404 }); + } + } catch (error) { + console.log("Danbooru error:", error); + return json({ error: "Failed to fetch image" }, { status: 500 }); + } +}; diff --git a/src/routes/api/models/+server.ts b/src/routes/api/models/+server.ts index 8969984b..2e8b72a7 100644 --- a/src/routes/api/models/+server.ts +++ b/src/routes/api/models/+server.ts @@ -1,239 +1,19 @@ -import type { Model } from "$lib/types.js"; -import { json } from "@sveltejs/kit"; +import { PipelineTag, type Model } from "$lib/types.js"; +import { error, json } from "@sveltejs/kit"; +import typia from "typia"; import type { RequestHandler } from "./$types.js"; - -enum CacheStatus { - SUCCESS = "success", - PARTIAL = "partial", - ERROR = "error", -} - -type Cache = { - data: Model[] | undefined; - timestamp: number; - status: CacheStatus; - // Track failed models to selectively refetch them - failedTokenizers: string[]; // Using array instead of Set for serialization compatibility - failedApiCalls: { - textGeneration: boolean; - imageTextToText: boolean; - }; -}; - -const cache: Cache = { - data: undefined, - timestamp: 0, - status: CacheStatus.ERROR, - failedTokenizers: [], - failedApiCalls: { - textGeneration: false, - imageTextToText: false, - }, -}; - -// The time between cache refreshes -const FULL_CACHE_REFRESH = 1000 * 60 * 60; // 1 hour -const PARTIAL_CACHE_REFRESH = 1000 * 60 * 15; // 15 minutes (shorter for partial results) - -const headers: HeadersInit = { - "Upgrade-Insecure-Requests": "1", - "Sec-Fetch-Dest": "document", - "Sec-Fetch-Mode": "navigate", - "Sec-Fetch-Site": "none", - "Sec-Fetch-User": "?1", - "Priority": "u=0, i", - "Pragma": "no-cache", - "Cache-Control": "no-cache", -}; - -const requestInit: RequestInit = { - credentials: "include", - headers, - method: "GET", - mode: "cors", -}; - -interface ApiQueryParams { - pipeline_tag?: "text-generation" | "image-text-to-text"; - filter: string; - inference_provider: string; - limit: number; - expand: string[]; -} - -const queryParams: ApiQueryParams = { - filter: "conversational", - inference_provider: "all", - limit: 100, - expand: ["inferenceProviderMapping", "config", "library_name", "pipeline_tag", "tags", "mask_token", "trendingScore"], -}; - -const baseUrl = "https://huggingface.co/api/models"; - -function buildApiUrl(params: ApiQueryParams): string { - const url = new URL(baseUrl); - - // Add simple params - Object.entries(params).forEach(([key, value]) => { - if (!Array.isArray(value)) { - url.searchParams.append(key, String(value)); - } - }); - - // Handle array params specially - params.expand.forEach(item => { - url.searchParams.append("expand[]", item); - }); - - return url.toString(); -} +import { getModelsForTag } from "./get-models.js"; export type ApiModelsResponse = { models: Model[]; }; -function createResponse(data: ApiModelsResponse): Response { - return json(data); -} - -export const GET: RequestHandler = async ({ fetch }) => { - const timestamp = Date.now(); - - // Determine if cache is valid - const elapsed = timestamp - cache.timestamp; - const cacheRefreshTime = cache.status === CacheStatus.SUCCESS ? FULL_CACHE_REFRESH : PARTIAL_CACHE_REFRESH; - - // Use cache if it's still valid and has data - if (elapsed < cacheRefreshTime && cache.data?.length) { - console.log(`Using ${cache.status} cache (${Math.floor(elapsed / 1000 / 60)} min old)`); - return createResponse({ models: cache.data }); +export const GET: RequestHandler = async ({ fetch, url: requestUrl }) => { + const requestedTags = requestUrl.searchParams.getAll("pipeline_tag"); + if (!typia.is(requestedTags)) { + return error(500, "Invalid query params"); } + const promises = await Promise.all(requestedTags.map(tag => getModelsForTag({ tag, fetch }))); - try { - // Determine which API calls we need to make based on cache status - const needTextGenFetch = elapsed >= FULL_CACHE_REFRESH || cache.failedApiCalls.textGeneration; - const needImgTextFetch = elapsed >= FULL_CACHE_REFRESH || cache.failedApiCalls.imageTextToText; - - // Track the existing models we'll keep - const existingModels = new Map(); - if (cache.data) { - cache.data.forEach(model => { - existingModels.set(model.id, model); - }); - } - - // Initialize new tracking for failed requests - const newFailedTokenizers: string[] = []; - const newFailedApiCalls = { - textGeneration: false, - imageTextToText: false, - }; - - // Fetch models as needed - let textGenModels: Model[] = []; - let imgText2TextModels: Model[] = []; - - // Make the needed API calls in parallel - const apiPromises: Promise[] = []; - if (needTextGenFetch) { - const url = buildApiUrl({ ...queryParams, pipeline_tag: "text-generation" }); - apiPromises.push( - fetch(url, requestInit).then(async response => { - if (!response.ok) { - console.error(`Error fetching text-generation models`, response.status, response.statusText); - newFailedApiCalls.textGeneration = true; - } else { - textGenModels = await response.json(); - } - }) - ); - } - - if (needImgTextFetch) { - apiPromises.push( - fetch(buildApiUrl({ ...queryParams, pipeline_tag: "image-text-to-text" }), requestInit).then(async response => { - if (!response.ok) { - console.error(`Error fetching image-text-to-text models`, response.status, response.statusText); - newFailedApiCalls.imageTextToText = true; - } else { - imgText2TextModels = await response.json(); - } - }) - ); - } - - await Promise.all(apiPromises); - - // If both needed API calls failed and we have cached data, use it - if ( - needTextGenFetch && - newFailedApiCalls.textGeneration && - needImgTextFetch && - newFailedApiCalls.imageTextToText && - cache.data?.length - ) { - console.log("All API requests failed. Using existing cache as fallback."); - cache.status = CacheStatus.ERROR; - cache.timestamp = timestamp; // Update timestamp to avoid rapid retry loops - cache.failedApiCalls = newFailedApiCalls; - return createResponse({ models: cache.data }); - } - - // For API calls we didn't need to make, use cached models - if (!needTextGenFetch && cache.data) { - textGenModels = cache.data.filter(model => model.pipeline_tag === "text-generation").map(model => model as Model); - } - - if (!needImgTextFetch && cache.data) { - imgText2TextModels = cache.data - .filter(model => model.pipeline_tag === "image-text-to-text") - .map(model => model as Model); - } - - const models: Model[] = [...textGenModels, ...imgText2TextModels].filter( - m => m.inferenceProviderMapping.length > 0 - ); - models.sort((a, b) => a.id.toLowerCase().localeCompare(b.id.toLowerCase())); - - // Determine cache status based on failures - const hasApiFailures = newFailedApiCalls.textGeneration || newFailedApiCalls.imageTextToText; - - const cacheStatus = hasApiFailures ? CacheStatus.PARTIAL : CacheStatus.SUCCESS; - - cache.data = models; - cache.timestamp = timestamp; - cache.status = cacheStatus; - cache.failedTokenizers = newFailedTokenizers; - cache.failedApiCalls = newFailedApiCalls; - - console.log( - `Cache updated: ${models.length} models, status: ${cacheStatus}, ` + - `failed tokenizers: ${newFailedTokenizers.length}, ` + - `API failures: text=${newFailedApiCalls.textGeneration}, img=${newFailedApiCalls.imageTextToText}` - ); - - return createResponse({ models }); - } catch (error) { - console.error("Error fetching models:", error); - - // If we have cached data, use it as fallback - if (cache.data?.length) { - cache.status = CacheStatus.ERROR; - // Mark all API calls as failed so we retry them next time - cache.failedApiCalls = { - textGeneration: true, - imageTextToText: true, - }; - return createResponse({ models: cache.data }); - } - - // No cache available, return empty array - cache.status = CacheStatus.ERROR; - cache.timestamp = timestamp; - cache.failedApiCalls = { - textGeneration: true, - imageTextToText: true, - }; - return createResponse({ models: [] }); - } + return json({ models: promises.flat() }); }; diff --git a/src/routes/api/models/cache.ts b/src/routes/api/models/cache.ts new file mode 100644 index 00000000..1e58ad2c --- /dev/null +++ b/src/routes/api/models/cache.ts @@ -0,0 +1,26 @@ +import { PipelineTag, type Model } from "$lib/types.js"; + +type CacheEntryArgs = { + data?: Model[]; + ok: boolean; +}; + +export class CacheEntry { + timestamp: number; + data: CacheEntryArgs["data"]; + ok: CacheEntryArgs["ok"]; + + constructor(args: CacheEntryArgs) { + this.timestamp = Date.now(); + this.data = args.data; + this.ok = args.ok; + } + + isFresh(): boolean { + return Math.abs(this.timestamp - Date.now()) < CACHE_REFRESH; // 1 hour in milliseconds + } +} + +export const cache: Partial> = {}; + +const CACHE_REFRESH = 1000 * 60 * 60; // 1 hour diff --git a/src/routes/api/models/get-model-image.ts b/src/routes/api/models/get-model-image.ts new file mode 100644 index 00000000..2d792aad --- /dev/null +++ b/src/routes/api/models/get-model-image.ts @@ -0,0 +1,48 @@ +export async function getModelPreviewImage( + modelId: string, + fetch: typeof globalThis.fetch +): Promise { + try { + const hfPage = `https://huggingface.co/${modelId}`; + const res = await fetch(hfPage, { + headers: { + "User-Agent": "Mozilla/5.0 (compatible; InferencePlayground/1.0)", + }, + }); + + if (!res.ok) { + return undefined; + } + + const html = await res.text(); + const allImages = html.match(/]+src="([^"]+)"/g); + + if (!allImages) { + return undefined; + } + + const modelImages = allImages + .map(img => { + const match = img.match(/src="([^"]+)"/); + return match ? match[1] : null; + }) + .filter((src): src is string => Boolean(src)) + .filter(src => { + // Check if the URL contains the model ID in the path + return src.includes(`/${modelId}/`) || src.includes(`/${modelId}/resolve/`); + }) + .map(src => { + // Convert relative URLs to absolute + if (src.startsWith('/')) { + return `https://huggingface.co${src}`; + } + // Return absolute URLs as-is + return src; + }); + + return modelImages[0]; // Return the first image found + } catch (error) { + console.error(`Error fetching preview image for ${modelId}:`, error); + return undefined; + } +} diff --git a/src/routes/api/models/get-models.ts b/src/routes/api/models/get-models.ts new file mode 100644 index 00000000..57ed3d9f --- /dev/null +++ b/src/routes/api/models/get-models.ts @@ -0,0 +1,84 @@ +import { PipelineTag, type Model } from "$lib/types.js"; +import type { RequestHandler } from "./$types.js"; +import { cache, CacheEntry } from "./cache.js"; +import { readFileSync, existsSync } from "fs"; +import { join } from "path"; + +type GetModelsForTagArgs = { + tag: PipelineTag; + fetch: Parameters[0]["fetch"]; +}; + +export async function getModelsForTag({ tag, fetch }: GetModelsForTagArgs): Promise { + if (cache[tag] && cache[tag].isFresh() && cache[tag].ok && cache[tag].data) { + console.log(`Using cache for ${tag}`); + return cache[tag].data; + } + + console.log(`Fetching ${tag}`); + + const requestInit: RequestInit = { + credentials: "include", + headers: { + "Upgrade-Insecure-Requests": "1", + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "none", + "Sec-Fetch-User": "?1", + "Priority": "u=0, i", + "Pragma": "no-cache", + "Cache-Control": "no-cache", + }, + method: "GET", + mode: "cors", + }; + + const url = new URL("https://huggingface.co/api/models"); + url.searchParams.append("inference_provider", "all"); + url.searchParams.append("limit", "100"); + url.searchParams.append("pipeline_tag", tag); + ["inferenceProviderMapping", "config", "library_name", "pipeline_tag", "tags", "mask_token", "trendingScore"].forEach( + s => url.searchParams.append("expand[]", s) + ); + + if ([PipelineTag.TextGeneration, PipelineTag.ImageTextToText].includes(tag)) { + url.searchParams.append("filter", "conversational"); + } + + const res = await fetch(url, requestInit); + console.log(url.href); + + if (!res.ok) { + cache[tag] = new CacheEntry({ ok: false }); + return []; + } + + const data = await res.json(); + + // Add preview images for visual pipeline tags using local mapping + if ([PipelineTag.TextToImage, PipelineTag.TextToVideo, PipelineTag.ImageTextToText].includes(tag)) { + let imageMapping: Record = {}; + + // Try to load the local image mapping + try { + const mappingPath = join(process.cwd(), 'static', 'model-image-mapping.json'); + if (existsSync(mappingPath)) { + const mappingContent = readFileSync(mappingPath, 'utf-8'); + imageMapping = JSON.parse(mappingContent); + } + } catch (error) { + console.warn('Could not load model image mapping:', error); + } + + const modelsWithImages = data.map((model: Model) => ({ + ...model, + preview_img: imageMapping[model.id] || undefined + })); + + cache[tag] = new CacheEntry({ ok: true, data: modelsWithImages }); + return modelsWithImages; + } + + cache[tag] = new CacheEntry({ ok: true, data }); + return data; +} diff --git a/src/routes/api/sb-image/+server.ts b/src/routes/api/sb-image/+server.ts new file mode 100644 index 00000000..f6309618 --- /dev/null +++ b/src/routes/api/sb-image/+server.ts @@ -0,0 +1,52 @@ +import { json } from "@sveltejs/kit"; +import type { RequestHandler } from "./$types"; + +export const GET: RequestHandler = async ({ fetch }) => { + try { + // Fetch from Safebooru API + const apiResponse = await fetch( + "https://safebooru.org/index.php?page=dapi&s=post&q=index&json=1&limit=1&pid=0&tags=clair_obscur%3a_expedition_33+sort%3arandom", + { + headers: { + "Accept": "application/json", + "Accept-Encoding": "identity", + "User-Agent": "Mozilla/5.0 (compatible; proxy)", + }, + } + ); + + if (!apiResponse.ok) { + throw new Error(`Safebooru API failed: ${apiResponse.status}`); + } + + const bodyString = await apiResponse.text(); + console.log("Safebooru API response:", bodyString); + + const data = JSON.parse(bodyString); + + if (Array.isArray(data) && data.length > 0 && data[0].file_url) { + // Fetch the actual image + const imageUrl = data[0].file_url; + const imageResponse = await fetch(imageUrl); + + if (!imageResponse.ok) { + throw new Error(`Failed to fetch image: ${imageResponse.status}`); + } + + const imageBuffer = await imageResponse.arrayBuffer(); + const contentType = imageResponse.headers.get("content-type") || "image/jpeg"; + + return new Response(imageBuffer, { + headers: { + "Content-Type": contentType, + "Content-Length": imageBuffer.byteLength.toString(), + }, + }); + } else { + return json({ error: "No image found" }, { status: 404 }); + } + } catch (error) { + console.log("Safebooru error:", error); + return json({ error: "Failed to fetch image" }, { status: 500 }); + } +}; diff --git a/src/routes/usage/+page.svelte b/src/routes/usage/+page.svelte deleted file mode 100644 index e69de29b..00000000 diff --git a/src/routes/visual/(components)/loading-animation.svelte b/src/routes/visual/(components)/loading-animation.svelte new file mode 100644 index 00000000..c1bf84ec --- /dev/null +++ b/src/routes/visual/(components)/loading-animation.svelte @@ -0,0 +1,132 @@ + + +
+ + + + + + + + {#each pixels as pixel} + + {/each} + + + + + Generating... + + +
+ + diff --git a/src/routes/visual/(components)/model-menu.svelte b/src/routes/visual/(components)/model-menu.svelte new file mode 100644 index 00000000..4714e6b7 --- /dev/null +++ b/src/routes/visual/(components)/model-menu.svelte @@ -0,0 +1,135 @@ + + + + +
+
+ +
+ {#each searchedModels as model} + {@const item = menu.getItem(model)} + {@const company = model.id.split("/")[0] ?? "Other"} +
+ {#if model.preview_img} +
+ +
+
+ {:else} +
+ {/if} + +
+
+

+ {getModelName(model)} +

+

+ {formatCompanyName(company)} +

+
+ {#if settings.model?.id === model.id} +
+ +
+ {/if} +
+
+ + {model.pipeline_tag} + +
+
+ {/each} +
diff --git a/src/routes/visual/(components)/settings.svelte b/src/routes/visual/(components)/settings.svelte new file mode 100644 index 00000000..a97a4bcb --- /dev/null +++ b/src/routes/visual/(components)/settings.svelte @@ -0,0 +1,526 @@ + + + + + +
+ + +
+
+
+
+ + +
+ + +
+ +
+ {#each searchedModels as model (model)} +
+ {model.id} + {#if modelCombobox.value?.id === model.id} + + {/if} +
+ {:else} +
No models found
+ {/each} +
+
+ +
+ +
+ + {#snippet radio(tag: PipelineTag, Icon: typeof IconImage)} + + {/snippet} + + {@render radio(PipelineTag.TextToImage, IconImage)} + {@render radio(PipelineTag.TextToVideo, IconVideo)} +
+
+ + {#if settings.model && settings.provider} +
+ +
+ {/if} + + + + + + + + {#if settings.filterTag === PipelineTag.TextToImage} + +
+ + + Advanced Parameters + + + + +
+ + +
+ + + + + +
+ + +
+
+ {/if} +
+
+ +
+ + + Text generation + + +
+ +
+ + diff --git a/src/routes/visual/(components)/sidebar.svelte b/src/routes/visual/(components)/sidebar.svelte new file mode 100644 index 00000000..7078fbe3 --- /dev/null +++ b/src/routes/visual/(components)/sidebar.svelte @@ -0,0 +1,46 @@ + + + + + + diff --git a/src/routes/visual/(components)/visual-card.svelte b/src/routes/visual/(components)/visual-card.svelte new file mode 100644 index 00000000..0a997df2 --- /dev/null +++ b/src/routes/visual/(components)/visual-card.svelte @@ -0,0 +1,284 @@ + + +
+ {#if !isVisualItem(item)} +
+ +
+ {:else} + + {/if} +
+
+ {#each keys(palette ?? {}) as swatch} + + {#snippet trigger(tooltip)} + + {/snippet} + {swatch} + + {/each} +
+
+

{item.config?.prompt || "N/A"}

+
+
+ Model {item.config?.model} +
+
+ Provider {item.config?.provider} +
+
+ Time + {#if isVisualItem(item)} + + {item.data.generationTimeMs ? formatGenerationTime(item.data.generationTimeMs) : "..."} + + {:else} + { + const t0 = performance.now(); + function counter() { + const delta = performance.now() - t0; + const secs = delta / 1000; + node.innerText = `${secs.toFixed(2)}s`; + frame = requestAnimationFrame(counter); + } + + let frame = requestAnimationFrame(counter); + + return () => { + cancelAnimationFrame(frame); + }; + }} + > + {/if} +
+
+
+ {#if !isVisualItem(item)} + + {:else} + + {#snippet trigger(tooltip)} + + {/snippet} + Re-use {item.type} settings + + + + + + {#snippet trigger(tooltip)} + + {/snippet} + Download {item.type} + + + + + + {#snippet trigger(tooltip)} + + {/snippet} + Delete {item.type} + + {/if} +
+
+
+
diff --git a/src/routes/visual/(state)/settings.svelte.ts b/src/routes/visual/(state)/settings.svelte.ts new file mode 100644 index 00000000..fe33c974 --- /dev/null +++ b/src/routes/visual/(state)/settings.svelte.ts @@ -0,0 +1,18 @@ +import { createPersistedObj } from "$lib/spells/persisted-obj.svelte"; +import { PipelineTag, type InferenceProviderMapping, type Model } from "$lib/types.js"; + +export const settings = createPersistedObj("inf-pg-vis-settings", { + columns: 2, + filterTag: PipelineTag.TextToImage, + prompt: "", + model: undefined as Model | undefined, + provider: undefined as InferenceProviderMapping["provider"] | undefined, + // Text-to-image parameters + guidance_scale: 7.5, + negative_prompt: "", + num_inference_steps: 4, + width: 512, + height: 512, + scheduler: undefined as string | undefined, + seed: undefined as number | undefined, +}); diff --git a/src/routes/visual/(state)/visual-items.svelte.ts b/src/routes/visual/(state)/visual-items.svelte.ts new file mode 100644 index 00000000..d75f969c --- /dev/null +++ b/src/routes/visual/(state)/visual-items.svelte.ts @@ -0,0 +1,153 @@ +import { idb, JsonEntityIndexedDbStorage } from "$lib/remult.js"; +import { dataURLtoBlob, fileToDataURL } from "$lib/utils/file.js"; +import { omit } from "$lib/utils/object.svelte"; +import { Entity, Fields, repo, type MembersOnly } from "remult"; +import { createSubscriber } from "svelte/reactivity"; +import typia from "typia"; + +export enum VisualEntityType { + Video = "video", + Image = "image", +} + +@Entity("visual-item") +export class VisualItemEntity { + @Fields.cuid() + id!: string; + + @Fields.enum(() => VisualEntityType) + type!: VisualEntityType; + + @Fields.createdAt() + createdAt!: Date; + + /** + * The key for the blob in storage + */ + @Fields.string() + storageKey?: string; + + @Fields.json() + config?: { + prompt: string; + model?: string; + provider?: string; + guidance_scale?: number; + negative_prompt?: string; + num_inference_steps?: number; + width?: number; + height?: number; + scheduler?: string; + seed?: number; + }; + + @Fields.number() + generationTimeMs?: number; +} + +const visualItemRepo = repo(VisualItemEntity, idb); + +export type VisualItemEntityMembers = MembersOnly; + +export type VisualItemArgs = VisualItemEntityMembers & {}; + +export class VisualItem { + #src = $state(); + data = $state.raw() as VisualItemEntityMembers; + config = $derived(this.data.config); + type = $derived(this.data.type); + id = $derived(this.data.id); + + constructor(args: VisualItemArgs) { + this.data = args; + } + + get blob() { + if (!this.src) return; + return dataURLtoBlob(this.src); + } + + get src() { + if (!this.#src && this.data.storageKey) { + blobs.get(this.data.storageKey).then(b => (this.#src = b)); + } + + return this.#src; + } + + delete() { + if (this.data.storageKey) blobs.delete(this.data.storageKey); + return visualItemRepo.delete(this.data.id); + } +} + +export type GeneratingItem = Pick; + +class VisualItems { + #items = $state([]); + generating = $state.raw([]); + + #sub = createSubscriber(() => { + visualItemRepo.find().then(res => { + res.forEach(item => this.#items.push(new VisualItem(item))); + }); + return visualItemRepo.addEventListener({ + saved: item => { + this.#items.push(new VisualItem(item)); + }, + deleted: item => { + this.#items = this.#items.filter(_item => _item.id !== item.id); + }, + }); + // return visualItemRepo.liveQuery().subscribe(info => { + // this.#items = info.items.map(item => new VisualItem(item)); + // }); + }); + + get all() { + this.#sub(); + return [ + ...this.generating, + ...this.#items.toSorted((a, b) => b.data.createdAt.getTime() - a.data.createdAt.getTime()), + ]; + } + + create(args: Partial) { + return visualItemRepo.save(omit(args, "id")); + } + + delete(item: VisualItem | GeneratingItem) { + if (item instanceof VisualItem) { + return visualItemRepo.delete(item.data.id); + } else { + this.generating = this.generating.filter(_item => _item !== item); + } + } +} + +export const visualItems = new VisualItems(); + +export const isVisualItem = typia.createIs(); + +const store = new JsonEntityIndexedDbStorage(); + +class Blobs { + async upload(blob: Blob) { + const dataUrl = await fileToDataURL(blob); + + const key = `blob-${crypto.randomUUID()}`; + store.setItem(key, dataUrl); + + return key; + } + + async get(key: string): Promise { + return await store.getItem(key); + } + + async delete(key: string) { + return await store.deleteItem(key); + } +} + +export const blobs = new Blobs(); diff --git a/src/routes/visual/+page.svelte b/src/routes/visual/+page.svelte new file mode 100644 index 00000000..698b2af2 --- /dev/null +++ b/src/routes/visual/+page.svelte @@ -0,0 +1,159 @@ + + + { + if (e.key === "Escape" && expandedItem) { + closeExpandedItem(); + } + }} +/> + +{#if token.showModal} + (token.showModal = false)} + on:submit={handleTokenSubmit} + /> +{/if} + +
+ + + + + Open settings + + + + + +
+ + +
+
+
+ + +
+ {#if visualItems.all.length === 0} +
+
+ +

No content generated yet

+

Click "Generate" to create your first

+
+
+ {:else} +
+ {#each visualItems.all as item (item.id)} + visualItems.delete(item)} + onReuse={() => reuseSettings(item)} + onExpand={() => expandItem(item)} + /> + {/each} +
+ {/if} +
+
+ + + { + // Close dialog when clicking on backdrop + if (e.target === dialogElement) { + closeExpandedItem(); + } + }} +> + + {#if expandedItem && expandedItem.src} + {#if expandedItem.data.type === "image"} + Expanded view: {expandedItem.config?.prompt} + {:else if expandedItem.data.type === "video"} + + {/if} + {/if} + + + diff --git a/src/routes/visual/+page.ts b/src/routes/visual/+page.ts new file mode 100644 index 00000000..dd278e1c --- /dev/null +++ b/src/routes/visual/+page.ts @@ -0,0 +1,13 @@ +import { PipelineTag } from "$lib/types.js"; +import type { PageLoad } from "./$types.js"; +import type { ApiModelsResponse } from "../api/models/+server.js"; + +export const load: PageLoad = async ({ fetch }) => { + const params = new URLSearchParams(); + params.append("pipeline_tag", PipelineTag.TextToImage); + params.append("pipeline_tag", PipelineTag.TextToVideo); + + const res = await fetch(`/api/models?${params.toString()}`); + const json: ApiModelsResponse = await res.json(); + return json; +}; diff --git a/src/routes/visual/TODO.md b/src/routes/visual/TODO.md new file mode 100644 index 00000000..9dbcc482 --- /dev/null +++ b/src/routes/visual/TODO.md @@ -0,0 +1,10 @@ +# TODO + +- [ ] AbortController for cancelling requests +- [x] Link back to normal playground +- [x] Persist generation parameters +- [x] Combobox for models +- [x] Save images locally +- [x] Video +- [x] Improve styling +- [x] Adjust color contrast for a11y purposes diff --git a/src/routes/visual/utils.ts b/src/routes/visual/utils.ts new file mode 100644 index 00000000..c0a9a10e --- /dev/null +++ b/src/routes/visual/utils.ts @@ -0,0 +1,551 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/** + * Supported image formats for frame extraction + */ +type ImageFormat = "image/png" | "image/jpeg" | "image/webp"; + +/** + * Options for frame extraction + */ +interface FrameExtractionOptions { + /** Position in video as percentage (0-100) */ + percentage?: number; + /** Output image format */ + format?: ImageFormat; + /** JPEG quality (0-1, only applies to JPEG format) */ + quality?: number; + /** Maximum width for output image (maintains aspect ratio) */ + maxWidth?: number; + /** Maximum height for output image (maintains aspect ratio) */ + maxHeight?: number; + /** Timeout in milliseconds (default: 15000) */ + timeout?: number; + /** Enable debug logging */ + debug?: boolean; +} + +/** + * Result of frame extraction operation + */ +interface FrameExtractionResult { + /** The extracted frame as a blob */ + blob: Blob; + /** Original video dimensions */ + originalDimensions: { + width: number; + height: number; + }; + /** Output image dimensions */ + outputDimensions: { + width: number; + height: number; + }; + /** Actual timestamp where frame was extracted (in seconds) */ + timestamp: number; +} + +/** + * Custom error class for video frame extraction + */ +class VideoFrameExtractionError extends Error { + constructor( + message: string, + public readonly code: string, + public readonly originalError?: Error + ) { + super(message); + this.name = "VideoFrameExtractionError"; + } +} + +/** + * Detects browser type for Firefox-specific handling + */ +function isFirefox(): boolean { + return navigator.userAgent.toLowerCase().includes("firefox"); +} + +/** + * Debug logger + */ +function debugLog(message: string, data?: any, enabled: boolean = false): void { + if (enabled) { + console.log(`[VideoFrameExtractor] ${message}`, data || ""); + } +} + +/** + * Calculates output dimensions while maintaining aspect ratio + */ +function calculateOutputDimensions( + originalWidth: number, + originalHeight: number, + maxWidth?: number, + maxHeight?: number +): { width: number; height: number } { + let { width, height } = { width: originalWidth, height: originalHeight }; + + if (maxWidth && width > maxWidth) { + height = (height * maxWidth) / width; + width = maxWidth; + } + + if (maxHeight && height > maxHeight) { + width = (width * maxHeight) / height; + height = maxHeight; + } + + return { + width: Math.round(width), + height: Math.round(height), + }; +} + +/** + * Aggressively waits for video frame to be drawable + */ +async function waitForDrawableFrame( + video: HTMLVideoElement, + canvas: HTMLCanvasElement, + ctx: CanvasRenderingContext2D, + maxAttempts: number = 20, + debug: boolean = false +): Promise { + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + debugLog( + `Frame readiness check attempt ${attempt}/${maxAttempts}`, + { + readyState: video.readyState, + currentTime: video.currentTime, + videoWidth: video.videoWidth, + videoHeight: video.videoHeight, + }, + debug + ); + + // Wait for video to be in a drawable state + if (video.readyState >= 2) { + // Clear and try to draw + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(video, 0, 0, canvas.width, canvas.height); + + // Check if we actually drew something + const imageData = ctx.getImageData(0, 0, Math.min(50, canvas.width), Math.min(50, canvas.height)); + const pixels = imageData.data; + + // Check for non-transparent pixels + let hasContent = false; + for (let i = 0; i < pixels.length; i += 4) { + const r = pixels[i]!; + const g = pixels[i + 1]!; + const b = pixels[i + 2]!; + const a = pixels[i + 3]!; + + // If we have any non-black, non-transparent pixel, we have content + if ((r > 0 || g > 0 || b > 0) && a > 0) { + hasContent = true; + break; + } + } + + debugLog(`Frame check result`, { hasContent, attempt }, debug); + + if (hasContent) { + return; // Success! + } + } + + // Wait before next attempt, with increasing delays + const delay = Math.min(100 + attempt * 50, 1000); + await new Promise(resolve => setTimeout(resolve, delay)); + } + + throw new Error(`Failed to get drawable frame after ${maxAttempts} attempts`); +} + +/** + * AGGRESSIVE Firefox-compatible video frame extraction + */ +async function extractVideoFrame( + videoBlob: Blob, + options: FrameExtractionOptions = {} +): Promise { + const { + percentage = 50, + format = "image/png", + quality = 0.92, + maxWidth, + maxHeight, + timeout = 15000, + debug = false, + } = options; + + debugLog("Starting AGGRESSIVE frame extraction", { percentage, format, timeout }, debug); + + // Validate inputs + if (percentage < 0 || percentage > 100) { + throw new VideoFrameExtractionError("Percentage must be between 0 and 100", "INVALID_PERCENTAGE"); + } + + const video = document.createElement("video"); + const canvas = document.createElement("canvas"); + const videoUrl = URL.createObjectURL(videoBlob); + + // AGGRESSIVE video configuration for Firefox + video.crossOrigin = "anonymous"; + video.muted = true; + video.playsInline = true; + video.preload = "auto"; // Changed to 'auto' for Firefox + video.controls = false; + video.autoplay = false; + video.loop = false; + + // Make video visible but tiny (Firefox sometimes needs this) + video.style.position = "fixed"; + video.style.top = "0px"; + video.style.left = "0px"; + video.style.width = "2px"; + video.style.height = "2px"; + video.style.opacity = "0.01"; // Barely visible but not hidden + video.style.pointerEvents = "none"; + video.style.zIndex = "-9999"; + + let cleanup: (() => void) | null = null; + + try { + cleanup = () => { + debugLog("Cleaning up resources", undefined, debug); + URL.revokeObjectURL(videoUrl); + if (video.parentNode) { + video.parentNode.removeChild(video); + } + canvas.width = 0; + canvas.height = 0; + }; + + // Add to DOM FIRST + document.body.appendChild(video); + + // Set source and FORCE load + video.src = videoUrl; + video.load(); + + debugLog("Video added to DOM and loading started", undefined, debug); + + // Wait for metadata with multiple fallbacks + await new Promise((resolve, reject) => { + let resolved = false; + + const timeoutId = setTimeout(() => { + if (!resolved) { + resolved = true; + reject(new Error("Metadata loading timed out")); + } + }, timeout / 2); + + const onSuccess = () => { + if (!resolved) { + resolved = true; + clearTimeout(timeoutId); + video.removeEventListener("loadedmetadata", onSuccess); + video.removeEventListener("loadeddata", onSuccess); + video.removeEventListener("canplay", onSuccess); + video.removeEventListener("error", onError); + resolve(); + } + }; + + const onError = (_e: Event) => { + if (!resolved) { + resolved = true; + clearTimeout(timeoutId); + video.removeEventListener("loadedmetadata", onSuccess); + video.removeEventListener("loadeddata", onSuccess); + video.removeEventListener("canplay", onSuccess); + video.removeEventListener("error", onError); + reject(new Error("Video loading failed")); + } + }; + + video.addEventListener("loadedmetadata", onSuccess); + video.addEventListener("loadeddata", onSuccess); + video.addEventListener("canplay", onSuccess); + video.addEventListener("error", onError); + }); + + debugLog( + "Metadata loaded", + { + duration: video.duration, + videoWidth: video.videoWidth, + videoHeight: video.videoHeight, + readyState: video.readyState, + }, + debug + ); + + const { videoWidth, videoHeight, duration } = video; + + if (videoWidth === 0 || videoHeight === 0) { + throw new VideoFrameExtractionError("Video has invalid dimensions", "INVALID_DIMENSIONS"); + } + + if (!isFinite(duration) || duration <= 0) { + throw new VideoFrameExtractionError("Video has invalid duration", "INVALID_DURATION"); + } + + // Setup canvas + const outputDimensions = calculateOutputDimensions(videoWidth, videoHeight, maxWidth, maxHeight); + + canvas.width = outputDimensions.width; + canvas.height = outputDimensions.height; + + const ctx = canvas.getContext("2d", { + willReadFrequently: false, + alpha: format === "image/png", + }); + + if (!ctx) { + throw new VideoFrameExtractionError("Failed to get 2D canvas context", "CANVAS_CONTEXT_ERROR"); + } + + // Calculate target time + const targetTime = Math.min(duration - 0.1, Math.max(0, (duration * percentage) / 100)); + + debugLog("Seeking to target time", { targetTime }, debug); + + // AGGRESSIVE seeking approach + await new Promise((resolve, reject) => { + let resolved = false; + + const timeoutId = setTimeout(() => { + if (!resolved) { + resolved = true; + reject(new Error("Seek timed out")); + } + }, timeout / 3); + + const onSeeked = () => { + if (!resolved) { + resolved = true; + clearTimeout(timeoutId); + video.removeEventListener("seeked", onSeeked); + video.removeEventListener("error", onError); + resolve(); + } + }; + + const onError = () => { + if (!resolved) { + resolved = true; + clearTimeout(timeoutId); + video.removeEventListener("seeked", onSeeked); + video.removeEventListener("error", onError); + reject(new Error("Seek failed")); + } + }; + + video.addEventListener("seeked", onSeeked); + video.addEventListener("error", onError); + + // Multiple seek attempts for Firefox + video.currentTime = targetTime; + + // Force another seek after a delay if Firefox is being stubborn + setTimeout(() => { + if (!resolved && Math.abs(video.currentTime - targetTime) > 0.1) { + debugLog("Forcing additional seek", { current: video.currentTime, target: targetTime }, debug); + video.currentTime = targetTime; + } + }, 500); + }); + + debugLog("Seek completed", { currentTime: video.currentTime }, debug); + + // AGGRESSIVE frame drawing with multiple attempts + await waitForDrawableFrame(video, canvas, ctx, 20, debug); + + debugLog("Frame successfully drawn to canvas", undefined, debug); + + // Convert to blob + const blob = await new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + reject(new Error("Blob conversion timed out")); + }, timeout / 3); + + canvas.toBlob( + result => { + clearTimeout(timeoutId); + if (result && result.size > 0) { + resolve(result); + } else { + reject(new Error("Failed to create blob or blob is empty")); + } + }, + format, + format === "image/jpeg" ? quality : undefined + ); + }); + + debugLog("Blob created successfully", { size: blob.size }, debug); + + const result: FrameExtractionResult = { + blob, + originalDimensions: { + width: videoWidth, + height: videoHeight, + }, + outputDimensions: { + width: canvas.width, + height: canvas.height, + }, + timestamp: video.currentTime, + }; + + return result; + } catch (error) { + debugLog("Error occurred", error, debug); + + if (error instanceof VideoFrameExtractionError) { + throw error; + } + + throw new VideoFrameExtractionError( + error instanceof Error ? error.message : String(error), + "EXTRACTION_ERROR", + error instanceof Error ? error : undefined + ); + } finally { + if (cleanup) { + cleanup(); + } + } +} + +/** + * Retry wrapper with different strategies + */ +async function extractVideoFrameWithRetry( + videoBlob: Blob, + options: FrameExtractionOptions = {}, + maxRetries: number = 3 +): Promise { + const strategies = [ + { ...options, preload: "auto" }, + { ...options, preload: "metadata", percentage: Math.max(1, (options.percentage || 50) - 5) }, + { ...options, preload: "none", percentage: Math.min(95, (options.percentage || 50) + 5) }, + ]; + + let lastError: Error | null = null; + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + debugLog(`AGGRESSIVE attempt ${attempt}/${maxRetries}`, undefined, options.debug); + + if (attempt > 1) { + const delay = 1000 * attempt; + await new Promise(resolve => setTimeout(resolve, delay)); + } + + const strategyOptions = strategies[Math.min(attempt - 1, strategies.length - 1)]; + const result = await extractVideoFrame(videoBlob, strategyOptions); + + return result; + } catch (error) { + lastError = error instanceof Error ? error : new Error(String(error)); + debugLog(`AGGRESSIVE attempt ${attempt} failed`, lastError.message, options.debug); + + if (attempt === maxRetries) { + throw lastError; + } + } + } + + throw lastError || new Error("All aggressive attempts failed"); +} + +// Export functions +export { + extractVideoFrame, + extractVideoFrameWithRetry, + VideoFrameExtractionError, + isFirefox, + type FrameExtractionOptions, + type FrameExtractionResult, + type ImageFormat, +}; + +import chroma from "chroma-js"; + +interface AdjustBgColorOptions { + /** The text color that will be displayed on the background */ + textColor: string | chroma.Color; + /** The background color to adjust */ + backgroundColor: string | chroma.Color; + /** Target APCA contrast ratio (positive for light text on dark bg, negative for dark text on light bg) */ + targetContrast: number; + /** Maximum number of adjustment iterations */ + maxIterations?: number; + /** Tolerance for contrast matching */ + tolerance?: number; + /** Whether to adjust lightness (true) or saturation (false) */ + adjustLightness?: boolean; +} + +/** + * Adjusts a background color to meet APCA contrast requirements against a text color + */ +export function adjustBgColorForAPCAContrast(options: AdjustBgColorOptions): chroma.Color { + const { + textColor, + backgroundColor, + targetContrast, + maxIterations = 50, + tolerance = 1, + adjustLightness = true, + } = options; + + let adjustedBgColor = chroma(backgroundColor); + const txtColor = chroma(textColor); + + let currentContrast = chroma.contrastAPCA(txtColor, adjustedBgColor); + let iterations = 0; + + if (Math.abs(currentContrast - targetContrast) <= tolerance) { + return adjustedBgColor; + } + + while (Math.abs(currentContrast - targetContrast) > tolerance && iterations < maxIterations) { + const [h, s, l] = adjustedBgColor.hsl(); + let newLightness: number; + + if (adjustLightness) { + // For APCA: more negative = better contrast for light text + // So if we want more negative contrast, make bg darker + if (targetContrast < currentContrast) { + // Want more negative (better contrast for light text) - make bg darker + newLightness = Math.max(0, l - 0.05); + } else { + // Want less negative - make bg lighter + newLightness = Math.min(1, l + 0.05); + } + + adjustedBgColor = chroma.hsl(h, s, newLightness); + } + + const newContrast = chroma.contrastAPCA(txtColor, adjustedBgColor); + + // Check if we're getting closer to target + const oldDistance = Math.abs(currentContrast - targetContrast); + const newDistance = Math.abs(newContrast - targetContrast); + + if (newDistance >= oldDistance) { + break; + } + + currentContrast = newContrast; + iterations++; + } + + return adjustedBgColor; +} diff --git a/static/model-image-mapping.json b/static/model-image-mapping.json new file mode 100644 index 00000000..1631a208 --- /dev/null +++ b/static/model-image-mapping.json @@ -0,0 +1,90 @@ +{ + "black-forest-labs/FLUX.1-dev": "/model-images/black-forest-labs_FLUX.1-dev.jpg", + "black-forest-labs/FLUX.1-schnell": "/model-images/black-forest-labs_FLUX.1-schnell.jpeg", + "stabilityai/stable-diffusion-3.5-large": "/model-images/stabilityai_stable-diffusion-3.5-large.png", + "stabilityai/stable-diffusion-xl-base-1.0": "/model-images/stabilityai_stable-diffusion-xl-base-1.0.png", + "HiDream-ai/HiDream-I1-Full": "/model-images/HiDream-ai_HiDream-I1-Full.jpg", + "multimodalart/isometric-skeumorphic-3d-bnb": "/model-images/multimodalart_isometric-skeumorphic-3d-bnb.png", + "stabilityai/stable-diffusion-3.5-medium": "/model-images/stabilityai_stable-diffusion-3.5-medium.jpg", + "ByteDance/Hyper-SD": "/model-images/ByteDance_Hyper-SD.jpg", + "prithivMLmods/Retro-Pixel-Flux-LoRA": "/model-images/prithivMLmods_Retro-Pixel-Flux-LoRA.png", + "strangerzonehf/Flux-Super-Realism-LoRA": "/model-images/strangerzonehf_Flux-Super-Realism-LoRA.png", + "Shakker-Labs/FLUX.1-dev-LoRA-Logo-Design": "/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Logo-Design.jpg", + "prithivMLmods/Canopus-LoRA-Flux-UltraRealism-2.0": "/model-images/prithivMLmods_Canopus-LoRA-Flux-UltraRealism-2.0.png", + "gokaygokay/Flux-2D-Game-Assets-LoRA": "/model-images/gokaygokay_Flux-2D-Game-Assets-LoRA.png", + "ByteDance/SDXL-Lightning": "/model-images/ByteDance_SDXL-Lightning.jpg", + "alvdansen/littletinies": "/model-images/alvdansen_littletinies.jpeg", + "Shakker-Labs/FLUX.1-dev-LoRA-add-details": "/model-images/Shakker-Labs_FLUX.1-dev-LoRA-add-details.jpeg", + "Shakker-Labs/FLUX.1-dev-LoRA-Micro-landscape-on-Mobile-Phone": "/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Micro-landscape-on-Mobile-Phone.jpg", + "stabilityai/stable-diffusion-3.5-large-turbo": "/model-images/stabilityai_stable-diffusion-3.5-large-turbo.png", + "prithivMLmods/Flux.1-Dev-Frosted-Container-LoRA": "/model-images/prithivMLmods_Flux.1-Dev-Frosted-Container-LoRA.png", + "strangerzonehf/Flux-Claymation-XC-LoRA": "/model-images/strangerzonehf_Flux-Claymation-XC-LoRA.png", + "Shakker-Labs/FLUX.1-dev-LoRA-Miniature-World": "/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Miniature-World.jpg", + "openfree/flux-chatgpt-ghibli-lora": "/model-images/openfree_flux-chatgpt-ghibli-lora.jpg", + "semiosphere/the_artist_flux": "/model-images/semiosphere_the_artist_flux.png", + "goofyai/3d_render_style_xl": "/model-images/goofyai_3d_render_style_xl.jpeg", + "CiroN2022/escher-blend": "/model-images/CiroN2022_escher-blend.jpeg", + "piebro/factorio-blueprint-visualizations-sdxl-lora": "/model-images/piebro_factorio-blueprint-visualizations-sdxl-lora.png", + "h1t/TCD-SDXL-LoRA": "/model-images/h1t_TCD-SDXL-LoRA.png", + "ottopilot/PriyaBelleXL": "/model-images/ottopilot_PriyaBelleXL.png", + "BlaireSilver13/BS-Pepe-V1": "/model-images/BlaireSilver13_BS-Pepe-V1.png", + "stabilityai/stable-diffusion-3-medium-diffusers": "/model-images/stabilityai_stable-diffusion-3-medium-diffusers.jpg", + "ottopilot/WhitneyDelgadoXL": "/model-images/ottopilot_WhitneyDelgadoXL.png", + "AiAF/michemashu55_LoRA_Flux1": "/model-images/AiAF_michemashu55_LoRA_Flux1.png", + "Shakker-Labs/FLUX.1-dev-LoRA-Vector-Journey": "/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Vector-Journey.jpeg", + "Norod78/CartoonStyle-flux-lora": "/model-images/Norod78_CartoonStyle-flux-lora.jpg", + "roelfrenkema/flux1.lora.donaldtrump": "/model-images/roelfrenkema_flux1.lora.donaldtrump.jpg", + "Shakker-Labs/FLUX.1-dev-LoRA-Dark-Fantasy": "/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Dark-Fantasy.jpg", + "Shakker-Labs/FLUX.1-dev-LoRA-AntiBlur": "/model-images/Shakker-Labs_FLUX.1-dev-LoRA-AntiBlur.jpg", + "jeremytai/enso-zen": "/model-images/jeremytai_enso-zen.png", + "UmeAiRT/FLUX.1-dev-LoRA-Modern_Pixel_art": "/model-images/UmeAiRT_FLUX.1-dev-LoRA-Modern_Pixel_art.png", + "glif-loradex-trainer/fabian3000_ethereal": "/model-images/glif-loradex-trainer_fabian3000_ethereal.jpg", + "jayavibhav/pixel-art-style": "/model-images/jayavibhav_pixel-art-style.jpg", + "Svngoku/ancient-africans": "/model-images/Svngoku_ancient-africans.png", + "glif-loradex-trainer/AP123_flux_dev_2DHD_pixel_art": "/model-images/glif-loradex-trainer_AP123_flux_dev_2DHD_pixel_art.jpg", + "fabdream/Comicbook-vintage": "/model-images/fabdream_Comicbook-vintage.png", + "prithivMLmods/Castor-Character-Polygon-Flux-LoRA": "/model-images/prithivMLmods_Castor-Character-Polygon-Flux-LoRA.webp", + "Keltezaa/beauty-in-evil-by-hailoknight": "/model-images/Keltezaa_beauty-in-evil-by-hailoknight.jpeg", + "DeZoomer/AdrianaLima-FluxLora": "/model-images/DeZoomer_AdrianaLima-FluxLora.webp", + "glif-loradex-trainer/i12bp8_appelsiensam_scribble_art_v1": "/model-images/glif-loradex-trainer_i12bp8_appelsiensam_scribble_art_v1.jpg", + "prithivMLmods/Ton618-Epic-Realism-Flux-LoRA": "/model-images/prithivMLmods_Ton618-Epic-Realism-Flux-LoRA.png", + "ali-vilab/In-Context-LoRA": "/model-images/ali-vilab_In-Context-LoRA.png", + "prithivMLmods/3D-Render-Flux-LoRA": "/model-images/prithivMLmods_3D-Render-Flux-LoRA.png", + "strangerzonehf/Flux-Ultimate-LoRA-Collection": "/model-images/strangerzonehf_Flux-Ultimate-LoRA-Collection.png", + "prithivMLmods/Flux.1-Dev-Movie-Boards-LoRA": "/model-images/prithivMLmods_Flux.1-Dev-Movie-Boards-LoRA.png", + "strangerzonehf/Flux-Midjourney-Mix2-LoRA": "/model-images/strangerzonehf_Flux-Midjourney-Mix2-LoRA.png", + "strangerzonehf/Flux-Icon-Kit-LoRA": "/model-images/strangerzonehf_Flux-Icon-Kit-LoRA.png", + "SedatAl/Interior-Flux-Lora": "/model-images/SedatAl_Interior-Flux-Lora.png", + "Keltezaa/fruit_splash_water_flux_lora": "/model-images/Keltezaa_fruit_splash_water_flux_lora.png", + "uriel353/flux-pawg": "/model-images/uriel353_flux-pawg.png", + "CRAWNiiK/Flux-3D-Emojies-LoRA": "/model-images/CRAWNiiK_Flux-3D-Emojies-LoRA.png", + "glif-loradex-trainer/antix82_apu_apustaja_cartoon": "/model-images/glif-loradex-trainer_antix82_apu_apustaja_cartoon.jpg", + "ysmikey/Layerpano3D-FLUX-Panorama-LoRA": "/model-images/ysmikey_Layerpano3D-FLUX-Panorama-LoRA.png", + "Keltezaa/Dynamic_Pose_Uncensored": "/model-images/Keltezaa_Dynamic_Pose_Uncensored.png", + "Norod78/once-upon-a-time-cartoon-style-flux-v3": "/model-images/Norod78_once-upon-a-time-cartoon-style-flux-v3.jpeg", + "Jonjew/MillaJovovich": "/model-images/Jonjew_MillaJovovich.png", + "strangerzonehf/3d-Station-Toon": "/model-images/strangerzonehf_3d-Station-Toon.png", + "InstantX/FLUX.1-dev-LoRA-Makoto-Shinkai": "/model-images/InstantX_FLUX.1-dev-LoRA-Makoto-Shinkai.png", + "glif-loradex-trainer/Swap_agrawal14_kuki_cartoons": "/model-images/glif-loradex-trainer_Swap_agrawal14_kuki_cartoons.jpg", + "strangerzonehf/Flux-Master-Claymation": "/model-images/strangerzonehf_Flux-Master-Claymation.png", + "uriel353/alina-becker": "/model-images/uriel353_alina-becker.png", + "strangerzonehf/Gem-Touch-LoRA-Flux": "/model-images/strangerzonehf_Gem-Touch-LoRA-Flux.png", + "Viktor1717/scandinavian-interior-style1": "/model-images/Viktor1717_scandinavian-interior-style1.jpg", + "un-43/Claymation": "/model-images/un-43_Claymation.png", + "glif-loradex-trainer/Swap_agrawal14_turquoise_bohemian_cartoonz": "/model-images/glif-loradex-trainer_Swap_agrawal14_turquoise_bohemian_cartoonz.jpg", + "RedbeardNZ/Flux.1-Dev-LoRA-HDR-Realism": "/model-images/RedbeardNZ_Flux.1-Dev-LoRA-HDR-Realism.png", + "RedbeardNZ/Flux-Realism-FineDetailed": "/model-images/RedbeardNZ_Flux-Realism-FineDetailed.png", + "rahul7star/ra3hul": "/model-images/rahul7star_ra3hul.jpg", + "openfree/claude-monet": "/model-images/openfree_claude-monet.jpg", + "openfree/pepe": "/model-images/openfree_pepe.jpg", + "openfree/pierre-auguste-renoir": "/model-images/openfree_pierre-auguste-renoir.jpg", + "openfree/paul-cezanne": "/model-images/openfree_paul-cezanne.jpg", + "openfree/van-gogh": "/model-images/openfree_van-gogh.jpg", + "openfree/winslow-homer": "/model-images/openfree_winslow-homer.jpg", + "Wan-AI/Wan2.1-T2V-14B": "/model-images/Wan-AI_Wan2.1-T2V-14B.png", + "Lightricks/LTX-Video-0.9.7-distilled": "/model-images/Lightricks_LTX-Video-0.9.7-distilled.gif", + "Wan-AI/Wan2.1-T2V-1.3B": "/model-images/Wan-AI_Wan2.1-T2V-1.3B.png", + "Lightricks/LTX-Video-0.9.7-dev": "/model-images/Lightricks_LTX-Video-0.9.7-dev.gif", + "CohereLabs/aya-vision-8b": "/model-images/CohereLabs_aya-vision-8b.png", + "CohereLabs/aya-vision-32b": "/model-images/CohereLabs_aya-vision-32b.png" +} \ No newline at end of file diff --git a/static/model-images/AiAF_michemashu55_LoRA_Flux1.png b/static/model-images/AiAF_michemashu55_LoRA_Flux1.png new file mode 100644 index 00000000..6dc23111 Binary files /dev/null and b/static/model-images/AiAF_michemashu55_LoRA_Flux1.png differ diff --git a/static/model-images/BlaireSilver13_BS-Pepe-V1.png b/static/model-images/BlaireSilver13_BS-Pepe-V1.png new file mode 100644 index 00000000..5e5cdf78 Binary files /dev/null and b/static/model-images/BlaireSilver13_BS-Pepe-V1.png differ diff --git a/static/model-images/ByteDance_Hyper-SD.jpg b/static/model-images/ByteDance_Hyper-SD.jpg new file mode 100644 index 00000000..76ce5852 Binary files /dev/null and b/static/model-images/ByteDance_Hyper-SD.jpg differ diff --git a/static/model-images/ByteDance_SDXL-Lightning.jpg b/static/model-images/ByteDance_SDXL-Lightning.jpg new file mode 100644 index 00000000..311d36b9 Binary files /dev/null and b/static/model-images/ByteDance_SDXL-Lightning.jpg differ diff --git a/static/model-images/CRAWNiiK_Flux-3D-Emojies-LoRA.png b/static/model-images/CRAWNiiK_Flux-3D-Emojies-LoRA.png new file mode 100644 index 00000000..49bf6088 Binary files /dev/null and b/static/model-images/CRAWNiiK_Flux-3D-Emojies-LoRA.png differ diff --git a/static/model-images/CiroN2022_escher-blend.jpeg b/static/model-images/CiroN2022_escher-blend.jpeg new file mode 100644 index 00000000..f8d81696 Binary files /dev/null and b/static/model-images/CiroN2022_escher-blend.jpeg differ diff --git a/static/model-images/CohereLabs_aya-vision-32b.png b/static/model-images/CohereLabs_aya-vision-32b.png new file mode 100644 index 00000000..8e09e8bc Binary files /dev/null and b/static/model-images/CohereLabs_aya-vision-32b.png differ diff --git a/static/model-images/CohereLabs_aya-vision-8b.png b/static/model-images/CohereLabs_aya-vision-8b.png new file mode 100644 index 00000000..b6ec1fdf Binary files /dev/null and b/static/model-images/CohereLabs_aya-vision-8b.png differ diff --git a/static/model-images/DeZoomer_AdrianaLima-FluxLora.webp b/static/model-images/DeZoomer_AdrianaLima-FluxLora.webp new file mode 100644 index 00000000..e374ce05 Binary files /dev/null and b/static/model-images/DeZoomer_AdrianaLima-FluxLora.webp differ diff --git a/static/model-images/HiDream-ai_HiDream-I1-Full.jpg b/static/model-images/HiDream-ai_HiDream-I1-Full.jpg new file mode 100644 index 00000000..49bd839e Binary files /dev/null and b/static/model-images/HiDream-ai_HiDream-I1-Full.jpg differ diff --git a/static/model-images/InstantX_FLUX.1-dev-LoRA-Makoto-Shinkai.png b/static/model-images/InstantX_FLUX.1-dev-LoRA-Makoto-Shinkai.png new file mode 100644 index 00000000..32b2272f Binary files /dev/null and b/static/model-images/InstantX_FLUX.1-dev-LoRA-Makoto-Shinkai.png differ diff --git a/static/model-images/Jonjew_MillaJovovich.png b/static/model-images/Jonjew_MillaJovovich.png new file mode 100644 index 00000000..b1aef684 Binary files /dev/null and b/static/model-images/Jonjew_MillaJovovich.png differ diff --git a/static/model-images/Keltezaa_Dynamic_Pose_Uncensored.png b/static/model-images/Keltezaa_Dynamic_Pose_Uncensored.png new file mode 100644 index 00000000..c5e6bf59 Binary files /dev/null and b/static/model-images/Keltezaa_Dynamic_Pose_Uncensored.png differ diff --git a/static/model-images/Keltezaa_beauty-in-evil-by-hailoknight.jpeg b/static/model-images/Keltezaa_beauty-in-evil-by-hailoknight.jpeg new file mode 100644 index 00000000..dc5b284f Binary files /dev/null and b/static/model-images/Keltezaa_beauty-in-evil-by-hailoknight.jpeg differ diff --git a/static/model-images/Keltezaa_fruit_splash_water_flux_lora.png b/static/model-images/Keltezaa_fruit_splash_water_flux_lora.png new file mode 100644 index 00000000..94d964a9 Binary files /dev/null and b/static/model-images/Keltezaa_fruit_splash_water_flux_lora.png differ diff --git a/static/model-images/Lightricks_LTX-Video-0.9.7-dev.gif b/static/model-images/Lightricks_LTX-Video-0.9.7-dev.gif new file mode 100644 index 00000000..a5e8adc2 --- /dev/null +++ b/static/model-images/Lightricks_LTX-Video-0.9.7-dev.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe193ca2f5d61ff2c2707efd22c1e76517f7f070896ceec58c9bec0738c6599c +size 147294304 diff --git a/static/model-images/Lightricks_LTX-Video-0.9.7-distilled.gif b/static/model-images/Lightricks_LTX-Video-0.9.7-distilled.gif new file mode 100644 index 00000000..a5e8adc2 --- /dev/null +++ b/static/model-images/Lightricks_LTX-Video-0.9.7-distilled.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe193ca2f5d61ff2c2707efd22c1e76517f7f070896ceec58c9bec0738c6599c +size 147294304 diff --git a/static/model-images/Norod78_CartoonStyle-flux-lora.jpg b/static/model-images/Norod78_CartoonStyle-flux-lora.jpg new file mode 100644 index 00000000..1157dff7 Binary files /dev/null and b/static/model-images/Norod78_CartoonStyle-flux-lora.jpg differ diff --git a/static/model-images/Norod78_once-upon-a-time-cartoon-style-flux-v3.jpeg b/static/model-images/Norod78_once-upon-a-time-cartoon-style-flux-v3.jpeg new file mode 100644 index 00000000..7f513ad8 Binary files /dev/null and b/static/model-images/Norod78_once-upon-a-time-cartoon-style-flux-v3.jpeg differ diff --git a/static/model-images/RedbeardNZ_Flux-Realism-FineDetailed.png b/static/model-images/RedbeardNZ_Flux-Realism-FineDetailed.png new file mode 100644 index 00000000..a206b31f Binary files /dev/null and b/static/model-images/RedbeardNZ_Flux-Realism-FineDetailed.png differ diff --git a/static/model-images/RedbeardNZ_Flux.1-Dev-LoRA-HDR-Realism.png b/static/model-images/RedbeardNZ_Flux.1-Dev-LoRA-HDR-Realism.png new file mode 100644 index 00000000..706d5716 Binary files /dev/null and b/static/model-images/RedbeardNZ_Flux.1-Dev-LoRA-HDR-Realism.png differ diff --git a/static/model-images/SedatAl_Interior-Flux-Lora.png b/static/model-images/SedatAl_Interior-Flux-Lora.png new file mode 100644 index 00000000..6737b8e8 Binary files /dev/null and b/static/model-images/SedatAl_Interior-Flux-Lora.png differ diff --git a/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-AntiBlur.jpg b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-AntiBlur.jpg new file mode 100644 index 00000000..fefedd24 Binary files /dev/null and b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-AntiBlur.jpg differ diff --git a/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Dark-Fantasy.jpg b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Dark-Fantasy.jpg new file mode 100644 index 00000000..8c91c700 Binary files /dev/null and b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Dark-Fantasy.jpg differ diff --git a/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Logo-Design.jpg b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Logo-Design.jpg new file mode 100644 index 00000000..476a4aa6 Binary files /dev/null and b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Logo-Design.jpg differ diff --git a/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Micro-landscape-on-Mobile-Phone.jpg b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Micro-landscape-on-Mobile-Phone.jpg new file mode 100644 index 00000000..b1c7e10f Binary files /dev/null and b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Micro-landscape-on-Mobile-Phone.jpg differ diff --git a/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Miniature-World.jpg b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Miniature-World.jpg new file mode 100644 index 00000000..c35d210b Binary files /dev/null and b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Miniature-World.jpg differ diff --git a/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Vector-Journey.jpeg b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Vector-Journey.jpeg new file mode 100644 index 00000000..102a2c59 Binary files /dev/null and b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-Vector-Journey.jpeg differ diff --git a/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-add-details.jpeg b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-add-details.jpeg new file mode 100644 index 00000000..1dfabd43 Binary files /dev/null and b/static/model-images/Shakker-Labs_FLUX.1-dev-LoRA-add-details.jpeg differ diff --git a/static/model-images/Svngoku_ancient-africans.png b/static/model-images/Svngoku_ancient-africans.png new file mode 100644 index 00000000..8169a891 Binary files /dev/null and b/static/model-images/Svngoku_ancient-africans.png differ diff --git a/static/model-images/UmeAiRT_FLUX.1-dev-LoRA-Modern_Pixel_art.png b/static/model-images/UmeAiRT_FLUX.1-dev-LoRA-Modern_Pixel_art.png new file mode 100644 index 00000000..ed776c0b Binary files /dev/null and b/static/model-images/UmeAiRT_FLUX.1-dev-LoRA-Modern_Pixel_art.png differ diff --git a/static/model-images/Viktor1717_scandinavian-interior-style1.jpg b/static/model-images/Viktor1717_scandinavian-interior-style1.jpg new file mode 100644 index 00000000..8d9a2723 Binary files /dev/null and b/static/model-images/Viktor1717_scandinavian-interior-style1.jpg differ diff --git a/static/model-images/Wan-AI_Wan2.1-T2V-1.3B.png b/static/model-images/Wan-AI_Wan2.1-T2V-1.3B.png new file mode 100644 index 00000000..0c55854c Binary files /dev/null and b/static/model-images/Wan-AI_Wan2.1-T2V-1.3B.png differ diff --git a/static/model-images/Wan-AI_Wan2.1-T2V-14B.png b/static/model-images/Wan-AI_Wan2.1-T2V-14B.png new file mode 100644 index 00000000..0c55854c Binary files /dev/null and b/static/model-images/Wan-AI_Wan2.1-T2V-14B.png differ diff --git a/static/model-images/ali-vilab_In-Context-LoRA.png b/static/model-images/ali-vilab_In-Context-LoRA.png new file mode 100644 index 00000000..b4acd986 Binary files /dev/null and b/static/model-images/ali-vilab_In-Context-LoRA.png differ diff --git a/static/model-images/alvdansen_littletinies.jpeg b/static/model-images/alvdansen_littletinies.jpeg new file mode 100644 index 00000000..f9732c7f Binary files /dev/null and b/static/model-images/alvdansen_littletinies.jpeg differ diff --git a/static/model-images/black-forest-labs_FLUX.1-dev.jpg b/static/model-images/black-forest-labs_FLUX.1-dev.jpg new file mode 100644 index 00000000..64e7b2ef Binary files /dev/null and b/static/model-images/black-forest-labs_FLUX.1-dev.jpg differ diff --git a/static/model-images/black-forest-labs_FLUX.1-schnell.jpeg b/static/model-images/black-forest-labs_FLUX.1-schnell.jpeg new file mode 100644 index 00000000..bbe49273 Binary files /dev/null and b/static/model-images/black-forest-labs_FLUX.1-schnell.jpeg differ diff --git a/static/model-images/fabdream_Comicbook-vintage.png b/static/model-images/fabdream_Comicbook-vintage.png new file mode 100644 index 00000000..e376797d Binary files /dev/null and b/static/model-images/fabdream_Comicbook-vintage.png differ diff --git a/static/model-images/glif-loradex-trainer_AP123_flux_dev_2DHD_pixel_art.jpg b/static/model-images/glif-loradex-trainer_AP123_flux_dev_2DHD_pixel_art.jpg new file mode 100644 index 00000000..66b3fdc0 Binary files /dev/null and b/static/model-images/glif-loradex-trainer_AP123_flux_dev_2DHD_pixel_art.jpg differ diff --git a/static/model-images/glif-loradex-trainer_Swap_agrawal14_kuki_cartoons.jpg b/static/model-images/glif-loradex-trainer_Swap_agrawal14_kuki_cartoons.jpg new file mode 100644 index 00000000..d7c9457d Binary files /dev/null and b/static/model-images/glif-loradex-trainer_Swap_agrawal14_kuki_cartoons.jpg differ diff --git a/static/model-images/glif-loradex-trainer_Swap_agrawal14_turquoise_bohemian_cartoonz.jpg b/static/model-images/glif-loradex-trainer_Swap_agrawal14_turquoise_bohemian_cartoonz.jpg new file mode 100644 index 00000000..e7988d03 Binary files /dev/null and b/static/model-images/glif-loradex-trainer_Swap_agrawal14_turquoise_bohemian_cartoonz.jpg differ diff --git a/static/model-images/glif-loradex-trainer_antix82_apu_apustaja_cartoon.jpg b/static/model-images/glif-loradex-trainer_antix82_apu_apustaja_cartoon.jpg new file mode 100644 index 00000000..ee88217b Binary files /dev/null and b/static/model-images/glif-loradex-trainer_antix82_apu_apustaja_cartoon.jpg differ diff --git a/static/model-images/glif-loradex-trainer_fabian3000_ethereal.jpg b/static/model-images/glif-loradex-trainer_fabian3000_ethereal.jpg new file mode 100644 index 00000000..3e97a4b9 Binary files /dev/null and b/static/model-images/glif-loradex-trainer_fabian3000_ethereal.jpg differ diff --git a/static/model-images/glif-loradex-trainer_i12bp8_appelsiensam_scribble_art_v1.jpg b/static/model-images/glif-loradex-trainer_i12bp8_appelsiensam_scribble_art_v1.jpg new file mode 100644 index 00000000..03d001f0 Binary files /dev/null and b/static/model-images/glif-loradex-trainer_i12bp8_appelsiensam_scribble_art_v1.jpg differ diff --git a/static/model-images/gokaygokay_Flux-2D-Game-Assets-LoRA.png b/static/model-images/gokaygokay_Flux-2D-Game-Assets-LoRA.png new file mode 100644 index 00000000..bd79c1bf Binary files /dev/null and b/static/model-images/gokaygokay_Flux-2D-Game-Assets-LoRA.png differ diff --git a/static/model-images/goofyai_3d_render_style_xl.jpeg b/static/model-images/goofyai_3d_render_style_xl.jpeg new file mode 100644 index 00000000..9f114c78 Binary files /dev/null and b/static/model-images/goofyai_3d_render_style_xl.jpeg differ diff --git a/static/model-images/h1t_TCD-SDXL-LoRA.png b/static/model-images/h1t_TCD-SDXL-LoRA.png new file mode 100644 index 00000000..133606cc Binary files /dev/null and b/static/model-images/h1t_TCD-SDXL-LoRA.png differ diff --git a/static/model-images/jayavibhav_pixel-art-style.jpg b/static/model-images/jayavibhav_pixel-art-style.jpg new file mode 100644 index 00000000..e6eafc5b Binary files /dev/null and b/static/model-images/jayavibhav_pixel-art-style.jpg differ diff --git a/static/model-images/jeremytai_enso-zen.png b/static/model-images/jeremytai_enso-zen.png new file mode 100644 index 00000000..dec97cd1 Binary files /dev/null and b/static/model-images/jeremytai_enso-zen.png differ diff --git a/static/model-images/multimodalart_isometric-skeumorphic-3d-bnb.png b/static/model-images/multimodalart_isometric-skeumorphic-3d-bnb.png new file mode 100644 index 00000000..6e8d7d41 Binary files /dev/null and b/static/model-images/multimodalart_isometric-skeumorphic-3d-bnb.png differ diff --git a/static/model-images/openfree_claude-monet.jpg b/static/model-images/openfree_claude-monet.jpg new file mode 100644 index 00000000..c961adef Binary files /dev/null and b/static/model-images/openfree_claude-monet.jpg differ diff --git a/static/model-images/openfree_flux-chatgpt-ghibli-lora.jpg b/static/model-images/openfree_flux-chatgpt-ghibli-lora.jpg new file mode 100644 index 00000000..7e2606dc Binary files /dev/null and b/static/model-images/openfree_flux-chatgpt-ghibli-lora.jpg differ diff --git a/static/model-images/openfree_paul-cezanne.jpg b/static/model-images/openfree_paul-cezanne.jpg new file mode 100644 index 00000000..22883eaa Binary files /dev/null and b/static/model-images/openfree_paul-cezanne.jpg differ diff --git a/static/model-images/openfree_pepe.jpg b/static/model-images/openfree_pepe.jpg new file mode 100644 index 00000000..65dd5169 Binary files /dev/null and b/static/model-images/openfree_pepe.jpg differ diff --git a/static/model-images/openfree_pierre-auguste-renoir.jpg b/static/model-images/openfree_pierre-auguste-renoir.jpg new file mode 100644 index 00000000..cc6abf99 Binary files /dev/null and b/static/model-images/openfree_pierre-auguste-renoir.jpg differ diff --git a/static/model-images/openfree_van-gogh.jpg b/static/model-images/openfree_van-gogh.jpg new file mode 100644 index 00000000..790c7329 Binary files /dev/null and b/static/model-images/openfree_van-gogh.jpg differ diff --git a/static/model-images/openfree_winslow-homer.jpg b/static/model-images/openfree_winslow-homer.jpg new file mode 100644 index 00000000..1a1024e4 Binary files /dev/null and b/static/model-images/openfree_winslow-homer.jpg differ diff --git a/static/model-images/ottopilot_PriyaBelleXL.png b/static/model-images/ottopilot_PriyaBelleXL.png new file mode 100644 index 00000000..f55ea44b Binary files /dev/null and b/static/model-images/ottopilot_PriyaBelleXL.png differ diff --git a/static/model-images/ottopilot_WhitneyDelgadoXL.png b/static/model-images/ottopilot_WhitneyDelgadoXL.png new file mode 100644 index 00000000..7853e1ed Binary files /dev/null and b/static/model-images/ottopilot_WhitneyDelgadoXL.png differ diff --git a/static/model-images/piebro_factorio-blueprint-visualizations-sdxl-lora.png b/static/model-images/piebro_factorio-blueprint-visualizations-sdxl-lora.png new file mode 100644 index 00000000..d35651c8 Binary files /dev/null and b/static/model-images/piebro_factorio-blueprint-visualizations-sdxl-lora.png differ diff --git a/static/model-images/prithivMLmods_3D-Render-Flux-LoRA.png b/static/model-images/prithivMLmods_3D-Render-Flux-LoRA.png new file mode 100644 index 00000000..77945c17 Binary files /dev/null and b/static/model-images/prithivMLmods_3D-Render-Flux-LoRA.png differ diff --git a/static/model-images/prithivMLmods_Canopus-LoRA-Flux-UltraRealism-2.0.png b/static/model-images/prithivMLmods_Canopus-LoRA-Flux-UltraRealism-2.0.png new file mode 100644 index 00000000..e7b7da25 Binary files /dev/null and b/static/model-images/prithivMLmods_Canopus-LoRA-Flux-UltraRealism-2.0.png differ diff --git a/static/model-images/prithivMLmods_Castor-Character-Polygon-Flux-LoRA.webp b/static/model-images/prithivMLmods_Castor-Character-Polygon-Flux-LoRA.webp new file mode 100644 index 00000000..45a88582 Binary files /dev/null and b/static/model-images/prithivMLmods_Castor-Character-Polygon-Flux-LoRA.webp differ diff --git a/static/model-images/prithivMLmods_Flux.1-Dev-Frosted-Container-LoRA.png b/static/model-images/prithivMLmods_Flux.1-Dev-Frosted-Container-LoRA.png new file mode 100644 index 00000000..011a8aba Binary files /dev/null and b/static/model-images/prithivMLmods_Flux.1-Dev-Frosted-Container-LoRA.png differ diff --git a/static/model-images/prithivMLmods_Flux.1-Dev-Movie-Boards-LoRA.png b/static/model-images/prithivMLmods_Flux.1-Dev-Movie-Boards-LoRA.png new file mode 100644 index 00000000..3621257d Binary files /dev/null and b/static/model-images/prithivMLmods_Flux.1-Dev-Movie-Boards-LoRA.png differ diff --git a/static/model-images/prithivMLmods_Retro-Pixel-Flux-LoRA.png b/static/model-images/prithivMLmods_Retro-Pixel-Flux-LoRA.png new file mode 100644 index 00000000..532d03b7 Binary files /dev/null and b/static/model-images/prithivMLmods_Retro-Pixel-Flux-LoRA.png differ diff --git a/static/model-images/prithivMLmods_Ton618-Epic-Realism-Flux-LoRA.png b/static/model-images/prithivMLmods_Ton618-Epic-Realism-Flux-LoRA.png new file mode 100644 index 00000000..b9252116 Binary files /dev/null and b/static/model-images/prithivMLmods_Ton618-Epic-Realism-Flux-LoRA.png differ diff --git a/static/model-images/rahul7star_ra3hul.jpg b/static/model-images/rahul7star_ra3hul.jpg new file mode 100644 index 00000000..fe96137b Binary files /dev/null and b/static/model-images/rahul7star_ra3hul.jpg differ diff --git a/static/model-images/roelfrenkema_flux1.lora.donaldtrump.jpg b/static/model-images/roelfrenkema_flux1.lora.donaldtrump.jpg new file mode 100644 index 00000000..1e82a5af Binary files /dev/null and b/static/model-images/roelfrenkema_flux1.lora.donaldtrump.jpg differ diff --git a/static/model-images/semiosphere_the_artist_flux.png b/static/model-images/semiosphere_the_artist_flux.png new file mode 100644 index 00000000..61549731 Binary files /dev/null and b/static/model-images/semiosphere_the_artist_flux.png differ diff --git a/static/model-images/stabilityai_stable-diffusion-3-medium-diffusers.jpg b/static/model-images/stabilityai_stable-diffusion-3-medium-diffusers.jpg new file mode 100644 index 00000000..ca067b66 Binary files /dev/null and b/static/model-images/stabilityai_stable-diffusion-3-medium-diffusers.jpg differ diff --git a/static/model-images/stabilityai_stable-diffusion-3.5-large-turbo.png b/static/model-images/stabilityai_stable-diffusion-3.5-large-turbo.png new file mode 100644 index 00000000..45139bc4 Binary files /dev/null and b/static/model-images/stabilityai_stable-diffusion-3.5-large-turbo.png differ diff --git a/static/model-images/stabilityai_stable-diffusion-3.5-large.png b/static/model-images/stabilityai_stable-diffusion-3.5-large.png new file mode 100644 index 00000000..952e656a Binary files /dev/null and b/static/model-images/stabilityai_stable-diffusion-3.5-large.png differ diff --git a/static/model-images/stabilityai_stable-diffusion-3.5-medium.jpg b/static/model-images/stabilityai_stable-diffusion-3.5-medium.jpg new file mode 100644 index 00000000..648b48b7 Binary files /dev/null and b/static/model-images/stabilityai_stable-diffusion-3.5-medium.jpg differ diff --git a/static/model-images/stabilityai_stable-diffusion-xl-base-1.0.png b/static/model-images/stabilityai_stable-diffusion-xl-base-1.0.png new file mode 100644 index 00000000..ac76c34b Binary files /dev/null and b/static/model-images/stabilityai_stable-diffusion-xl-base-1.0.png differ diff --git a/static/model-images/strangerzonehf_3d-Station-Toon.png b/static/model-images/strangerzonehf_3d-Station-Toon.png new file mode 100644 index 00000000..c81b275e Binary files /dev/null and b/static/model-images/strangerzonehf_3d-Station-Toon.png differ diff --git a/static/model-images/strangerzonehf_Flux-Claymation-XC-LoRA.png b/static/model-images/strangerzonehf_Flux-Claymation-XC-LoRA.png new file mode 100644 index 00000000..c2c6abb8 Binary files /dev/null and b/static/model-images/strangerzonehf_Flux-Claymation-XC-LoRA.png differ diff --git a/static/model-images/strangerzonehf_Flux-Icon-Kit-LoRA.png b/static/model-images/strangerzonehf_Flux-Icon-Kit-LoRA.png new file mode 100644 index 00000000..82b45150 Binary files /dev/null and b/static/model-images/strangerzonehf_Flux-Icon-Kit-LoRA.png differ diff --git a/static/model-images/strangerzonehf_Flux-Master-Claymation.png b/static/model-images/strangerzonehf_Flux-Master-Claymation.png new file mode 100644 index 00000000..da16453a Binary files /dev/null and b/static/model-images/strangerzonehf_Flux-Master-Claymation.png differ diff --git a/static/model-images/strangerzonehf_Flux-Midjourney-Mix2-LoRA.png b/static/model-images/strangerzonehf_Flux-Midjourney-Mix2-LoRA.png new file mode 100644 index 00000000..bd9d689e Binary files /dev/null and b/static/model-images/strangerzonehf_Flux-Midjourney-Mix2-LoRA.png differ diff --git a/static/model-images/strangerzonehf_Flux-Super-Realism-LoRA.png b/static/model-images/strangerzonehf_Flux-Super-Realism-LoRA.png new file mode 100644 index 00000000..ce0dc19f Binary files /dev/null and b/static/model-images/strangerzonehf_Flux-Super-Realism-LoRA.png differ diff --git a/static/model-images/strangerzonehf_Flux-Ultimate-LoRA-Collection.png b/static/model-images/strangerzonehf_Flux-Ultimate-LoRA-Collection.png new file mode 100644 index 00000000..9759786b Binary files /dev/null and b/static/model-images/strangerzonehf_Flux-Ultimate-LoRA-Collection.png differ diff --git a/static/model-images/strangerzonehf_Gem-Touch-LoRA-Flux.png b/static/model-images/strangerzonehf_Gem-Touch-LoRA-Flux.png new file mode 100644 index 00000000..69133b4c Binary files /dev/null and b/static/model-images/strangerzonehf_Gem-Touch-LoRA-Flux.png differ diff --git a/static/model-images/un-43_Claymation.png b/static/model-images/un-43_Claymation.png new file mode 100644 index 00000000..c2c6abb8 Binary files /dev/null and b/static/model-images/un-43_Claymation.png differ diff --git a/static/model-images/uriel353_alina-becker.png b/static/model-images/uriel353_alina-becker.png new file mode 100644 index 00000000..117387a5 Binary files /dev/null and b/static/model-images/uriel353_alina-becker.png differ diff --git a/static/model-images/ysmikey_Layerpano3D-FLUX-Panorama-LoRA.png b/static/model-images/ysmikey_Layerpano3D-FLUX-Panorama-LoRA.png new file mode 100644 index 00000000..c37326e8 Binary files /dev/null and b/static/model-images/ysmikey_Layerpano3D-FLUX-Panorama-LoRA.png differ diff --git a/static/placeholder.mp4 b/static/placeholder.mp4 new file mode 100644 index 00000000..22378b52 Binary files /dev/null and b/static/placeholder.mp4 differ diff --git a/vite.config.ts b/vite.config.ts index c1c8ddce..8076deb2 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -12,7 +12,9 @@ export default defineConfig({ sveltekit(), Icons({ compiler: "svelte", autoInstall: true }), ], - server: { allowedHosts: isDev ? true : undefined }, + server: { + allowedHosts: isDev ? true : undefined, + }, test: { workspace: [ {