diff options
Diffstat (limited to 'frontend')
167 files changed, 6157 insertions, 6119 deletions
diff --git a/frontend/.eslintignore b/frontend/.eslintignore deleted file mode 100644 index fb4d584..0000000 --- a/frontend/.eslintignore +++ /dev/null @@ -1,16 +0,0 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package -.env -.env.* -!.env.example -/coverage - -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml -package-lock.json -yarn.lock - -/src/gql
\ No newline at end of file diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs deleted file mode 100644 index 352fa79..0000000 --- a/frontend/.eslintrc.cjs +++ /dev/null @@ -1,50 +0,0 @@ -module.exports = { - root: true, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended-type-checked', - 'plugin:@typescript-eslint/stylistic-type-checked', - 'plugin:svelte/recommended', - 'prettier' - ], - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - ignorePatterns: ['*.cjs'], - parserOptions: { - sourceType: 'module', - ecmaVersion: 2022, - extraFileExtensions: ['.svelte'], - project: true, - tsconfigRootDir: __dirname - }, - env: { - browser: true, - es2022: true, - node: true - }, - overrides: [ - { - files: ['*.svelte'], - parser: 'svelte-eslint-parser', - parserOptions: { - parser: '@typescript-eslint/parser' - }, - rules: { - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-enum-comparison': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - 'no-undef': 'off' - } - }, - { - files: ['codegen.ts', 'svelte.config.js'], - extends: ['plugin:@typescript-eslint/disable-type-checked'] - } - ], - rules: { - 'no-console': 'warn', - eqeqeq: 'error' - } -}; diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..308fe8a --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,37 @@ +import { includeIgnoreFile } from '@eslint/compat'; +import js from '@eslint/js'; +import prettier from 'eslint-config-prettier'; +import svelte from 'eslint-plugin-svelte'; +import globals from 'globals'; +import { fileURLToPath } from 'node:url'; +import ts from 'typescript-eslint'; +import svelteConfig from './svelte.config.js'; + +const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); + +export default ts.config( + includeIgnoreFile(gitignorePath), + js.configs.recommended, + ...ts.configs.recommended, + ...svelte.configs.recommended, + prettier, + ...svelte.configs.prettier, + { + languageOptions: { + globals: { ...globals.browser, ...globals.node } + }, + rules: { 'no-undef': 'off' } + }, + { + files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], + ignores: ['eslint.config.js', 'svelte.config.js'], + languageOptions: { + parserOptions: { + projectService: true, + extraFileExtensions: ['.svelte'], + parser: ts.parser, + svelteConfig + } + } + } +); diff --git a/frontend/operations.graphql b/frontend/operations.graphql index 5c41080..09364f6 100644 --- a/frontend/operations.graphql +++ b/frontend/operations.graphql @@ -22,6 +22,8 @@ fragment Comic on Comic { title originalTitle favourite + date + pageCount cover { ...Image } @@ -401,7 +403,7 @@ query scrapeComic($id: Int!, $scraper: String!) { } } -query frontpage { +query frontpage($seed: Int!) { recent: comics(pagination: { items: 6 }, sort: { on: CREATED_AT, direction: DESCENDING }) { edges { ...Comic @@ -411,7 +413,7 @@ query frontpage { favourites: comics( pagination: { items: 6 } filter: { include: { favourite: true } } - sort: { on: RANDOM } + sort: { on: RANDOM, seed: $seed } ) { edges { ...Comic @@ -421,7 +423,7 @@ query frontpage { bookmarked: comics( pagination: { items: 6 } filter: { include: { bookmarked: true } } - sort: { on: RANDOM } + sort: { on: RANDOM, seed: $seed } ) { edges { ...Comic @@ -430,6 +432,31 @@ query frontpage { } } +query statistics { + statistics { + total { + archives + artists + characters + circles + comic { + artists + characters + circles + tags + worlds + } + comics + images + scrapers + pages + namespaces + tags + worlds + } + } +} + mutation addComic($input: AddComicInput!) { addComic(input: $input) { ... on AddComicSuccess { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cdbb3e4..f0803f2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,57 +1,60 @@ { "name": "hircine", - "version": "0.2.0", + "version": "0.4.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hircine", - "version": "0.2.0", + "version": "0.4.4", "license": "ISC", "dependencies": { "@jsonurl/jsonurl": "^1.1.8", "@urql/svelte": "^4.2.2", "filesize": "^10.1.6", "graphql": "npm:graphql-web-lite@^16.6.0", - "svelecte": "^4.4.1", - "svelte-modals": "^1.3.0" + "svelecte": "^5.0.0", + "svelte-modals": "^2.0.0" }, "devDependencies": { + "@eslint/compat": "^1.2.5", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.19.0", "@graphql-codegen/cli": "^5.0.3", "@graphql-codegen/typed-document-node": "^5.0.11", "@graphql-codegen/typescript-operations": "^4.3.1", "@iconify-json/material-symbols": "^1.2.6", - "@iconify/tailwind": "^1.1.3", + "@iconify/tailwind4": "^1.0.3", "@sveltejs/adapter-static": "^3.0.6", "@sveltejs/kit": "^2.8.1", - "@sveltejs/vite-plugin-svelte": "^3.0.2", - "@typescript-eslint/eslint-plugin": "^8.14.0", - "@typescript-eslint/parser": "^8.14.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@tailwindcss/vite": "^4.0.6", "@zerodevx/svelte-toast": "^0.9.6", - "autoprefixer": "^10.4.20", "date-fns": "^4.1.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.46.0", + "eslint-config-prettier": "^10.0.0", + "eslint-plugin-svelte": "^3.0.0", "fast-deep-equal": "^3.1.3", + "globals": "^16.0.0", + "jsdom": "^26.0.0", "npm-check-updates": "^17.1.11", - "postcss": "^8.4.49", "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.2.8", - "prettier-plugin-tailwindcss": "^0.6.8", - "svelte": "^4.2.12", - "svelte-check": "^3.6.6", - "tailwindcss": "^3.4.14", + "prettier-plugin-tailwindcss": "^0.6.11", + "svelte": "^5.5.0", + "svelte-check": "^4.0.0", + "tailwindcss": "^4.0.0", "tslib": "^2.8.1", "typescript": "^5.6.3", - "vite": "^5.4.11", - "vitest": "^2.1.5" + "typescript-eslint": "^8.28.0", + "vite": "^6.0.0", + "vitest": "^3.0.0" } }, "node_modules/@0no-co/graphql.web": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.11.tgz", - "integrity": "sha512-xuSJ9WXwTmtngWkbdEoopMo6F8NLtjy84UNAMsAr5C3/2SgAL/dEU10TMqTIsipqPQ8HA/7WzeqQ9DEQxSvPPA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.1.2.tgz", + "integrity": "sha512-N2NGsU5FLBhT8NZ+3l2YrzZSHITjNXNuDhC4iDiikv0IujaJ0Xc6xIxQZ/Ek3Cb+rgPjnLHYyJm11tInuJn+cw==", "license": "MIT", "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" @@ -62,19 +65,6 @@ } } }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -88,30 +78,47 @@ "node": ">=6.0.0" } }, + "node_modules/@antfu/install-pkg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.0.0.tgz", + "integrity": "sha512-xvX6P/lo1B3ej0OsaErAjqgFYzYVcJpamjLAFLYh9vRJngBrMoUG7aVnrGTeqM7yxbyTD5p3F2+0/QUEh8Vzhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-manager-detector": "^0.2.8", + "tinyexec": "^0.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/utils": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", + "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@ardatan/relay-compiler": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz", - "integrity": "sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q==", + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.3.tgz", + "integrity": "sha512-mBDFOGvAoVlWaWqs3hm1AciGHSQE1rqFc/liZTyYz/Oek9yZdT5H26pH2zAFuEiTiBVPPyMuqf5VjOFPI2DGsQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.14.0", - "@babel/generator": "^7.14.0", - "@babel/parser": "^7.14.0", - "@babel/runtime": "^7.0.0", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.0.0", - "babel-preset-fbjs": "^3.4.0", + "@babel/generator": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/runtime": "^7.26.10", "chalk": "^4.0.0", "fb-watchman": "^2.0.0", - "fbjs": "^3.0.0", - "glob": "^7.1.1", "immutable": "~3.7.6", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "relay-runtime": "12.0.0", - "signedsource": "^1.0.0", - "yargs": "^15.3.1" + "signedsource": "^1.0.0" }, "bin": { "relay-compiler": "bin/relay-compiler" @@ -120,131 +127,27 @@ "graphql": "*" } }, - "node_modules/@ardatan/relay-compiler/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/@ardatan/relay-compiler/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ardatan/relay-compiler/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ardatan/relay-compiler/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ardatan/relay-compiler/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/@asamuzakjp/css-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.2.tgz", + "integrity": "sha512-nwgc7jPn3LpZ4JWsoHtuwBsad1qSSLDDX634DdG0PBJofIuIEtSWk4KkRmuXyu178tjuHAbwiMNNzwqIyLYxZw==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" + "@csstools/css-calc": "^2.1.2", + "@csstools/css-color-parser": "^3.0.8", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" } }, - "node_modules/@ardatan/relay-compiler/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, "license": "ISC" }, - "node_modules/@ardatan/relay-compiler/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@ardatan/relay-compiler/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@ardatan/sync-fetch": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz", - "integrity": "sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA==", - "dev": true, - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.1" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -261,9 +164,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, "license": "MIT", "engines": { @@ -271,22 +174,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -302,14 +205,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -318,27 +221,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.8", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -348,42 +238,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", - "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.25.9", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", - "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", @@ -416,75 +270,16 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", - "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", - "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", - "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", - "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", @@ -516,27 +311,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -545,74 +340,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", - "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", @@ -629,438 +356,240 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", - "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "node_modules/@babel/runtime": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", - "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", - "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/traverse": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", - "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/template": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", - "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz", - "integrity": "sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-syntax-flow": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", - "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", - "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", - "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", - "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", - "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", - "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", - "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", - "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=4" } }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", - "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", - "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-syntax-jsx": "^7.25.9", - "@babel/types": "^7.25.9" - }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=18" } }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", - "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "node_modules/@csstools/css-calc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz", + "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, "engines": { - "node": ">=6.9.0" + "node": ">=18" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" } }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", - "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "node_modules/@csstools/css-color-parser": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz", + "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=18" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" } }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", - "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, "engines": { - "node": ">=6.9.0" + "node": ">=18" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@csstools/css-tokenizer": "^3.0.3" } }, - "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "node_modules/@envelop/core": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@envelop/core/-/core-5.2.3.tgz", + "integrity": "sha512-KfoGlYD/XXQSc3BkM1/k15+JQbkQ4ateHazeZoWl9P71FsLTDXSjGy6j7QqfhpIDSbxNISqhPMfZHYSbDFOofQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@envelop/instrumentation": "^1.0.0", + "@envelop/types": "^5.2.1", + "@whatwg-node/promise-helpers": "^1.2.4", + "tslib": "^2.5.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "node_modules/@envelop/instrumentation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@envelop/instrumentation/-/instrumentation-1.0.0.tgz", + "integrity": "sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@whatwg-node/promise-helpers": "^1.2.1", + "tslib": "^2.5.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "node_modules/@envelop/types": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@envelop/types/-/types-5.2.1.tgz", + "integrity": "sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", "cpu": [ "ppc64" ], @@ -1071,13 +600,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", "cpu": [ "arm" ], @@ -1088,13 +617,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", "cpu": [ "arm64" ], @@ -1105,13 +634,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", "cpu": [ "x64" ], @@ -1122,13 +651,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", "cpu": [ "arm64" ], @@ -1139,13 +668,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", "cpu": [ "x64" ], @@ -1156,13 +685,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", "cpu": [ "arm64" ], @@ -1173,13 +702,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", "cpu": [ "x64" ], @@ -1190,13 +719,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", "cpu": [ "arm" ], @@ -1207,13 +736,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", "cpu": [ "arm64" ], @@ -1224,13 +753,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", "cpu": [ "ia32" ], @@ -1241,13 +770,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", "cpu": [ "loong64" ], @@ -1258,13 +787,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", "cpu": [ "mips64el" ], @@ -1275,13 +804,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", "cpu": [ "ppc64" ], @@ -1292,13 +821,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", "cpu": [ "riscv64" ], @@ -1309,13 +838,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", "cpu": [ "s390x" ], @@ -1326,13 +855,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", "cpu": [ "x64" ], @@ -1343,13 +872,30 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", "cpu": [ "x64" ], @@ -1360,13 +906,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", "cpu": [ "x64" ], @@ -1377,13 +940,13 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", "cpu": [ "x64" ], @@ -1394,13 +957,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", "cpu": [ "arm64" ], @@ -1411,13 +974,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", "cpu": [ "ia32" ], @@ -1428,13 +991,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", "cpu": [ "x64" ], @@ -1445,13 +1008,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", + "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", "dev": true, "license": "MIT", "dependencies": { @@ -1467,6 +1030,19 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", @@ -1477,14 +1053,32 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/compat": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.8.tgz", + "integrity": "sha512-LqCYHdWL/QqKIJuZ/ucMAv8d4luKGs4oCPgpt8mWztQAtPrHfXKQ/XAUc8ljCHAfJCn6SvkpTcGt5Tsh8saowA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -1492,44 +1086,33 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "license": "Apache-2.0", "engines": { - "node": "*" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", - "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1550,17 +1133,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -1574,23 +1146,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/js": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", - "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", + "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", "dev": true, "license": "MIT", "engines": { @@ -1598,9 +1157,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1608,18 +1167,39 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", - "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.13.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", + "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", + "dev": true, + "license": "MIT" + }, "node_modules/@graphql-codegen/add": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-5.0.3.tgz", @@ -1642,16 +1222,16 @@ "license": "0BSD" }, "node_modules/@graphql-codegen/cli": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-5.0.3.tgz", - "integrity": "sha512-ULpF6Sbu2d7vNEOgBtE9avQp2oMgcPY/QBYcCqk0Xru5fz+ISjcovQX29V7CS7y5wWBRzNLoXwJQGeEyWbl05g==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-5.0.5.tgz", + "integrity": "sha512-9p9SI5dPhJdyU+O6p1LUqi5ajDwpm6pUhutb1fBONd0GZltLFwkgWFiFtM6smxkYXlYVzw61p1kTtwqsuXO16w==", "dev": true, "license": "MIT", "dependencies": { "@babel/generator": "^7.18.13", "@babel/template": "^7.18.10", "@babel/types": "^7.18.13", - "@graphql-codegen/client-preset": "^4.4.0", + "@graphql-codegen/client-preset": "^4.6.0", "@graphql-codegen/core": "^4.0.2", "@graphql-codegen/plugin-helpers": "^5.0.3", "@graphql-tools/apollo-engine-loader": "^8.0.0", @@ -1664,7 +1244,7 @@ "@graphql-tools/prisma-loader": "^8.0.0", "@graphql-tools/url-loader": "^8.0.0", "@graphql-tools/utils": "^10.0.0", - "@whatwg-node/fetch": "^0.9.20", + "@whatwg-node/fetch": "^0.10.0", "chalk": "^4.1.0", "cosmiconfig": "^8.1.3", "debounce": "^1.2.0", @@ -1704,21 +1284,21 @@ } }, "node_modules/@graphql-codegen/client-preset": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.5.0.tgz", - "integrity": "sha512-0fFGSjpDhB7Jp6v+FQWDIeNJhL8VEiy3zeazyus3mGUELPaRQsoos2NczkDWnyMjSB1NHn4GrI53DB4TXkTAog==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.8.0.tgz", + "integrity": "sha512-IVtTl7GsPMbQihk5+l5fDYksnPPOoC52sKxzquyIyuecZLEB7W3nNLV29r6+y+tjXTRPA774FR7CHGA2adzhjw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/template": "^7.20.7", "@graphql-codegen/add": "^5.0.3", - "@graphql-codegen/gql-tag-operations": "4.0.11", + "@graphql-codegen/gql-tag-operations": "4.0.17", "@graphql-codegen/plugin-helpers": "^5.1.0", - "@graphql-codegen/typed-document-node": "^5.0.11", - "@graphql-codegen/typescript": "^4.1.1", - "@graphql-codegen/typescript-operations": "^4.3.1", - "@graphql-codegen/visitor-plugin-common": "^5.5.0", + "@graphql-codegen/typed-document-node": "^5.1.1", + "@graphql-codegen/typescript": "^4.1.6", + "@graphql-codegen/typescript-operations": "^4.6.0", + "@graphql-codegen/visitor-plugin-common": "^5.8.0", "@graphql-tools/documents": "^1.0.0", "@graphql-tools/utils": "^10.0.0", "@graphql-typed-document-node/core": "3.2.0", @@ -1728,7 +1308,8 @@ "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql-sock": "^1.0.0" } }, "node_modules/@graphql-codegen/client-preset/node_modules/tslib": { @@ -1762,14 +1343,14 @@ "license": "0BSD" }, "node_modules/@graphql-codegen/gql-tag-operations": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.11.tgz", - "integrity": "sha512-EUQuBsYB5RtNlzBb/O0nJvbWC8HvPRWwVTHRf0ElOoQlJfRgfDom2GWmEM5hXa2afzMqB7AWxOH24ibOqiYnMQ==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.17.tgz", + "integrity": "sha512-2pnvPdIG6W9OuxkrEZ6hvZd142+O3B13lvhrZ48yyEBh2ujtmKokw0eTwDHtlXUqjVS0I3q7+HB2y12G/m69CA==", "dev": true, "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.1.0", - "@graphql-codegen/visitor-plugin-common": "5.5.0", + "@graphql-codegen/visitor-plugin-common": "5.8.0", "@graphql-tools/utils": "^10.0.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" @@ -1839,14 +1420,14 @@ "license": "0BSD" }, "node_modules/@graphql-codegen/typed-document-node": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.0.11.tgz", - "integrity": "sha512-btENKrSIUZ5UllS8sFhVZ+Y91VL0knK9gHxW/6/WzaCTxBQ+wOk07vQNeoWlvMrkl0QeUsGt6YvSo0SoPtsKdA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.1.1.tgz", + "integrity": "sha512-Bp/BrMZDKRwzuVeLv+pSljneqONM7gqu57ZaV34Jbncu2hZWMRDMfizTKghoEwwZbRCYYfJO9tA0sYVVIfI1kg==", "dev": true, "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.1.0", - "@graphql-codegen/visitor-plugin-common": "5.5.0", + "@graphql-codegen/visitor-plugin-common": "5.8.0", "auto-bind": "~4.0.0", "change-case-all": "1.0.15", "tslib": "~2.6.0" @@ -1866,15 +1447,15 @@ "license": "0BSD" }, "node_modules/@graphql-codegen/typescript": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.1.1.tgz", - "integrity": "sha512-+o5LOT71K9hdO4lDVnRGkkET5RdlKvxlQGug8dZgRGrhE2/xoPBsKfLhg9AoJGYMauNZxKj3blABQxHOKEku6Q==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.1.6.tgz", + "integrity": "sha512-vpw3sfwf9A7S+kIUjyFxuvrywGxd4lmwmyYnnDVjVE4kSQ6Td3DpqaPTy8aNQ6O96vFoi/bxbZS2BW49PwSUUA==", "dev": true, "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.1.0", "@graphql-codegen/schema-ast": "^4.0.2", - "@graphql-codegen/visitor-plugin-common": "5.5.0", + "@graphql-codegen/visitor-plugin-common": "5.8.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, @@ -1886,15 +1467,15 @@ } }, "node_modules/@graphql-codegen/typescript-operations": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-4.3.1.tgz", - "integrity": "sha512-yW5Iia6IK1VKiPm3oeukYMQN5pEBLwRlG8ZzQA9beeLQ8PskKyz6mjar6U7dJ2hc8pv/qT4R8kcJOQ2RloniAQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-4.6.0.tgz", + "integrity": "sha512-/EltSdE/uPoEAblRTVLABVDhsrE//Kl3pCflyG1PWl4gWL9/OzQXYGjo6TF6bPMVn/QBWoO0FeboWf+bk84SXA==", "dev": true, "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.1.0", - "@graphql-codegen/typescript": "^4.1.1", - "@graphql-codegen/visitor-plugin-common": "5.5.0", + "@graphql-codegen/typescript": "^4.1.6", + "@graphql-codegen/visitor-plugin-common": "5.8.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, @@ -1902,7 +1483,8 @@ "node": ">=16" }, "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql-sock": "^1.0.0" } }, "node_modules/@graphql-codegen/typescript-operations/node_modules/tslib": { @@ -1920,9 +1502,9 @@ "license": "0BSD" }, "node_modules/@graphql-codegen/visitor-plugin-common": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.5.0.tgz", - "integrity": "sha512-FSkxe/o4qKbpK+ipIT/jxZLYH0+3+XdIrJWsKlCW9wwJMF9mEJLJtzZNcxHSjz7+Eny6SUElAT2dqZ5XByxkog==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.8.0.tgz", + "integrity": "sha512-lC1E1Kmuzi3WZUlYlqB4fP6+CvbKH9J+haU1iWmgsBx5/sO2ROeXJG4Dmt8gP03bI2BwjiwV5WxCEMlyeuzLnA==", "dev": true, "license": "MIT", "dependencies": { @@ -1951,16 +1533,26 @@ "dev": true, "license": "0BSD" }, + "node_modules/@graphql-hive/signal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@graphql-hive/signal/-/signal-1.0.0.tgz", + "integrity": "sha512-RiwLMc89lTjvyLEivZ/qxAC5nBHoS2CtsWFSOsN35sxG9zoo5Z+JsFHM8MlvmO9yt+MJNIyC5MLE1rsbOphlag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@graphql-tools/apollo-engine-loader": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.4.tgz", - "integrity": "sha512-dwFhFDvqRr1+UkSPVYciz202a0TInKe1at+eS3YYoirg2FacaCuQDeGWG4w3rLJQXKnGhFAdFUfgBMt2ZqXxYA==", + "version": "8.0.20", + "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.20.tgz", + "integrity": "sha512-m5k9nXSyjq31yNsEqDXLyykEjjn3K3Mo73oOKI+Xjy8cpnsgbT4myeUJIYYQdLrp7fr9Y9p7ZgwT5YcnwmnAbA==", "dev": true, "license": "MIT", "dependencies": { - "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/utils": "^10.8.6", "@whatwg-node/fetch": "^0.10.0", + "sync-fetch": "0.6.0-2", "tslib": "^2.4.0" }, "engines": { @@ -1970,64 +1562,34 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/fetch": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.1.tgz", - "integrity": "sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@whatwg-node/node-fetch": "^0.7.1", - "urlpattern-polyfill": "^10.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.2.tgz", - "integrity": "sha512-OAAEIbyspvQwkcRGutYN3D0a+hzQogvcZ7I3hf6vg742ZEq52yMJTGtkwjl3KZRmzzUltd/oEMxEGsXFLjnuLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.6.3" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@graphql-tools/batch-execute": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.6.tgz", - "integrity": "sha512-S0mLTUJQJuItGmwouYZyXeFaRWOVmVCAMLi33M5PuZbnsoTwKMB/YPPkAQ72ay3GfclnW66XcO4EClbVynw7eQ==", + "version": "9.0.15", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.15.tgz", + "integrity": "sha512-qlWUl6yi87FU5WvyJ0uD81R4Y30oQIuW3mJCjOrEvifyT+f/rEqSZFOhYrofYoZAoTcwqOhy6WgH+b9+AtRYjA==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.5.6", - "dataloader": "^2.2.2", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "@graphql-tools/utils": "^10.8.1", + "@whatwg-node/promise-helpers": "^1.3.0", + "dataloader": "^2.2.3", + "tslib": "^2.8.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/code-file-loader": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.5.tgz", - "integrity": "sha512-2witIKEmcFbJH+PFIsO60Gy4RtaPFP1QageDdd4Ra77DN4kHKbPOjlbaPWynECySz7yIZfUEkVO2PLqrb/h5BQ==", + "version": "8.1.20", + "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.20.tgz", + "integrity": "sha512-GzIbjjWJIc04KWnEr8VKuPe0FA2vDTlkaeub5p4lLimljnJ6C0QSkOyCUnFmsB9jetQcHm0Wfmn/akMnFUG+wA==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/graphql-tag-pluck": "8.3.4", - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/graphql-tag-pluck": "8.3.19", + "@graphql-tools/utils": "^10.8.6", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -2040,23 +1602,24 @@ } }, "node_modules/@graphql-tools/delegate": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.1.2.tgz", - "integrity": "sha512-2XXJdxjud0ROiKxIiieCx4SxzSjYNdCz1bVDrI6+nTFxV5kB15OnJU6jvom44kv+NJo4Dym5GOJNqlBEjxoFVA==", + "version": "10.2.17", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.2.17.tgz", + "integrity": "sha512-z+LpZrTQCEXA4fbdJcSsvhaMqT4xi/O8B0mP30ENGyTbSfa20QamOQx9jgCiw2ii/ucwxfGMhygwlpZG36EU4w==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/batch-execute": "^9.0.6", - "@graphql-tools/executor": "^1.3.3", - "@graphql-tools/schema": "^10.0.8", - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/batch-execute": "^9.0.15", + "@graphql-tools/executor": "^1.4.7", + "@graphql-tools/schema": "^10.0.11", + "@graphql-tools/utils": "^10.8.1", "@repeaterjs/repeater": "^3.0.6", - "dataloader": "^2.2.2", + "@whatwg-node/promise-helpers": "^1.3.0", + "dataloader": "^2.2.3", "dset": "^3.1.2", - "tslib": "^2.5.0" + "tslib": "^2.8.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" @@ -2080,17 +1643,18 @@ } }, "node_modules/@graphql-tools/executor": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.3.3.tgz", - "integrity": "sha512-lpkgokF6vjGHNluANOTsWoSM0vuvUuVpjY1810tvM6Vlyoq2tJ+nmqweGbMsq/GLhmZQP1lY/nOkj2zPJXLWiw==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.4.7.tgz", + "integrity": "sha512-U0nK9jzJRP9/9Izf1+0Gggd6K6RNRsheFo1gC/VWzfnsr0qjcOSS9qTjY0OTC5iTPt4tQ+W5Zpw/uc7mebI6aA==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.5.6", - "@graphql-typed-document-node/core": "3.2.0", + "@graphql-tools/utils": "^10.8.6", + "@graphql-typed-document-node/core": "^3.2.0", "@repeaterjs/repeater": "^3.0.4", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.4.0" }, "engines": { "node": ">=16.0.0" @@ -2099,87 +1663,77 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/executor-graphql-ws": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-1.3.2.tgz", - "integrity": "sha512-m+7+g3dSyaomuJAgDoG/9RcZC5/hGEpDQjmKmbLf/WvGdv5fLJNsuoJ7pIjlT5r7wQJNjEPGoHeh9pD/YykRww==", + "node_modules/@graphql-tools/executor-common": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-0.0.4.tgz", + "integrity": "sha512-SEH/OWR+sHbknqZyROCFHcRrbZeUAyjCsgpVWCRjqjqRbiJiXq6TxNIIOmpXgkrXWW/2Ev4Wms6YSGJXjdCs6Q==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.5.6", - "@types/ws": "^8.0.0", - "graphql-ws": "^5.14.0", - "isomorphic-ws": "^5.0.0", - "tslib": "^2.4.0", - "ws": "^8.17.1" + "@envelop/core": "^5.2.3", + "@graphql-tools/utils": "^10.8.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/executor-http": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.1.9.tgz", - "integrity": "sha512-dJRj78QEGNNnlhkhqPUG9z+1uAr7znZ4dzabEVgY5uSXTmUIFcTKpOGYv2/QAuvyqGN40XxbcdVRJta6XHX2BQ==", + "node_modules/@graphql-tools/executor-graphql-ws": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-2.0.5.tgz", + "integrity": "sha512-gI/D9VUzI1Jt1G28GYpvm5ckupgJ5O8mi5Y657UyuUozX34ErfVdZ81g6oVcKFQZ60LhCzk7jJeykK48gaLhDw==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.5.6", - "@repeaterjs/repeater": "^3.0.4", - "@whatwg-node/fetch": "^0.10.0", - "extract-files": "^11.0.0", - "meros": "^1.2.1", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "@graphql-tools/executor-common": "^0.0.4", + "@graphql-tools/utils": "^10.8.1", + "@whatwg-node/disposablestack": "^0.0.6", + "graphql-ws": "^6.0.3", + "isomorphic-ws": "^5.0.0", + "tslib": "^2.8.1", + "ws": "^8.17.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/fetch": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.1.tgz", - "integrity": "sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==", + "node_modules/@graphql-tools/executor-http": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.3.3.tgz", + "integrity": "sha512-LIy+l08/Ivl8f8sMiHW2ebyck59JzyzO/yF9SFS4NH6MJZUezA1xThUXCDIKhHiD56h/gPojbkpcFvM2CbNE7A==", "dev": true, "license": "MIT", "dependencies": { - "@whatwg-node/node-fetch": "^0.7.1", - "urlpattern-polyfill": "^10.0.0" + "@graphql-hive/signal": "^1.0.0", + "@graphql-tools/executor-common": "^0.0.4", + "@graphql-tools/utils": "^10.8.1", + "@repeaterjs/repeater": "^3.0.4", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/fetch": "^0.10.4", + "@whatwg-node/promise-helpers": "^1.3.0", + "meros": "^1.2.1", + "tslib": "^2.8.1" }, "engines": { "node": ">=18.0.0" - } - }, - "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.2.tgz", - "integrity": "sha512-OAAEIbyspvQwkcRGutYN3D0a+hzQogvcZ7I3hf6vg742ZEq52yMJTGtkwjl3KZRmzzUltd/oEMxEGsXFLjnuLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.6.3" }, - "engines": { - "node": ">=18.0.0" + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@graphql-tools/executor-legacy-ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.2.tgz", - "integrity": "sha512-Bjgny4svnOVOCKXuiS6oSiZB3oy/GVMHh+6CbztlA+YcDu6jUiB7fTQiJuZA/c+e3xTxl9xMe3zOsEKwuHy07A==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.17.tgz", + "integrity": "sha512-TvltY6eL4DY1Vt66Z8kt9jVmNcI+WkvVPQZrPbMCM3rv2Jw/sWvSwzUBezRuWX0sIckMifYVh23VPcGBUKX/wg==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/utils": "^10.8.6", "@types/ws": "^8.0.0", "isomorphic-ws": "^5.0.0", "tslib": "^2.4.0", @@ -2193,14 +1747,14 @@ } }, "node_modules/@graphql-tools/git-loader": { - "version": "8.0.9", - "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.9.tgz", - "integrity": "sha512-buiGwz6C5kPAn5ROIfa+IlYO05n/veuQ2H6rv+dvxB8XUGTQ0beO1jSUeDaLuxWBfWUxEaaKdqB6WIGA2y75fA==", + "version": "8.0.24", + "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.24.tgz", + "integrity": "sha512-ypLC9N2bKNC0QNbrEBTbWKwbV607f7vK2rSGi9uFeGr8E29tWplo6or9V/+TM0ZfIkUsNp/4QX/zKTgo8SbwQg==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/graphql-tag-pluck": "8.3.4", - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/graphql-tag-pluck": "8.3.19", + "@graphql-tools/utils": "^10.8.6", "is-glob": "4.0.3", "micromatch": "^4.0.8", "tslib": "^2.4.0", @@ -2214,19 +1768,19 @@ } }, "node_modules/@graphql-tools/github-loader": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-8.0.4.tgz", - "integrity": "sha512-9CQ6moBPZS2ZEcodqG03r2+xX4zZSu1H4VfK1y4GwSPwow6T2crgW//8/j3HWfUI8FxPCiLjCFeGjlaskhdjJQ==", + "version": "8.0.20", + "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-8.0.20.tgz", + "integrity": "sha512-Icch8bKZ1iP3zXCB9I0ded1hda9NPskSSalw7ZM21kXvLiOR5nZhdqPF65gCFkIKo+O4NR4Bp51MkKj+wl+vpg==", "dev": true, "license": "MIT", "dependencies": { - "@ardatan/sync-fetch": "^0.0.1", "@graphql-tools/executor-http": "^1.1.9", - "@graphql-tools/graphql-tag-pluck": "^8.3.4", - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/graphql-tag-pluck": "^8.3.19", + "@graphql-tools/utils": "^10.8.6", "@whatwg-node/fetch": "^0.10.0", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "@whatwg-node/promise-helpers": "^1.0.0", + "sync-fetch": "0.6.0-2", + "tslib": "^2.4.0" }, "engines": { "node": ">=16.0.0" @@ -2235,45 +1789,15 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/fetch": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.1.tgz", - "integrity": "sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@whatwg-node/node-fetch": "^0.7.1", - "urlpattern-polyfill": "^10.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.2.tgz", - "integrity": "sha512-OAAEIbyspvQwkcRGutYN3D0a+hzQogvcZ7I3hf6vg742ZEq52yMJTGtkwjl3KZRmzzUltd/oEMxEGsXFLjnuLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.6.3" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@graphql-tools/graphql-file-loader": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.3.tgz", - "integrity": "sha512-r47xfTaulXuqjvg/tzUxjhH9Q0FTe9/ApFZsDUyV6Kw4u8n4Ku0ofg7etf1kYDpMVNclici0qB32fnvMi4p3BA==", + "version": "8.0.19", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.19.tgz", + "integrity": "sha512-kyEZL4rRJ5LelfCXL3GLgbMiu5Zd7memZaL8ZxPXGI7DA8On1e5IVBH3zZJwf7LzhjSVnPaHM7O/bRzGvTbXzQ==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/import": "7.0.3", - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/import": "7.0.18", + "@graphql-tools/utils": "^10.8.6", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -2286,18 +1810,18 @@ } }, "node_modules/@graphql-tools/graphql-tag-pluck": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.4.tgz", - "integrity": "sha512-prb+3Pec8qxgouZVBA4jOXGTxKFEw7w2IPPLnz1P06EgxBvRQXTcHtRo9HNWSGMYO4jUrpYiIqlq/Jzjlgb3rA==", + "version": "8.3.19", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.19.tgz", + "integrity": "sha512-LEw/6IYOUz48HjbWntZXDCzSXsOIM1AyWZrlLoJOrA8QAlhFd8h5Tny7opCypj8FO9VvpPFugWoNDh5InPOEQA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.22.9", - "@babel/parser": "^7.16.8", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8", - "@graphql-tools/utils": "^10.5.6", + "@babel/core": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "@graphql-tools/utils": "^10.8.6", "tslib": "^2.4.0" }, "engines": { @@ -2308,13 +1832,13 @@ } }, "node_modules/@graphql-tools/import": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-7.0.3.tgz", - "integrity": "sha512-uCL95SW7kI5D3bGbDEgs7JTRGedJ2OQM/jmT9GVc5KAkZr9JJN6rxqwiypFzV475Dox5q9CKSz80JPui4vc6zg==", + "version": "7.0.18", + "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-7.0.18.tgz", + "integrity": "sha512-1tw1/1QLB0n5bPWfIrhCRnrHIlbMvbwuifDc98g4FPhJ7OXD+iUQe+IpmD5KHVwYWXWhZOuJuq45DfV/WLNq3A==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/utils": "^10.8.6", "resolve-from": "5.0.0", "tslib": "^2.4.0" }, @@ -2326,13 +1850,13 @@ } }, "node_modules/@graphql-tools/json-file-loader": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.3.tgz", - "integrity": "sha512-hKXLhO7Ba6W2+yEGGPZs2/xHDGt56D0eW1ZgleV1PyDIALuLl9gNsg3IU70N4m8j07sHHTwbKaoUnsACkDkpbA==", + "version": "8.0.18", + "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.18.tgz", + "integrity": "sha512-JjjIxxewgk8HeMR3npR3YbOkB7fxmdgmqB9kZLWdkRKBxrRXVzhryyq+mhmI0Evzt6pNoHIc3vqwmSctG2sddg==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/utils": "^10.8.6", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -2345,14 +1869,14 @@ } }, "node_modules/@graphql-tools/load": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-8.0.4.tgz", - "integrity": "sha512-5S7DCsvGz/SU/YP9nbo1sUHVzO2sGi/DCeiNUilI3m2bIEpWkGe7GVvUUKUSusgbNRbd5u6+trLJD8f7N7uXjw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-8.1.0.tgz", + "integrity": "sha512-OGfOm09VyXdNGJS/rLqZ6ztCiG2g6AMxhwtET8GZXTbnjptFc17GtKwJ3Jv5w7mjJ8dn0BHydvIuEKEUK4ciYw==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/schema": "^10.0.8", - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/schema": "^10.0.23", + "@graphql-tools/utils": "^10.8.6", "p-limit": "3.1.0", "tslib": "^2.4.0" }, @@ -2364,13 +1888,13 @@ } }, "node_modules/@graphql-tools/merge": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.9.tgz", - "integrity": "sha512-w9yaU7UMRQvtkTYZHo+c7cS7LO7rqc2H6g3k0aUs8VE9YlFYG1bYdxvEPM5bloaoVqr5TmbqIZqzl1CHeJKilQ==", + "version": "9.0.24", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.24.tgz", + "integrity": "sha512-NzWx/Afl/1qHT3Nm1bghGG2l4jub28AdvtG11PoUlmjcIjnFBJMv4vqL0qnxWe8A82peWo4/TkVdjJRLXwgGEw==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/utils": "^10.8.6", "tslib": "^2.4.0" }, "engines": { @@ -2427,45 +1951,15 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/fetch": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.1.tgz", - "integrity": "sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@whatwg-node/node-fetch": "^0.7.1", - "urlpattern-polyfill": "^10.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.2.tgz", - "integrity": "sha512-OAAEIbyspvQwkcRGutYN3D0a+hzQogvcZ7I3hf6vg742ZEq52yMJTGtkwjl3KZRmzzUltd/oEMxEGsXFLjnuLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.6.3" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@graphql-tools/relay-operation-optimizer": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.3.tgz", - "integrity": "sha512-CqtzgLkFQaDCDePVoC9myPLcp2kCDQQCdyliR1Q0YOa6tbBUNl7q82n7qznrzrJVH3Y+Is59ASz3FhjOolUy4g==", + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.19.tgz", + "integrity": "sha512-xnjLpfzw63yIX1bo+BVh4j1attSwqEkUbpJ+HAhdiSUa3FOQFfpWgijRju+3i87CwhjBANqdTZbcsqLT1hEXig==", "dev": true, "license": "MIT", "dependencies": { - "@ardatan/relay-compiler": "12.0.0", - "@graphql-tools/utils": "^10.5.6", + "@ardatan/relay-compiler": "^12.0.3", + "@graphql-tools/utils": "^10.8.6", "tslib": "^2.4.0" }, "engines": { @@ -2476,16 +1970,15 @@ } }, "node_modules/@graphql-tools/schema": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.8.tgz", - "integrity": "sha512-jkCSq+DdT6Rf/MN3oVz250AMFWZO0E5kh0C2K+kJfS80iBW7/7kLjiSbKz+WD9UBtsJPW2zyzKYC4ylU4jmnLw==", + "version": "10.0.23", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.23.tgz", + "integrity": "sha512-aEGVpd1PCuGEwqTXCStpEkmheTHNdMayiIKH1xDWqYp9i8yKv9FRDgkGrY4RD8TNxnf7iII+6KOBGaJ3ygH95A==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/merge": "^9.0.9", - "@graphql-tools/utils": "^10.5.6", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "@graphql-tools/merge": "^9.0.24", + "@graphql-tools/utils": "^10.8.6", + "tslib": "^2.4.0" }, "engines": { "node": ">=16.0.0" @@ -2495,23 +1988,23 @@ } }, "node_modules/@graphql-tools/url-loader": { - "version": "8.0.15", - "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.15.tgz", - "integrity": "sha512-4cCSaUFK/cULxDnU6mwwRLsFKxEswuFmG7/J68ic+CyJYUVMCc0x2QoKG4E2oeTY2dYIzlT39BmOT4+auqjDmw==", + "version": "8.0.31", + "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.31.tgz", + "integrity": "sha512-QGP3py6DAdKERHO5D38Oi+6j+v0O3rkBbnLpyOo87rmIRbwE6sOkL5JeHegHs7EEJ279fBX6lMt8ry0wBMGtyA==", "dev": true, "license": "MIT", "dependencies": { - "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/executor-graphql-ws": "^1.3.2", + "@graphql-tools/executor-graphql-ws": "^2.0.1", "@graphql-tools/executor-http": "^1.1.9", - "@graphql-tools/executor-legacy-ws": "^1.1.2", - "@graphql-tools/utils": "^10.5.6", + "@graphql-tools/executor-legacy-ws": "^1.1.17", + "@graphql-tools/utils": "^10.8.6", "@graphql-tools/wrap": "^10.0.16", "@types/ws": "^8.0.0", "@whatwg-node/fetch": "^0.10.0", + "@whatwg-node/promise-helpers": "^1.0.0", "isomorphic-ws": "^5.0.0", + "sync-fetch": "0.6.0-2", "tslib": "^2.4.0", - "value-or-promise": "^1.0.11", "ws": "^8.17.1" }, "engines": { @@ -2521,46 +2014,17 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/fetch": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.1.tgz", - "integrity": "sha512-gmPOLrsjSZWEZlr9Oe5+wWFBq3CG6fN13rGlM91Jsj/vZ95G9CCvrORGBAxMXy0AJGiC83aYiHXn3JzTzXQmbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@whatwg-node/node-fetch": "^0.7.1", - "urlpattern-polyfill": "^10.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.2.tgz", - "integrity": "sha512-OAAEIbyspvQwkcRGutYN3D0a+hzQogvcZ7I3hf6vg742ZEq52yMJTGtkwjl3KZRmzzUltd/oEMxEGsXFLjnuLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.6.3" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@graphql-tools/utils": { - "version": "10.5.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.6.tgz", - "integrity": "sha512-JAC44rhbLzXUHiltceyEpWkxmX4e45Dfg19wRFoA9EbDxQVbOzVNF76eEECdg0J1owFsJwfLqCwz7/6xzrovOw==", + "version": "10.8.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.8.6.tgz", + "integrity": "sha512-Alc9Vyg0oOsGhRapfL3xvqh1zV8nKoFUdtLhXX7Ki4nClaIJXckrA86j+uxEuG3ic6j4jlM1nvcWXRn/71AVLQ==", "dev": true, "license": "MIT", "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", + "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", - "dset": "^3.1.2", + "dset": "^3.1.4", "tslib": "^2.4.0" }, "engines": { @@ -2571,20 +2035,20 @@ } }, "node_modules/@graphql-tools/wrap": { - "version": "10.0.16", - "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.16.tgz", - "integrity": "sha512-O/sOoPCnG2tWfhfIeWLQMPS7ipzjMiVOxwhjOUD9DaQd39XFBD4Al/MmKNc2343ua7NyqMwdfgXQjqGH1LFlPA==", + "version": "10.0.35", + "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.35.tgz", + "integrity": "sha512-qBga3wo7+GqY+ClGexiyRz9xgy1RWozZryTuGX8usGWPa4wKi/tJS4rKWQQesgB3Fh//SZUCRA5u2nwZaZQw1Q==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/delegate": "^10.1.2", - "@graphql-tools/schema": "^10.0.8", - "@graphql-tools/utils": "^10.5.6", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "@graphql-tools/delegate": "^10.2.17", + "@graphql-tools/schema": "^10.0.11", + "@graphql-tools/utils": "^10.8.1", + "@whatwg-node/promise-helpers": "^1.3.0", + "tslib": "^2.8.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" @@ -2653,9 +2117,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2667,26 +2131,30 @@ } }, "node_modules/@iconify-json/material-symbols": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@iconify-json/material-symbols/-/material-symbols-1.2.6.tgz", - "integrity": "sha512-eXqnY9DY8B/7Uw/INwhpN8gfBrC3BBaWBx7T/Yf4oHxQRiVto4qSlDF3MD46q7vqp4GjtyQW1IkZvhgWHn0LTA==", + "version": "1.2.20", + "resolved": "https://registry.npmjs.org/@iconify-json/material-symbols/-/material-symbols-1.2.20.tgz", + "integrity": "sha512-+KqOT+3fD+LC2FbWiV8gd4+JLMiVUtmqrjzpKN1ji7rfMQTwvYJ94RT0WQlmL+vfDNJ5MTRe3rBzzJyvIH/aSg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@iconify/types": "*" } }, - "node_modules/@iconify/tailwind": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@iconify/tailwind/-/tailwind-1.1.3.tgz", - "integrity": "sha512-SfyeT+2b/aKWA6DjwdevXdLUqaEqJ5xWTegD92KItaWc47IYsGuqrt/GOz4dJCPcTVCrsUjlvMpy8cNd+uV5nQ==", + "node_modules/@iconify/tailwind4": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@iconify/tailwind4/-/tailwind4-1.0.6.tgz", + "integrity": "sha512-43ZXe+bC7CuE2LCgROdqbQeFYJi/J7L/k1UpSy8KDQlWVsWxPzLSWbWhlJx4uRYLOh1NRyw02YlDOgzBOFNd+A==", "dev": true, "license": "MIT", "dependencies": { - "@iconify/types": "^2.0.0" + "@iconify/types": "^2.0.0", + "@iconify/utils": "^2.2.1" }, "funding": { "url": "https://github.com/sponsors/cyberalien" + }, + "peerDependencies": { + "tailwindcss": ">= 4" } }, "node_modules/@iconify/types": { @@ -2696,113 +2164,40 @@ "dev": true, "license": "MIT" }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/@iconify/utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "@antfu/install-pkg": "^1.0.0", + "@antfu/utils": "^8.1.0", + "@iconify/types": "^2.0.0", + "debug": "^4.4.0", + "globals": "^15.14.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.0.0", + "mlly": "^1.7.4" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/@iconify/utils/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -2853,13 +2248,6 @@ "integrity": "sha512-0D6VFTWahCZymjTA33qmTOgo3sjePaYFalOura76kN/CKJ3HtS4lyxdNLklhrnDrqjWHqgZEqPMwuTGUqDL/+w==", "license": "MIT" }, - "node_modules/@kamilkisiela/fast-url-parser": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz", - "integrity": "sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==", - "dev": true, - "license": "MIT" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2898,21 +2286,10 @@ "node": ">= 8" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@polka/url": { - "version": "1.0.0-next.28", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", - "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", "dev": true, "license": "MIT" }, @@ -2924,9 +2301,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.26.0.tgz", - "integrity": "sha512-gJNwtPDGEaOEgejbaseY6xMFu+CPltsc8/T+diUTTbOQLqD+bnrJq9ulH6WD69TqwqWmrfRAtUv30cCFZlbGTQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", "cpu": [ "arm" ], @@ -2938,9 +2315,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.26.0.tgz", - "integrity": "sha512-YJa5Gy8mEZgz5JquFruhJODMq3lTHWLm1fOy+HIANquLzfIOzE9RA5ie3JjCdVb9r46qfAQY/l947V0zfGJ0OQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", "cpu": [ "arm64" ], @@ -2952,9 +2329,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.26.0.tgz", - "integrity": "sha512-ErTASs8YKbqTBoPLp/kA1B1Um5YSom8QAc4rKhg7b9tyyVqDBlQxy7Bf2wW7yIlPGPg2UODDQcbkTlruPzDosw==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", "cpu": [ "arm64" ], @@ -2966,9 +2343,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.26.0.tgz", - "integrity": "sha512-wbgkYDHcdWW+NqP2mnf2NOuEbOLzDblalrOWcPyY6+BRbVhliavon15UploG7PpBRQ2bZJnbmh8o3yLoBvDIHA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", "cpu": [ "x64" ], @@ -2980,9 +2357,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.26.0.tgz", - "integrity": "sha512-Y9vpjfp9CDkAG4q/uwuhZk96LP11fBz/bYdyg9oaHYhtGZp7NrbkQrj/66DYMMP2Yo/QPAsVHkV891KyO52fhg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", "cpu": [ "arm64" ], @@ -2994,9 +2371,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.26.0.tgz", - "integrity": "sha512-A/jvfCZ55EYPsqeaAt/yDAG4q5tt1ZboWMHEvKAH9Zl92DWvMIbnZe/f/eOXze65aJaaKbL+YeM0Hz4kLQvdwg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", "cpu": [ "x64" ], @@ -3008,9 +2385,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.26.0.tgz", - "integrity": "sha512-paHF1bMXKDuizaMODm2bBTjRiHxESWiIyIdMugKeLnjuS1TCS54MF5+Y5Dx8Ui/1RBPVRE09i5OUlaLnv8OGnA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", "cpu": [ "arm" ], @@ -3022,9 +2399,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.26.0.tgz", - "integrity": "sha512-cwxiHZU1GAs+TMxvgPfUDtVZjdBdTsQwVnNlzRXC5QzIJ6nhfB4I1ahKoe9yPmoaA/Vhf7m9dB1chGPpDRdGXg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", "cpu": [ "arm" ], @@ -3036,9 +2413,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.26.0.tgz", - "integrity": "sha512-4daeEUQutGRCW/9zEo8JtdAgtJ1q2g5oHaoQaZbMSKaIWKDQwQ3Yx0/3jJNmpzrsScIPtx/V+1AfibLisb3AMQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", "cpu": [ "arm64" ], @@ -3050,9 +2427,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.26.0.tgz", - "integrity": "sha512-eGkX7zzkNxvvS05ROzJ/cO/AKqNvR/7t1jA3VZDi2vRniLKwAWxUr85fH3NsvtxU5vnUUKFHKh8flIBdlo2b3Q==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", "cpu": [ "arm64" ], @@ -3063,10 +2440,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.26.0.tgz", - "integrity": "sha512-Odp/lgHbW/mAqw/pU21goo5ruWsytP7/HCC/liOt0zcGG0llYWKrd10k9Fj0pdj3prQ63N5yQLCLiE7HTX+MYw==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", "cpu": [ "ppc64" ], @@ -3078,9 +2469,23 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.26.0.tgz", - "integrity": "sha512-MBR2ZhCTzUgVD0OJdTzNeF4+zsVogIR1U/FsyuFerwcqjZGvg2nYe24SAHp8O5sN8ZkRVbHwlYeHqcSQ8tcYew==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", "cpu": [ "riscv64" ], @@ -3092,9 +2497,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.26.0.tgz", - "integrity": "sha512-YYcg8MkbN17fMbRMZuxwmxWqsmQufh3ZJFxFGoHjrE7bv0X+T6l3glcdzd7IKLiwhT+PZOJCblpnNlz1/C3kGQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", "cpu": [ "s390x" ], @@ -3106,9 +2511,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.26.0.tgz", - "integrity": "sha512-ZuwpfjCwjPkAOxpjAEjabg6LRSfL7cAJb6gSQGZYjGhadlzKKywDkCUnJ+KEfrNY1jH5EEoSIKLCb572jSiglA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", "cpu": [ "x64" ], @@ -3120,9 +2525,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.26.0.tgz", - "integrity": "sha512-+HJD2lFS86qkeF8kNu0kALtifMpPCZU80HvwztIKnYwym3KnA1os6nsX4BGSTLtS2QVAGG1P3guRgsYyMA0Yhg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", "cpu": [ "x64" ], @@ -3134,9 +2539,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.26.0.tgz", - "integrity": "sha512-WUQzVFWPSw2uJzX4j6YEbMAiLbs0BUysgysh8s817doAYhR5ybqTI1wtKARQKo6cGop3pHnrUJPFCsXdoFaimQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", "cpu": [ "arm64" ], @@ -3148,9 +2553,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.26.0.tgz", - "integrity": "sha512-D4CxkazFKBfN1akAIY6ieyOqzoOoBV1OICxgUblWxff/pSjCA2khXlASUx7mK6W1oP4McqhgcCsu6QaLj3WMWg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", "cpu": [ "ia32" ], @@ -3162,9 +2567,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.26.0.tgz", - "integrity": "sha512-2x8MO1rm4PGEP0xWbubJW5RtbNLk3puzAMaLQd3B3JHVw4KcHlmXcO+Wewx9zCoo7EUFiMlu/aZbCJ7VjMzAag==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", "cpu": [ "x64" ], @@ -3175,10 +2580,19 @@ "win32" ] }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz", + "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, "node_modules/@sveltejs/adapter-static": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.6.tgz", - "integrity": "sha512-MGJcesnJWj7FxDcB/GbrdYD3q24Uk0PIL4QIX149ku+hlJuj//nxUbb0HxUTpjkecWfHjVveSUnUaQWnPRXlpg==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz", + "integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3186,25 +2600,23 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.8.1.tgz", - "integrity": "sha512-uuOfFwZ4xvnfPsiTB6a4H1ljjTUksGhWnYq5X/Y9z4x5+3uM2Md8q/YVeHL+7w+mygAwoEFdgKZ8YkUuk+VKww==", + "version": "2.20.7", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.7.tgz", + "integrity": "sha512-dVbLMubpJJSLI4OYB+yWYNHGAhgc2bVevWuBjDj8jFUXIJOAnLwYP3vsmtcgoxNGUXoq0rHS5f7MFCsryb6nzg==", "dev": true, - "hasInstallScript": true, "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", "devalue": "^5.1.0", - "esm-env": "^1.0.0", + "esm-env": "^1.2.2", "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", - "sirv": "^3.0.0", - "tiny-glob": "^0.2.9" + "sirv": "^3.0.0" }, "bin": { "svelte-kit": "svelte-kit.js" @@ -3213,50 +2625,328 @@ "node": ">=18.13" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3" + "vite": "^5.0.3 || ^6.0.0" } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", - "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.0.3.tgz", + "integrity": "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==", "dev": true, "license": "MIT", "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", - "debug": "^4.3.4", + "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", + "debug": "^4.4.0", "deepmerge": "^4.3.1", "kleur": "^4.1.5", - "magic-string": "^0.30.10", - "svelte-hmr": "^0.16.0", - "vitefu": "^0.2.5" + "magic-string": "^0.30.15", + "vitefu": "^1.0.4" }, "engines": { - "node": "^18.0.0 || >=20" + "node": "^18.0.0 || ^20.0.0 || >=22" }, "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" + "svelte": "^5.0.0", + "vite": "^6.0.0" } }, "node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", - "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-4.0.1.tgz", + "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.3.4" + "debug": "^4.3.7" }, "engines": { - "node": "^18.0.0 || >=20" + "node": "^18.0.0 || ^20.0.0 || >=22" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.4.tgz", + "integrity": "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.29.2", + "tailwindcss": "4.1.4" + } + }, + "node_modules/@tailwindcss/node/node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.4.tgz", + "integrity": "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.4", + "@tailwindcss/oxide-darwin-arm64": "4.1.4", + "@tailwindcss/oxide-darwin-x64": "4.1.4", + "@tailwindcss/oxide-freebsd-x64": "4.1.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", + "@tailwindcss/oxide-linux-x64-musl": "4.1.4", + "@tailwindcss/oxide-wasm32-wasi": "4.1.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.4.tgz", + "integrity": "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.4.tgz", + "integrity": "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.4.tgz", + "integrity": "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.4.tgz", + "integrity": "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.4.tgz", + "integrity": "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.4.tgz", + "integrity": "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.4.tgz", + "integrity": "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.4.tgz", + "integrity": "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.4.tgz", + "integrity": "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.4.tgz", + "integrity": "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.0", + "@emnapi/runtime": "^1.4.0", + "@emnapi/wasi-threads": "^1.0.1", + "@napi-rs/wasm-runtime": "^0.2.8", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.4.tgz", + "integrity": "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.4.tgz", + "integrity": "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.4.tgz", + "integrity": "sha512-4UQeMrONbvrsXKXXp/uxmdEN5JIJ9RkH7YVzs6AMxC/KC1+Np7WZBaNIco7TEjlkthqxZbt8pU/ipD+hKjm80A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.4", + "@tailwindcss/oxide": "4.1.4", + "tailwindcss": "4.1.4" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" } }, "node_modules/@types/cookie": { @@ -3267,9 +2957,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "license": "MIT" }, "node_modules/@types/js-yaml": { @@ -3287,26 +2977,19 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.21.0" } }, - "node_modules/@types/pug": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", - "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/ws": { - "version": "8.5.13", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", - "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "dev": true, "license": "MIT", "dependencies": { @@ -3314,21 +2997,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.14.0.tgz", - "integrity": "sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.14.0", - "@typescript-eslint/type-utils": "8.14.0", - "@typescript-eslint/utils": "8.14.0", - "@typescript-eslint/visitor-keys": "8.14.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3339,25 +3022,21 @@ }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.14.0.tgz", - "integrity": "sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.14.0", - "@typescript-eslint/types": "8.14.0", - "@typescript-eslint/typescript-estree": "8.14.0", - "@typescript-eslint/visitor-keys": "8.14.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4" }, "engines": { @@ -3368,23 +3047,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz", - "integrity": "sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.14.0", - "@typescript-eslint/visitor-keys": "8.14.0" + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3395,16 +3070,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.14.0.tgz", - "integrity": "sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.14.0", - "@typescript-eslint/utils": "8.14.0", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3413,16 +3088,15 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.14.0.tgz", - "integrity": "sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", "dev": true, "license": "MIT", "engines": { @@ -3434,20 +3108,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz", - "integrity": "sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.14.0", - "@typescript-eslint/visitor-keys": "8.14.0", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3456,16 +3130,40 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -3476,16 +3174,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.14.0.tgz", - "integrity": "sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.14.0", - "@typescript-eslint/types": "8.14.0", - "@typescript-eslint/typescript-estree": "8.14.0" + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3495,18 +3193,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz", - "integrity": "sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.14.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.30.1", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3517,9 +3216,9 @@ } }, "node_modules/@urql/core": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.0.8.tgz", - "integrity": "sha512-1GOnUw7/a9bzkcM0+U8U5MmxW2A7FE5YquuEmcJzTtW5tIs2EoS4F2ITpuKBjRBbyRjZgO860nWFPo1m4JImGA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.1.1.tgz", + "integrity": "sha512-aGh024z5v2oINGD/In6rAtVKTm4VmQ2TxKQBAtk2ZSME5dunZFcjltw4p5ENQg+5CBhZ3FHMzl0Oa+rwqiWqlg==", "license": "MIT", "dependencies": { "@0no-co/graphql.web": "^1.0.5", @@ -3527,12 +3226,12 @@ } }, "node_modules/@urql/svelte": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@urql/svelte/-/svelte-4.2.2.tgz", - "integrity": "sha512-6ntLGsWcnNtaMZVmFpePfFTSpYxYpznCAqnuvLDjt7Oa7YqHcFiyPnz7IIsiPD9VE6hZSi0+RwmRk5BMba/teQ==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@urql/svelte/-/svelte-4.2.3.tgz", + "integrity": "sha512-v3eArfymhdjaM5VQFp3QZxq9veYPadmDfX7ueid/kD4DlRplIycPakJ2FrKigh46SXa5mWqJ3QWuWyRKVu61sw==", "license": "MIT", "dependencies": { - "@urql/core": "^5.0.0", + "@urql/core": "^5.1.1", "wonka": "^6.3.2" }, "peerDependencies": { @@ -3541,38 +3240,38 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.5.tgz", - "integrity": "sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", + "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.5", - "@vitest/utils": "2.1.5", - "chai": "^5.1.2", - "tinyrainbow": "^1.2.0" + "@vitest/spy": "3.1.1", + "@vitest/utils": "3.1.1", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.5.tgz", - "integrity": "sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", + "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.5", + "@vitest/spy": "3.1.1", "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" + "magic-string": "^0.30.17" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0" + "vite": "^5.0.0 || ^6.0.0" }, "peerDependenciesMeta": { "msw": { @@ -3584,51 +3283,51 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.5.tgz", - "integrity": "sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", + "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^1.2.0" + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.5.tgz", - "integrity": "sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", + "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.5", - "pathe": "^1.1.2" + "@vitest/utils": "3.1.1", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.5.tgz", - "integrity": "sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", + "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.5", - "magic-string": "^0.30.12", - "pathe": "^1.1.2" + "@vitest/pretty-format": "3.1.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.5.tgz", - "integrity": "sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", + "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3639,28 +3338,42 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.5.tgz", - "integrity": "sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", + "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.5", - "loupe": "^3.1.2", - "tinyrainbow": "^1.2.0" + "@vitest/pretty-format": "3.1.1", + "loupe": "^3.1.3", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, + "node_modules/@whatwg-node/disposablestack": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz", + "integrity": "sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@whatwg-node/promise-helpers": "^1.0.0", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@whatwg-node/fetch": { - "version": "0.9.23", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.23.tgz", - "integrity": "sha512-7xlqWel9JsmxahJnYVUj/LLxWcnA93DR4c9xlw3U814jWTiYalryiH1qToik1hOxweKKRLi4haXHM5ycRksPBA==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.6.tgz", + "integrity": "sha512-6uzhO2aQ757p3bSHcemA8C4pqEXuyBqyGAM7cYpO0c6/igRMV9As9XL0W12h5EPYMclgr7FgjmbVQBoWEdJ/yA==", "dev": true, "license": "MIT", "dependencies": { - "@whatwg-node/node-fetch": "^0.6.0", + "@whatwg-node/node-fetch": "^0.7.18", "urlpattern-polyfill": "^10.0.0" }, "engines": { @@ -3668,21 +3381,34 @@ } }, "node_modules/@whatwg-node/node-fetch": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.6.0.tgz", - "integrity": "sha512-tcZAhrpx6oVlkEsRngeTEEE7I5/QdLjeEz4IlekabGaESP7+Dkm/6a9KcF1KdCBB7mO9PXtBkwCuTCt8+UPg8Q==", + "version": "0.7.18", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.18.tgz", + "integrity": "sha512-IxKdVWfZYasGiyxBcsROxq6FmDQu3MNNiOYJ/yqLKhe+Qq27IIWsK7ItbjS2M9L5aM5JxjWkIS7JDh7wnsn+CQ==", "dev": true, "license": "MIT", "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", + "@fastify/busboy": "^3.1.1", + "@whatwg-node/disposablestack": "^0.0.6", + "@whatwg-node/promise-helpers": "^1.3.1", "tslib": "^2.6.3" }, "engines": { "node": ">=18.0.0" } }, + "node_modules/@whatwg-node/promise-helpers": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.1.tgz", + "integrity": "sha512-D+OwTEunoQhVHVToD80dPhfz9xgPLqJyEA3F5jCRM14A2u8tBBQVdZekqfqx6ZAfZ+POT4Hb0dn601UKMsvADw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@zerodevx/svelte-toast": { "version": "0.9.6", "resolved": "https://registry.npmjs.org/@zerodevx/svelte-toast/-/svelte-toast-0.9.6.tgz", @@ -3694,9 +3420,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3716,14 +3442,11 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -3801,34 +3524,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3895,44 +3590,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -3942,52 +3599,6 @@ "node": ">= 0.4" } }, - "node_modules/babel-plugin-syntax-trailing-function-commas": { - "version": "7.0.0-beta.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", - "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/babel-preset-fbjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", - "integrity": "sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-proposal-class-properties": "^7.0.0", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/plugin-syntax-class-properties": "^7.0.0", - "@babel/plugin-syntax-flow": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "@babel/plugin-transform-arrow-functions": "^7.0.0", - "@babel/plugin-transform-block-scoped-functions": "^7.0.0", - "@babel/plugin-transform-block-scoping": "^7.0.0", - "@babel/plugin-transform-classes": "^7.0.0", - "@babel/plugin-transform-computed-properties": "^7.0.0", - "@babel/plugin-transform-destructuring": "^7.0.0", - "@babel/plugin-transform-flow-strip-types": "^7.0.0", - "@babel/plugin-transform-for-of": "^7.0.0", - "@babel/plugin-transform-function-name": "^7.0.0", - "@babel/plugin-transform-literals": "^7.0.0", - "@babel/plugin-transform-member-expression-literals": "^7.0.0", - "@babel/plugin-transform-modules-commonjs": "^7.0.0", - "@babel/plugin-transform-object-super": "^7.0.0", - "@babel/plugin-transform-parameters": "^7.0.0", - "@babel/plugin-transform-property-literals": "^7.0.0", - "@babel/plugin-transform-react-display-name": "^7.0.0", - "@babel/plugin-transform-react-jsx": "^7.0.0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0", - "@babel/plugin-transform-spread": "^7.0.0", - "@babel/plugin-transform-template-literals": "^7.0.0", - "babel-plugin-syntax-trailing-function-commas": "^7.0.0-beta.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4016,19 +3627,6 @@ ], "license": "MIT" }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -4042,13 +3640,14 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -4065,9 +3664,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -4085,9 +3684,9 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -4132,28 +3731,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/buffer-crc32": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", - "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dev": true, - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -4185,30 +3762,10 @@ "tslib": "^2.0.3" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/caniuse-lite": { - "version": "1.0.30001680", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", - "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", + "version": "1.0.30001714", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001714.tgz", + "integrity": "sha512-mtgapdwDLSSBnCI3JokHM7oEQBLxiJKVRtg10AxM1AyeiKcM96f0Mkbqeq+1AbiCtvMcHRulAAEMu693JrSWqg==", "dev": true, "funding": [ { @@ -4239,9 +3796,9 @@ } }, "node_modules/chai": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", - "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", "dev": true, "license": "MIT", "dependencies": { @@ -4330,41 +3887,19 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" } }, "node_modules/clean-stack": { @@ -4473,17 +4008,13 @@ "node": ">=0.8" } }, - "node_modules/code-red": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" + "engines": { + "node": ">=6" } }, "node_modules/color-convert": { @@ -4513,16 +4044,6 @@ "dev": true, "license": "MIT" }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/common-tags": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", @@ -4540,6 +4061,13 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true, + "license": "MIT" + }, "node_modules/constant-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", @@ -4597,13 +4125,13 @@ } }, "node_modules/cross-fetch": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", - "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", "dev": true, "license": "MIT", "dependencies": { - "node-fetch": "^2.6.12" + "node-fetch": "^2.7.0" } }, "node_modules/cross-inspect": { @@ -4620,9 +4148,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", - "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -4634,19 +4162,6 @@ "node": ">= 8" } }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -4660,10 +4175,48 @@ "node": ">=4" } }, + "node_modules/cssstyle": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz", + "integrity": "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.1.1", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/dataloader": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", - "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", + "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==", "dev": true, "license": "MIT" }, @@ -4686,9 +4239,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { @@ -4703,15 +4256,12 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, "node_modules/deep-eql": { "version": "5.0.2", @@ -4773,6 +4323,16 @@ "node": ">=8" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/devalue": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", @@ -4780,13 +4340,6 @@ "dev": true, "license": "MIT" }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4800,13 +4353,6 @@ "node": ">=8" } }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, - "license": "MIT" - }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -4819,9 +4365,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -4841,17 +4387,10 @@ "node": ">=4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, "node_modules/electron-to-chromium": { - "version": "1.5.58", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.58.tgz", - "integrity": "sha512-al2l4r+24ZFL7WzyPTlyD0fC33LLzvxqLCwurtBibVPghRGO9hSTl+tis8t1kD7biPiH/en4U0I7o/nQbYeoVA==", + "version": "1.5.138", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.138.tgz", + "integrity": "sha512-FWlQc52z1dXqm+9cCJ2uyFgJkESd+16j6dBEjsgDNuHjBpuIzL8/lRc0uvh1k8RNI6waGo6tcy2DvwkTBJOLDg==", "dev": true, "license": "ISC" }, @@ -4862,6 +4401,43 @@ "dev": true, "license": "MIT" }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4873,23 +4449,16 @@ } }, "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", - "dev": true, - "license": "MIT" - }, - "node_modules/es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, "license": "MIT" }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4897,32 +4466,34 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" } }, "node_modules/escalade": { @@ -4949,30 +4520,31 @@ } }, "node_modules/eslint": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz", - "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==", + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", + "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.7.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.14.0", - "@eslint/plugin-kit": "^0.2.0", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.24.0", + "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.0", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", + "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", @@ -4988,8 +4560,7 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -5009,39 +4580,10 @@ } } }, - "node_modules/eslint-compat-utils": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", - "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/eslint-compat-utils/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz", + "integrity": "sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==", "dev": true, "license": "MIT", "bin": { @@ -5052,32 +4594,30 @@ } }, "node_modules/eslint-plugin-svelte": { - "version": "2.46.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.46.0.tgz", - "integrity": "sha512-1A7iEMkzmCZ9/Iz+EAfOGYL8IoIG6zeKEq1SmpxGeM5SXmoQq+ZNnCpXFVJpsxPWYx8jIVGMerQMzX20cqUl0g==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.5.1.tgz", + "integrity": "sha512-Qn1slddZHfqYiDO6IN8/iN3YL+VuHlgYjm30FT+hh0Jf/TX0jeZMTJXQMajFm5f6f6hURi+XO8P+NPYD+T4jkg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@jridgewell/sourcemap-codec": "^1.4.15", - "eslint-compat-utils": "^0.5.1", + "@eslint-community/eslint-utils": "^4.4.1", + "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "known-css-properties": "^0.35.0", - "postcss": "^8.4.38", + "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", - "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.1.0", - "semver": "^7.6.2", - "svelte-eslint-parser": "^0.43.0" + "postcss-safe-parser": "^7.0.0", + "semver": "^7.6.3", + "svelte-eslint-parser": "^1.1.1" }, "engines": { - "node": "^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://github.com/sponsors/ota-meshi" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0", + "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "peerDependenciesMeta": { @@ -5087,9 +4627,9 @@ } }, "node_modules/eslint-plugin-svelte/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -5100,9 +4640,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5117,30 +4657,6 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", @@ -5153,24 +4669,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/esm-env": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.1.4.tgz", - "integrity": "sha512-oO82nKPHKkzIj/hbtuDYy/JHqBHFlMIW36SDiPCVsj87ntDLcWN+sJ1erdVryd4NxODacFTsdrIE3b7IamqbOg==", - "dev": true, + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", "license": "MIT" }, "node_modules/espree": { @@ -5191,19 +4693,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -5217,6 +4706,15 @@ "node": ">=0.10" } }, + "node_modules/esrap": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.6.tgz", + "integrity": "sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -5244,6 +4742,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" @@ -5260,15 +4759,22 @@ } }, "node_modules/expect-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", + "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.0.0" } }, + "node_modules/exsolve": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", + "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", + "dev": true, + "license": "MIT" + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -5284,26 +4790,6 @@ "node": ">=4" } }, - "node_modules/extract-files": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-11.0.0.tgz", - "integrity": "sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20 || >= 14.13" - }, - "funding": { - "url": "https://github.com/sponsors/jaydenseric" - } - }, - "node_modules/fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", - "dev": true, - "license": "MIT" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5312,9 +4798,9 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -5322,7 +4808,7 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -5355,20 +4841,10 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, "license": "ISC", "dependencies": { @@ -5408,6 +4884,30 @@ "dev": true, "license": "MIT" }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -5501,63 +5001,25 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" + "fetch-blob": "^3.1.2" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=12.20.0" } }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -5573,16 +5035,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5603,28 +5055,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -5638,47 +5068,19 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", + "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", - "dev": true, - "license": "MIT" - }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -5700,13 +5102,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -5722,30 +5117,30 @@ "license": "MIT" }, "node_modules/graphql": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", - "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", "license": "MIT", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, "node_modules/graphql-config": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-5.1.3.tgz", - "integrity": "sha512-RBhejsPjrNSuwtckRlilWzLVt2j8itl74W9Gke1KejDTz7oaA5kVd6wRn9zK9TS5mcmIYGxf7zN7a1ORMdxp1Q==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-5.1.4.tgz", + "integrity": "sha512-ObdBeL3ycddHrNbFvhGZ12pK8jUzWvvyN2A+6ij3XrtLH/KrkXt+BboEAEgXmeUrTcD5RjJnz8IZu3Cgc/oX/w==", "dev": true, "license": "MIT", "dependencies": { "@graphql-tools/graphql-file-loader": "^8.0.0", "@graphql-tools/json-file-loader": "^8.0.0", - "@graphql-tools/load": "^8.0.0", + "@graphql-tools/load": "^8.1.0", "@graphql-tools/merge": "^9.0.0", "@graphql-tools/url-loader": "^8.0.0", "@graphql-tools/utils": "^10.0.0", - "cosmiconfig": "^8.1.0", + "cosmiconfig": "^9.0.0", "jiti": "^2.0.0", - "minimatch": "^9.0.5", + "minimatch": "^10.0.0", "string-env-interpolation": "^1.0.1", "tslib": "^2.4.0" }, @@ -5762,16 +5157,69 @@ } } }, + "node_modules/graphql-config/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/graphql-config/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/graphql-config/node_modules/jiti": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.0.tgz", - "integrity": "sha512-H5UpaUI+aHOqZXlYOaFP/8AzKsg+guWu+Pr3Y8i7+Y3zr1aXAvCvTAQ1RxSc6oVD8R8c7brgNtTVP91E7upH/g==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/graphql-config/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/graphql-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", @@ -5786,6 +5234,21 @@ "graphql": "14 - 16" } }, + "node_modules/graphql-sock": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graphql-sock/-/graphql-sock-1.0.1.tgz", + "integrity": "sha512-gSA0CXdNMvNlpEnH2GY1//SUY7laDsAn51sDm4yh6TTH5UkfbNINydyUAoMHHkAaCaOLNXELQmu3GVcSOw4twg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "semantic-to-nullable": "dist/cli/to-nullable.js", + "semantic-to-strict": "dist/cli/to-strict.js" + }, + "peerDependencies": { + "graphql": "15.x || 16.x || 17.x" + } + }, "node_modules/graphql-tag": { "version": "2.12.6", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", @@ -5803,19 +5266,30 @@ } }, "node_modules/graphql-ws": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.0.tgz", - "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.4.tgz", + "integrity": "sha512-8b4OZtNOvv8+NZva8HXamrc0y1jluYC0+13gdh7198FKjVzXyTvVc95DCwGzaKEfn3YuWZxUqjJlHe3qKM/F2g==", "dev": true, "license": "MIT", - "workspaces": [ - "website" - ], "engines": { - "node": ">=10" + "node": ">=20" }, "peerDependencies": { - "graphql": ">=0.11 <=16" + "@fastify/websocket": "^10 || ^11", + "graphql": "^15.10.1 || ^16", + "uWebSockets.js": "^20", + "ws": "^8" + }, + "peerDependenciesMeta": { + "@fastify/websocket": { + "optional": true + }, + "uWebSockets.js": { + "optional": true + }, + "ws": { + "optional": true + } } }, "node_modules/has-flag": { @@ -5828,19 +5302,6 @@ "node": ">=8" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/header-case": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", @@ -5852,6 +5313,19 @@ "tslib": "^2.0.3" } }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -5867,13 +5341,13 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -5935,9 +5409,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6005,18 +5479,6 @@ "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -6082,35 +5544,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -6174,6 +5607,13 @@ "node": ">=0.12.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-reference": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", @@ -6259,26 +5699,10 @@ "ws": "*" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jiti": { - "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", "bin": { @@ -6286,9 +5710,9 @@ } }, "node_modules/jose": { - "version": "5.9.6", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz", - "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", "dev": true, "license": "MIT", "funding": { @@ -6315,10 +5739,50 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -6410,6 +5874,13 @@ "dev": true, "license": "MIT" }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6424,6 +5895,245 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", + "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", + "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", + "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", + "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", + "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", + "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", + "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", + "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", + "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", + "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", + "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -6487,6 +6197,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/local-pkg": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz", + "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.0.1", + "quansync": "^0.2.8" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-character": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", @@ -6598,9 +6326,9 @@ } }, "node_modules/loupe": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", - "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", "dev": true, "license": "MIT" }, @@ -6635,9 +6363,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -6653,12 +6381,6 @@ "node": ">=0.10.0" } }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "license": "CC0-1.0" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -6711,63 +6433,49 @@ "node": ">=6" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } + "license": "MIT" }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", "dev": true, "license": "MIT", "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" } }, "node_modules/mri": { @@ -6781,9 +6489,9 @@ } }, "node_modules/mrmime": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", - "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", "dev": true, "license": "MIT", "engines": { @@ -6804,22 +6512,10 @@ "dev": true, "license": "ISC" }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -6853,6 +6549,26 @@ "tslib": "^2.0.3" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -6874,6 +6590,31 @@ } } }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -6882,36 +6623,29 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "license": "MIT" }, "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", "dev": true, "license": "MIT", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, "engines": { "node": ">=0.10.0" } }, "node_modules/npm-check-updates": { - "version": "17.1.11", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.11.tgz", - "integrity": "sha512-TR2RuGIH7P3Qrb0jfdC/nT7JWqXPKjDlxuNQt3kx4oNVf1Pn5SBRB7KLypgYZhruivJthgTtfkkyK4mz342VjA==", + "version": "17.1.18", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.18.tgz", + "integrity": "sha512-bkUy2g4v1i+3FeUf5fXMLbxmV95eG4/sS7lYE32GrUeVgQRfQEk39gpskksFunyaxQgTIdrvYbnuNbO/pSUSqw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -6930,6 +6664,13 @@ "dev": true, "license": "MIT" }, + "node_modules/nwsapi": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", + "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", + "dev": true, + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6940,26 +6681,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -7076,23 +6797,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/package-manager-detector": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "quansync": "^0.2.7" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -7151,6 +6865,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/pascal-case": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", @@ -7183,16 +6910,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -7203,13 +6920,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, "node_modules/path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", @@ -7233,30 +6943,6 @@ "node": ">=0.10.0" } }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -7268,9 +6954,9 @@ } }, "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, @@ -7284,17 +6970,6 @@ "node": ">= 14.16" } }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7315,30 +6990,22 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "node_modules/pkg-types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz", + "integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 6" + "dependencies": { + "confbox": "^0.2.1", + "exsolve": "^1.0.1", + "pathe": "^2.0.3" } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "dev": true, "funding": [ { @@ -7356,7 +7023,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -7364,44 +7031,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, "node_modules/postcss-load-config": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", @@ -7442,10 +7071,10 @@ "node": ">= 6" } }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "node_modules/postcss-safe-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", "dev": true, "funding": [ { @@ -7453,36 +7082,20 @@ "url": "https://opencollective.com/postcss/" }, { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, "engines": { - "node": ">=12.0" + "node": ">=18.0" }, "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-safe-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", - "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" + "postcss": "^8.4.31" } }, "node_modules/postcss-scss": { @@ -7513,9 +7126,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -7526,13 +7139,6 @@ "node": ">=4" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7544,9 +7150,9 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", "bin": { @@ -7560,9 +7166,9 @@ } }, "node_modules/prettier-plugin-svelte": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.2.8.tgz", - "integrity": "sha512-PAHmmU5cGZdnhW4mWhmvxuG2PVbbHIxUuPOdUKvfE+d4Qt2d29iU5VWrPdsaW5YqVEE0nqhlvN4eoKmVMpIF3Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.3.3.tgz", + "integrity": "sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -7571,9 +7177,9 @@ } }, "node_modules/prettier-plugin-tailwindcss": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.8.tgz", - "integrity": "sha512-dGu3kdm7SXPkiW4nzeWKCl3uoImdd5CTZEJGxyypEPL37Wj0HT2pLqjrvSei1nTeuQfO4PUfjeW5cTUNRLZ4sA==", + "version": "0.6.11", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.11.tgz", + "integrity": "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==", "dev": true, "license": "MIT", "engines": { @@ -7584,7 +7190,7 @@ "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", - "@zackad/prettier-plugin-twig-melody": "*", + "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", @@ -7611,7 +7217,7 @@ "@trivago/prettier-plugin-sort-imports": { "optional": true }, - "@zackad/prettier-plugin-twig-melody": { + "@zackad/prettier-plugin-twig": { "optional": true }, "prettier-plugin-astro": { @@ -7669,6 +7275,23 @@ "node": ">=6" } }, + "node_modules/quansync": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz", + "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7690,16 +7313,6 @@ ], "license": "MIT" }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -7716,16 +7329,17 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/regenerator-runtime": { @@ -7765,9 +7379,9 @@ "license": "ISC" }, "node_modules/remove-trailing-spaces": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz", - "integrity": "sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.9.tgz", + "integrity": "sha512-xzG7w5IRijvIkHIjDk65URsJJ7k4J95wmcArY5PRcmjldIOl7oTvG8+X2Ag690R7SfwiOcHrWZKVc1Pp5WIOzA==", "dev": true, "license": "MIT" }, @@ -7781,31 +7395,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true, - "license": "ISC" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -7831,9 +7420,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { @@ -7848,28 +7437,14 @@ "dev": true, "license": "MIT" }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/rollup": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.26.0.tgz", - "integrity": "sha512-ilcl12hnWonG8f+NxU6BlgysVA0gvY2l8N0R84S1HcINbW20bvwuCngJkkInV6LXhwRpucsW5k1ovDwEdBVrNg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.7" }, "bin": { "rollup": "dist/bin/rollup" @@ -7879,27 +7454,36 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.26.0", - "@rollup/rollup-android-arm64": "4.26.0", - "@rollup/rollup-darwin-arm64": "4.26.0", - "@rollup/rollup-darwin-x64": "4.26.0", - "@rollup/rollup-freebsd-arm64": "4.26.0", - "@rollup/rollup-freebsd-x64": "4.26.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.26.0", - "@rollup/rollup-linux-arm-musleabihf": "4.26.0", - "@rollup/rollup-linux-arm64-gnu": "4.26.0", - "@rollup/rollup-linux-arm64-musl": "4.26.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.26.0", - "@rollup/rollup-linux-riscv64-gnu": "4.26.0", - "@rollup/rollup-linux-s390x-gnu": "4.26.0", - "@rollup/rollup-linux-x64-gnu": "4.26.0", - "@rollup/rollup-linux-x64-musl": "4.26.0", - "@rollup/rollup-win32-arm64-msvc": "4.26.0", - "@rollup/rollup-win32-ia32-msvc": "4.26.0", - "@rollup/rollup-win32-x64-msvc": "4.26.0", + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" } }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -7935,9 +7519,9 @@ } }, "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7985,17 +7569,17 @@ "dev": true, "license": "MIT" }, - "node_modules/sander": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", - "integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==", + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "es6-promise": "^3.1.2", - "graceful-fs": "^4.1.3", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.2" + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" } }, "node_modules/scuid": { @@ -8027,13 +7611,6 @@ "upper-case-first": "^2.0.2" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "license": "ISC" - }, "node_modules/set-cookie-parser": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", @@ -8072,11 +7649,14 @@ } }, "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", "dev": true, "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8103,9 +7683,9 @@ "license": "BSD-3-Clause" }, "node_modules/sirv": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz", - "integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", + "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", "dev": true, "license": "MIT", "dependencies": { @@ -8153,26 +7733,11 @@ "tslib": "^2.0.3" } }, - "node_modules/sorcery": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.1.tgz", - "integrity": "sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.14", - "buffer-crc32": "^1.0.0", - "minimist": "^1.2.0", - "sander": "^0.5.0" - }, - "bin": { - "sorcery": "bin/sorcery" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -8196,21 +7761,12 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", - "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", "dev": true, "license": "MIT" }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -8243,22 +7799,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -8272,33 +7812,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -8312,50 +7825,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8369,230 +7838,131 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/svelecte": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/svelecte/-/svelecte-4.4.1.tgz", - "integrity": "sha512-/qqGaeqohKiQ8d4MF5VliqeGCKqPXcSjQf1mg+ADZMxL33ihvdr4Zp4f/2Eq1vyM+qiiaGZ+XH0OMV932IyD5g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/svelecte/-/svelecte-5.2.0.tgz", + "integrity": "sha512-xLg32wrb8FMg5scyMjSHl9eETQaI2pH3XfgtmHfZZ7pdo+5IsE7/pPZrF9T0AzTNhZHfbp23AJTqUeIkMTqDgA==", "license": "MIT", - "dependencies": { - "svelte-tiny-virtual-list": "^2.1.0" - }, "peerDependencies": { - "svelte": "^3.43.0 || ^4.0.0" + "svelte": "^5.2.7" } }, "node_modules/svelte": { - "version": "4.2.19", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz", - "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/estree": "^1.0.1", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^4.0.0", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", + "version": "5.27.3", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.27.3.tgz", + "integrity": "sha512-MK16NUEFwAunCkdJpIIJ6hvKElx0zFlKMqQd7NAIugMfrL0YeOH8VEn5pg9g2Q6RLj2JrGJL6c0zaAwmXx/nHQ==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "esm-env": "^1.2.1", + "esrap": "^1.4.6", + "is-reference": "^3.0.3", "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/svelte-check": { - "version": "3.8.6", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.6.tgz", - "integrity": "sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.1.6.tgz", + "integrity": "sha512-P7w/6tdSfk3zEVvfsgrp3h3DFC75jCdZjTQvgGJtjPORs1n7/v2VMPIoty3PWv7jnfEm3x0G/p9wH4pecTb0Wg==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", - "chokidar": "^3.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", "picocolors": "^1.0.0", - "sade": "^1.7.4", - "svelte-preprocess": "^5.1.3", - "typescript": "^5.0.3" + "sade": "^1.7.4" }, "bin": { "svelte-check": "bin/svelte-check" }, + "engines": { + "node": ">= 18.0.0" + }, "peerDependencies": { - "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" } }, - "node_modules/svelte-eslint-parser": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.43.0.tgz", - "integrity": "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==", + "node_modules/svelte-check/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", "dev": true, "license": "MIT", - "dependencies": { - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "postcss": "^8.4.39", - "postcss-scss": "^4.0.9" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, "peerDependencies": { - "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" + "picomatch": "^3 || ^4" }, "peerDependenciesMeta": { - "svelte": { + "picomatch": { "optional": true } } }, - "node_modules/svelte-eslint-parser/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/svelte-check/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, + "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/svelte-eslint-parser/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/svelte-eslint-parser": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.1.2.tgz", + "integrity": "sha512-vqFBRamDKo1l70KMfxxXj1/0Cco5TfMDnqaAjgz6D8PyoMhfMcDOLRkAwPg8WkMyZjMtQL3wW66TZ0x59iqO2w==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.0", + "postcss": "^8.4.49", + "postcss-scss": "^4.0.9", + "postcss-selector-parser": "^7.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/svelte-hmr": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", - "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^12.20 || ^14.13.1 || >= 16" - }, - "peerDependencies": { - "svelte": "^3.19.0 || ^4.0.0" - } - }, - "node_modules/svelte-modals": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/svelte-modals/-/svelte-modals-1.3.0.tgz", - "integrity": "sha512-b1Ylnyv9O6b7VYeWGJVToaVU2lw7GtErVwiEdojyfnOuZcrhNlQ5eDqbTrL3xyKz8j2VTy/QiGUl1lm/6SnQ2A==", - "license": "MIT", - "peerDependencies": { - "svelte": "^3.0.0 || ^4.0.0" - } - }, - "node_modules/svelte-preprocess": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz", - "integrity": "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@types/pug": "^2.0.6", - "detect-indent": "^6.1.0", - "magic-string": "^0.30.5", - "sorcery": "^0.11.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">= 16.0.0" + "url": "https://github.com/sponsors/ota-meshi" }, "peerDependencies": { - "@babel/core": "^7.10.2", - "coffeescript": "^2.5.1", - "less": "^3.11.3 || ^4.0.0", - "postcss": "^7 || ^8", - "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", - "pug": "^3.0.0", - "sass": "^1.26.8", - "stylus": "^0.55.0", - "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", - "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0", - "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0" + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "coffeescript": { - "optional": true - }, - "less": { - "optional": true - }, - "postcss": { - "optional": true - }, - "postcss-load-config": { - "optional": true - }, - "pug": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "typescript": { + "svelte": { "optional": true } } }, - "node_modules/svelte-tiny-virtual-list": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/svelte-tiny-virtual-list/-/svelte-tiny-virtual-list-2.1.2.tgz", - "integrity": "sha512-jeP/WMvgFUR4mYXHGPiCexjX5DuzSO+3xzHNhxfcsFyy+uYPtnqI5UGb383swpzQAyXB0OBqYfzpYihD/5gxnA==", - "license": "MIT" + "node_modules/svelte-modals": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/svelte-modals/-/svelte-modals-2.0.1.tgz", + "integrity": "sha512-4nGWxveM0eILQnkjmlA7nComRSsCW5QssY9d1qNklZaNlnwnziW5xYqLN3ufjyA0/Cn9oMO4i9+8s8zmlw4IiQ==", + "license": "MIT", + "peerDependencies": { + "svelte": ">=5" + } }, "node_modules/swap-case": { "version": "2.0.2", @@ -8604,121 +7974,62 @@ "tslib": "^2.0.3" } }, - "node_modules/tailwindcss": { - "version": "3.4.14", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", - "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==", + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } + "license": "MIT" }, - "node_modules/tailwindcss/node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "node_modules/sync-fetch": { + "version": "0.6.0-2", + "resolved": "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.6.0-2.tgz", + "integrity": "sha512-c7AfkZ9udatCuAy9RSfiGPpeOKKUAUK5e1cXadLOGUjasdxqYqAK0jTNkM/FSEyJ3a5Ra27j/tw/PS0qLmaF/A==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" + "node-fetch": "^3.3.2", + "timeout-signal": "^2.0.0", + "whatwg-mimetype": "^4.0.0" }, "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": ">=18" } }, - "node_modules/tailwindcss/node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "node_modules/sync-fetch/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "dev": true, "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, "engines": { - "node": ">=14" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/antonk52" + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "node_modules/tailwindcss": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", + "integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==", "dev": true, "license": "MIT" }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, "engines": { - "node": ">=0.8" + "node": ">=6" } }, "node_modules/through": { @@ -8728,15 +8039,14 @@ "dev": true, "license": "MIT" }, - "node_modules/tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "node_modules/timeout-signal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/timeout-signal/-/timeout-signal-2.0.0.tgz", + "integrity": "sha512-YBGpG4bWsHoPvofT6y/5iqulfXIiIErl5B0LdtHT1mGXDFTAhhRrbUpTvBgYbovr+3cKblya2WAOcpoy90XguA==", "dev": true, "license": "MIT", - "dependencies": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" + "engines": { + "node": ">=16" } }, "node_modules/tinybench": { @@ -8747,16 +8057,61 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", - "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", - "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", "dev": true, "license": "MIT", "engines": { @@ -8764,9 +8119,9 @@ } }, "node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", "dev": true, "license": "MIT", "engines": { @@ -8793,6 +8148,26 @@ "tslib": "^2.0.3" } }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -8829,33 +8204,45 @@ "node": ">=6" } }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } }, "node_modules/ts-api-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", - "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/ts-log": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.7.tgz", @@ -8897,9 +8284,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -8910,10 +8297,33 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", + "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.30.1", + "@typescript-eslint/parser": "8.30.1", + "@typescript-eslint/utils": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/ua-parser-js": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.39.tgz", - "integrity": "sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==", + "version": "1.0.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", + "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", "dev": true, "funding": [ { @@ -8937,6 +8347,13 @@ "node": "*" } }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -8948,9 +8365,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -8967,23 +8384,10 @@ "node": ">=0.10.0" } }, - "node_modules/unixify/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -9002,7 +8406,7 @@ "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -9055,32 +8459,25 @@ "dev": true, "license": "MIT" }, - "node_modules/value-or-promise": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", - "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/vite": { - "version": "5.4.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", - "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.2.tgz", + "integrity": "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.25.0", + "fdir": "^6.4.3", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.12" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -9089,19 +8486,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.4.0" + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -9122,40 +8525,78 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, "node_modules/vite-node": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.5.tgz", - "integrity": "sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", + "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0" }, "bin": { "vite-node": "vite-node.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vitefu": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", - "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.6.tgz", + "integrity": "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==", "dev": true, "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*" + ], "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "peerDependenciesMeta": { "vite": { @@ -9164,47 +8605,48 @@ } }, "node_modules/vitest": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.5.tgz", - "integrity": "sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "2.1.5", - "@vitest/mocker": "2.1.5", - "@vitest/pretty-format": "^2.1.5", - "@vitest/runner": "2.1.5", - "@vitest/snapshot": "2.1.5", - "@vitest/spy": "2.1.5", - "@vitest/utils": "2.1.5", - "chai": "^5.1.2", - "debug": "^4.3.7", - "expect-type": "^1.1.0", - "magic-string": "^0.30.12", - "pathe": "^1.1.2", - "std-env": "^3.8.0", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", + "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "3.1.1", + "@vitest/mocker": "3.1.1", + "@vitest/pretty-format": "^3.1.1", + "@vitest/runner": "3.1.1", + "@vitest/snapshot": "3.1.1", + "@vitest/spy": "3.1.1", + "@vitest/utils": "3.1.1", + "chai": "^5.2.0", + "debug": "^4.4.0", + "expect-type": "^1.2.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "std-env": "^3.8.1", "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", - "tinyrainbow": "^1.2.0", - "vite": "^5.0.0", - "vite-node": "2.1.5", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.1.1", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.5", - "@vitest/ui": "2.1.5", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.1.1", + "@vitest/ui": "3.1.1", "happy-dom": "*", "jsdom": "*" }, @@ -9212,6 +8654,9 @@ "@edge-runtime/vm": { "optional": true }, + "@types/debug": { + "optional": true + }, "@types/node": { "optional": true }, @@ -9229,6 +8674,19 @@ } } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -9239,22 +8697,74 @@ "defaults": "^1.0.3" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true, - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dev": true, "license": "MIT", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/which": { @@ -9273,13 +8783,6 @@ "node": ">= 8" } }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true, - "license": "ISC" - }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -9298,9 +8801,9 @@ } }, "node_modules/wonka": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.4.tgz", - "integrity": "sha512-CjpbqNtBGNAeyNS/9W6q3kSkKE52+FjIj7AkFlLr11s/VWGUu6a2CdYSdGxocIhIVjaW/zchesBQUKPVU69Cqg==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz", + "integrity": "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==", "license": "MIT" }, "node_modules/word-wrap": { @@ -9328,36 +8831,10 @@ "node": ">=8" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "dev": true, "license": "MIT", "engines": { @@ -9376,6 +8853,23 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -9394,9 +8888,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", - "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", "dev": true, "license": "ISC", "bin": { @@ -9454,6 +8948,12 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zimmerframe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", + "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", + "license": "MIT" } } } diff --git a/frontend/package.json b/frontend/package.json index e9d4e10..f2ada99 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "hircine", - "version": "0.2.0", + "version": "0.4.4", "license": "ISC", "private": true, "scripts": { @@ -15,35 +15,38 @@ "test": "vitest" }, "devDependencies": { + "@eslint/compat": "^1.2.5", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.19.0", "@graphql-codegen/cli": "^5.0.3", "@graphql-codegen/typed-document-node": "^5.0.11", "@graphql-codegen/typescript-operations": "^4.3.1", "@iconify-json/material-symbols": "^1.2.6", - "@iconify/tailwind": "^1.1.3", + "@iconify/tailwind4": "^1.0.3", "@sveltejs/adapter-static": "^3.0.6", "@sveltejs/kit": "^2.8.1", - "@sveltejs/vite-plugin-svelte": "^3.0.2", - "@typescript-eslint/eslint-plugin": "^8.14.0", - "@typescript-eslint/parser": "^8.14.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@tailwindcss/vite": "^4.0.6", "@zerodevx/svelte-toast": "^0.9.6", - "autoprefixer": "^10.4.20", "date-fns": "^4.1.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.46.0", + "eslint-config-prettier": "^10.0.0", + "eslint-plugin-svelte": "^3.0.0", "fast-deep-equal": "^3.1.3", + "globals": "^16.0.0", + "jsdom": "^26.0.0", "npm-check-updates": "^17.1.11", - "postcss": "^8.4.49", "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.2.8", - "prettier-plugin-tailwindcss": "^0.6.8", - "svelte": "^4.2.12", - "svelte-check": "^3.6.6", - "tailwindcss": "^3.4.14", + "prettier-plugin-tailwindcss": "^0.6.11", + "svelte": "^5.5.0", + "svelte-check": "^4.0.0", + "tailwindcss": "^4.0.0", "tslib": "^2.8.1", "typescript": "^5.6.3", - "vite": "^5.4.11", - "vitest": "^2.1.5" + "typescript-eslint": "^8.28.0", + "vite": "^6.0.0", + "vitest": "^3.0.0" }, "type": "module", "dependencies": { @@ -51,7 +54,7 @@ "@urql/svelte": "^4.2.2", "filesize": "^10.1.6", "graphql": "npm:graphql-web-lite@^16.6.0", - "svelecte": "^4.4.1", - "svelte-modals": "^1.3.0" + "svelecte": "^5.0.0", + "svelte-modals": "^2.0.0" } } diff --git a/frontend/postcss.config.cjs b/frontend/postcss.config.cjs deleted file mode 100644 index 054c147..0000000 --- a/frontend/postcss.config.cjs +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {} - } -}; diff --git a/frontend/src/app.css b/frontend/src/app.css index e1b6ca0..3f15de7 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -1,178 +1,219 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import 'tailwindcss'; +@config '../tailwind.config.cjs'; +@plugin "@iconify/tailwind4"; + +@theme { + --breakpoint-3xl: 120rem; +} @layer base { - body { - display: grid; - grid-template-columns: 3rem 1fr; - scrollbar-color: theme('colors.gray.500') rgba(0, 0, 0, 0); - font-family: 'Noto Sans', sans-serif; + button:not(:disabled), + [role='button']:not(:disabled) { + cursor: pointer; } +} - input, - textarea { - @apply w-full rounded bg-slate-900 p-[.4rem] focus:outline focus:outline-1 focus:outline-slate-500; - } +@utility btn { + @apply flex items-center justify-center rounded-xs p-2 text-white transition-colors focus-visible:outline disabled:opacity-40; +} - label { - @apply mb-0.5 inline-block; - } +@utility btn-xs { + @apply btn rounded-xs p-0.5 py-0; +} - form { - @apply flex flex-col gap-4 p-px text-sm; - } +@utility btn-blue { + @apply btn bg-blue-700 hover:bg-blue-600 focus:outline-blue-400 focus-visible:outline-blue-400 disabled:bg-blue-900; +} - .rounded-group > * { - @apply rounded-none first:rounded-l-sm last:rounded-r-sm !important; - } +@utility btn-rose { + @apply btn bg-rose-700 hover:bg-rose-600 focus:outline-rose-400 focus-visible:outline-rose-400 disabled:bg-rose-900; +} - .rounded-group-start > * { - @apply rounded-none first:rounded-l-sm !important; - } +@utility btn-slate { + @apply btn bg-slate-700 hover:bg-slate-600 focus:outline-slate-400 focus-visible:outline-slate-400 disabled:bg-slate-800; +} - .rounded-group-end > * { - @apply rounded-none last:rounded-r-sm !important; - } +@utility btn-transparent { + @apply btn rounded-full bg-black/50 p-1 text-white/80 backdrop-blur-xs hover:bg-black/50 hover:text-white; +} - .grid-labels { - @apply grid grid-cols-[1fr_5fr] gap-2; - } +@utility icon-xs { + @apply text-[18px]; +} - header { - grid-area: header; - } +@utility icon-base { + @apply text-[24px]; +} - aside { - overflow: auto; - grid-area: sidebar; - @apply lg:w-[28rem] xl:w-[32rem] min-[1920px]:w-[36rem]; - } +@utility icon-lg { + @apply text-[28px]; +} - main { - grid-area: main; - } +@utility icon-2xl { + @apply text-[48px]; } -@layer components { - .btn { - @apply flex items-center justify-center rounded-sm p-2 text-white transition-colors disabled:opacity-40; +@utility icon-gray { + &.hoverable:hover { + @apply text-gray-200/80; } - .btn-xs { - @apply btn rounded-sm p-0.5 py-0; + &.dim { + @apply text-gray-100/40; } +} - .btn-blue { - @apply btn bg-blue-700 hover:bg-blue-600 disabled:bg-blue-900; +@utility hoverable { + &.icon-gray:hover { + @apply text-gray-200/80; } - .btn-rose { - @apply btn bg-rose-700 hover:bg-rose-600 disabled:bg-rose-900; + &.icon-yellow:hover { + @apply text-yellow-300/80; } +} - .btn-slate { - @apply btn bg-slate-700 hover:bg-slate-600 disabled:bg-slate-800; +@utility dim { + &.icon-gray { + @apply text-gray-100/40; } - .btn-indigo { - @apply btn bg-indigo-700 hover:bg-indigo-600 disabled:bg-indigo-800; + &.icon-yellow { + @apply text-slate-100/40; } +} + +@utility icon-yellow { + @apply text-yellow-300; - .icon-xs { - @apply text-[18px]; + &.hoverable:hover { + @apply text-yellow-300/80; } - .icon-base { - @apply text-[24px]; + &.dim { + @apply text-slate-100/40; } +} + +@utility toggled { + @apply bg-indigo-700 hover:bg-indigo-600; +} + +@utility ellipsis-nowrap { + @apply overflow-hidden text-ellipsis whitespace-nowrap; +} + +@utility rounded-inherit { + border-radius: inherit; +} - .icon-lg { - @apply text-[28px]; +@utility grid-card-h { + grid-template-columns: 210px 1fr; + grid-template-rows: 300px; +} + +@utility grid-card-cover-only { + @apply !grid-card-h; +} + +@utility grid-card-v { + grid-template-columns: 1fr; + grid-template-rows: 500px 1fr; +} + +@layer base { + body { + display: grid; + grid-template-columns: 3rem 1fr; + scrollbar-color: var(--color-gray-500) rgba(0, 0, 0, 0); + font-family: 'Noto Sans', sans-serif; } - .icon-2xl { - @apply text-[48px]; + input, + textarea { + @apply w-full rounded-sm bg-slate-900 p-[.4rem]; } - .icon-gray.hoverable:hover { - @apply text-gray-200/80; + input, + textarea, + select { + @apply focus:isolate focus:outline focus:outline-slate-400 focus-visible:isolate focus-visible:outline-slate-400; } - .icon-gray.dim { - @apply text-gray-100/40; + button { + @apply focus:outline-slate-400 focus-visible:isolate focus-visible:outline focus-visible:outline-slate-400; } - .icon-yellow { - @apply text-yellow-300; + a { + @apply focus-visible:isolate focus-visible:outline-2 focus-visible:outline-blue-600; } - .icon-yellow.hoverable:hover { - @apply text-yellow-300/80; + label { + @apply mb-0.5 inline-block; } - .icon-yellow.dim { - @apply text-slate-100/40; + form { + @apply flex flex-col gap-4 p-px text-sm; } -} -@layer utilities { - .toggled { - @apply bg-indigo-700 hover:bg-indigo-600; + .rounded-group > * { + @apply rounded-none! first:rounded-l-xs! last:rounded-r-xs!; } - .floating { - @apply rounded-full bg-black/50 p-1 text-white/80 backdrop-blur-sm hover:bg-black/50 hover:text-white; + .rounded-group-start > * { + @apply rounded-none! first:rounded-l-xs!; } - .ellipsis-nowrap { - @apply overflow-hidden text-ellipsis whitespace-nowrap; + .rounded-group-end > * { + @apply rounded-none! last:rounded-r-xs!; } - .rounded-inherit { - border-radius: inherit; + .grid-labels { + @apply grid grid-cols-[1fr_5fr] gap-2; } - .grid-card-h { - grid-template-columns: 210px 1fr; - grid-template-rows: 300px; + header { + grid-area: header; } - .grid-card-cover-only { - @apply !grid-card-h; + aside { + overflow: auto; + grid-area: sidebar; + @apply 3xl:w-[36rem] lg:w-[28rem] xl:w-[32rem]; } - .grid-card-v { - grid-template-columns: 1fr; - grid-template-rows: 500px 1fr; + main { + grid-area: main; } } .svelecte { - --sv-bg: theme(colors.slate.900); - --sv-disabled-bg: theme(colors.slate.900); + --sv-bg: var(--color-slate-900); + --sv-disabled-bg: var(--color-slate-900); --sv-border: 1px solid rgba(0, 0, 0, 0); - --sv-dropdown-active-bg: theme(colors.indigo.800); - --sv-item-selected-bg: theme(colors.indigo.800); - --sv-item-btn-bg-hover: theme(colors.rose.900); - --sv-item-btn-color-hover: theme(colors.rose.100); - --sv-separator-bg: theme(colors.gray.700); + --sv-dropdown-active-bg: var(--color-indigo-800); + --sv-item-selected-bg: var(--color-indigo-800); + --sv-item-btn-bg-hover: var(--color-rose-900); + --sv-item-btn-color-hover: var(--color-rose-100); + --sv-separator-bg: var(--color-gray-700); + --sv-placeholder-color: var(--color-gray-500); --sv-min-height: 38px; --sv-item-wrap-padding: 3px 3px 3px 5px; + --sv-selection-multi-wrap-padding: 3px 3px 3px 5px; } .svelecte.is-focused { - --sv-border: 1px solid theme(colors.slate.500); + --sv-border: 1px solid var(--color-slate-400); } -.svelecte input::placeholder { - color: theme(colors.gray.500); +.exclude .svelecte { + --sv-border: 1px solid var(--color-red-900); + --sv-item-selected-bg: var(--color-rose-800); + --sv-dropdown-active-bg: var(--color-rose-800); } -.exclude .svelecte { - --sv-border: 1px solid theme(colors.red.900); - --sv-item-selected-bg: theme(colors.rose.800); - --sv-dropdown-active-bg: theme(colors.rose.800); +.exclude .svelecte.is-focused { + --sv-border: 1px solid var(--color-red-500); } .sv-item--btn { diff --git a/frontend/src/gql/Queries.ts b/frontend/src/gql/Queries.ts index cc9dd4c..c2f1432 100644 --- a/frontend/src/gql/Queries.ts +++ b/frontend/src/gql/Queries.ts @@ -192,10 +192,19 @@ export function worldsQuery(client: Client, args?: gql.WorldsQueryVariables) { }); } -export function frontpageQuery(client: Client) { +export function frontpageQuery(client: Client, args: gql.FrontpageQueryVariables) { return queryStore({ client: client, query: gql.FrontpageDocument, + variables: args, + requestPolicy: 'network-only' + }); +} + +export function statisticsQuery(client: Client) { + return queryStore({ + client: client, + query: gql.StatisticsDocument, requestPolicy: 'network-only' }); } diff --git a/frontend/src/gql/Utils.ts b/frontend/src/gql/Utils.ts index dd21bbe..6fedd05 100644 --- a/frontend/src/gql/Utils.ts +++ b/frontend/src/gql/Utils.ts @@ -1,9 +1,25 @@ -import equal from 'fast-deep-equal'; +import { omit } from '$lib/Utils'; +import type { Client } from '@urql/svelte'; import * as gql from './graphql'; -export type OmitIdentifiers<T> = Omit<T, 'id' | '__typename'>; +type Typename = '__typename'; +type Identifiers = Typename | 'id'; + +export type OmitTypename<T> = Omit<T, Typename>; +export type OmitIdentifiers<T> = Omit<T, Identifiers>; export type RequiredName<T> = T & { name: string }; +export type MutationWith<T> = ( + client: Client, + args: { ids: number[] | number; input: T } +) => Promise<unknown>; + +export function omitIdentifiers<T extends { __typename?: unknown; id: number }>( + obj: T +): OmitIdentifiers<T> { + return omit(obj, '__typename', 'id'); +} +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isSuccess(object: any): object is gql.Success { if (object.__typename === undefined) { return false; @@ -12,63 +28,10 @@ export function isSuccess(object: any): object is gql.Success { return object.__typename.endsWith('Success') && (object as gql.Success).message !== undefined; } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isError(object: any): object is gql.Error { if (object.__typename === undefined) { return false; } return object.__typename.endsWith('Error') && (object as gql.Error).message !== undefined; } - -type Item = { - id: number | string; - name: string; -}; - -export function itemEquals(a: Item, b: Item) { - return a.name == b.name; -} - -function assocEquals(as: Item[], bs: Item[]) { - return equal( - as.map((a) => a.id), - bs.map((b) => b.id) - ); -} - -function stringEquals(a: string | null | undefined, b: string | null | undefined) { - return (a ? a : null) == (b ? b : null); -} - -export function tagEquals(a: gql.FullTag, b: gql.FullTag) { - return ( - itemEquals(a, b) && - stringEquals(a.description, b.description) && - assocEquals(a.namespaces, b.namespaces) - ); -} - -export function comicEquals( - a: gql.FullComicFragment | undefined, - b: gql.FullComicFragment | undefined -) { - if (a === undefined) return b === undefined; - if (b === undefined) return a === undefined; - - return ( - stringEquals(a.title, b.title) && - stringEquals(a.originalTitle, b.originalTitle) && - stringEquals(a.url, b.url) && - stringEquals(a.date, b.date) && - a.category == b.category && - a.rating == b.rating && - a.censorship == b.censorship && - a.language == b.language && - a.direction == b.direction && - a.layout == b.layout && - assocEquals(a.artists, b.artists) && - assocEquals(a.circles, b.circles) && - assocEquals(a.characters, b.characters) && - assocEquals(a.tags, b.tags) && - assocEquals(a.worlds, b.worlds) - ); -} diff --git a/frontend/src/gql/graphql.ts b/frontend/src/gql/graphql.ts index 4d09e75..03fd972 100644 --- a/frontend/src/gql/graphql.ts +++ b/frontend/src/gql/graphql.ts @@ -122,6 +122,7 @@ export type Artist = { }; export type ArtistFilter = { + comics?: InputMaybe<BasicCountFilter>; name?: InputMaybe<StringFilter>; }; @@ -139,6 +140,7 @@ export type ArtistFilterResult = { export type ArtistResponse = Artist | IdNotFoundError; export enum ArtistSort { + ComicCount = 'COMIC_COUNT', CreatedAt = 'CREATED_AT', Name = 'NAME', Random = 'RANDOM', @@ -164,10 +166,14 @@ export type ArtistsUpsertInput = { export type AssociationFilter = { all?: InputMaybe<Array<Scalars['Int']['input']>>; any?: InputMaybe<Array<Scalars['Int']['input']>>; - empty?: InputMaybe<Scalars['Boolean']['input']>; + count?: InputMaybe<CountFilter>; exact?: InputMaybe<Array<Scalars['Int']['input']>>; }; +export type BasicCountFilter = { + count: CountFilter; +}; + export enum Category { Artbook = 'ARTBOOK', Comic = 'COMIC', @@ -203,6 +209,7 @@ export type Character = { }; export type CharacterFilter = { + comics?: InputMaybe<BasicCountFilter>; name?: InputMaybe<StringFilter>; }; @@ -220,6 +227,7 @@ export type CharacterFilterResult = { export type CharacterResponse = Character | IdNotFoundError; export enum CharacterSort { + ComicCount = 'COMIC_COUNT', CreatedAt = 'CREATED_AT', Name = 'NAME', Random = 'RANDOM', @@ -249,6 +257,7 @@ export type Circle = { }; export type CircleFilter = { + comics?: InputMaybe<BasicCountFilter>; name?: InputMaybe<StringFilter>; }; @@ -266,6 +275,7 @@ export type CircleFilterResult = { export type CircleResponse = Circle | IdNotFoundError; export enum CircleSort { + ComicCount = 'COMIC_COUNT', CreatedAt = 'CREATED_AT', Name = 'NAME', Random = 'RANDOM', @@ -348,6 +358,9 @@ export type ComicScraper = { }; export enum ComicSort { + ArtistCount = 'ARTIST_COUNT', + CharacterCount = 'CHARACTER_COUNT', + CircleCount = 'CIRCLE_COUNT', CreatedAt = 'CREATED_AT', Date = 'DATE', OriginalTitle = 'ORIGINAL_TITLE', @@ -355,7 +368,8 @@ export enum ComicSort { Random = 'RANDOM', TagCount = 'TAG_COUNT', Title = 'TITLE', - UpdatedAt = 'UPDATED_AT' + UpdatedAt = 'UPDATED_AT', + WorldCount = 'WORLD_COUNT' } export type ComicSortInput = { @@ -387,6 +401,20 @@ export type ComicTagsUpsertInput = { options?: InputMaybe<UpsertOptions>; }; +export type ComicTotals = { + __typename?: 'ComicTotals'; + artists: Scalars['Int']['output']; + characters: Scalars['Int']['output']; + circles: Scalars['Int']['output']; + tags: Scalars['Int']['output']; + worlds: Scalars['Int']['output']; +}; + +export type CountFilter = { + operator?: InputMaybe<Operator>; + value: Scalars['Int']['input']; +}; + export type CoverInput = { id: Scalars['Int']['input']; }; @@ -853,6 +881,7 @@ export type Namespace = { export type NamespaceFilter = { name?: InputMaybe<StringFilter>; + tags?: InputMaybe<BasicCountFilter>; }; export type NamespaceFilterInput = { @@ -873,6 +902,7 @@ export enum NamespaceSort { Name = 'NAME', Random = 'RANDOM', SortName = 'SORT_NAME', + TagCount = 'TAG_COUNT', UpdatedAt = 'UPDATED_AT' } @@ -896,6 +926,12 @@ export enum OnMissing { Ignore = 'IGNORE' } +export enum Operator { + Equal = 'EQUAL', + GreaterThan = 'GREATER_THAN', + LowerThan = 'LOWER_THAN' +} + export type Page = { __typename?: 'Page'; comicId?: Maybe<Scalars['Int']['output']>; @@ -940,6 +976,7 @@ export type Query = { namespace: NamespaceResponse; namespaces: NamespaceFilterResult; scrapeComic: ScrapeComicResponse; + statistics: Statistics; tag: TagResponse; tags: TagFilterResult; world: WorldResponse; @@ -1120,6 +1157,11 @@ export enum SortDirection { Descending = 'DESCENDING' } +export type Statistics = { + __typename?: 'Statistics'; + total: Totals; +}; + export type StringFilter = { contains?: InputMaybe<Scalars['String']['input']>; }; @@ -1138,11 +1180,12 @@ export type Tag = { export type TagAssociationFilter = { all?: InputMaybe<Array<Scalars['String']['input']>>; any?: InputMaybe<Array<Scalars['String']['input']>>; - empty?: InputMaybe<Scalars['Boolean']['input']>; + count?: InputMaybe<CountFilter>; exact?: InputMaybe<Array<Scalars['String']['input']>>; }; export type TagFilter = { + comics?: InputMaybe<BasicCountFilter>; name?: InputMaybe<StringFilter>; namespaces?: InputMaybe<AssociationFilter>; }; @@ -1161,8 +1204,10 @@ export type TagFilterResult = { export type TagResponse = FullTag | IdNotFoundError; export enum TagSort { + ComicCount = 'COMIC_COUNT', CreatedAt = 'CREATED_AT', Name = 'NAME', + NamespaceCount = 'NAMESPACE_COUNT', Random = 'RANDOM', UpdatedAt = 'UPDATED_AT' } @@ -1173,6 +1218,22 @@ export type TagSortInput = { seed?: InputMaybe<Scalars['Int']['input']>; }; +export type Totals = { + __typename?: 'Totals'; + archives: Scalars['Int']['output']; + artists: Scalars['Int']['output']; + characters: Scalars['Int']['output']; + circles: Scalars['Int']['output']; + comic: ComicTotals; + comics: Scalars['Int']['output']; + images: Scalars['Int']['output']; + namespaces: Scalars['Int']['output']; + pages: Scalars['Int']['output']; + scrapers: Scalars['Int']['output']; + tags: Scalars['Int']['output']; + worlds: Scalars['Int']['output']; +}; + export type UniquePagesInput = { ids: Array<Scalars['Int']['input']>; }; @@ -1293,6 +1354,7 @@ export type World = { }; export type WorldFilter = { + comics?: InputMaybe<BasicCountFilter>; name?: InputMaybe<StringFilter>; }; @@ -1310,6 +1372,7 @@ export type WorldFilterResult = { export type WorldResponse = IdNotFoundError | World; export enum WorldSort { + ComicCount = 'COMIC_COUNT', CreatedAt = 'CREATED_AT', Name = 'NAME', Random = 'RANDOM', @@ -1336,9 +1399,9 @@ export type ImageFragment = { __typename?: 'Image', hash: string, width: number, export type PageFragment = { __typename?: 'Page', id: number, path: string, comicId?: number | null, image: { __typename?: 'Image', id: number, hash: string, aspectRatio: number, width: number, height: number } }; -export type ComicFragment = { __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }; +export type ComicFragment = { __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, date?: string | null, pageCount: number, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }; -export type FullArchiveFragment = { __typename?: 'FullArchive', id: number, name: string, path: string, size: number, createdAt: string, mtime: string, organized: boolean, pageCount: number, pages: Array<{ __typename?: 'Page', id: number, path: string, comicId?: number | null, image: { __typename?: 'Image', id: number, hash: string, aspectRatio: number, width: number, height: number } }>, comics: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> }; +export type FullArchiveFragment = { __typename?: 'FullArchive', id: number, name: string, path: string, size: number, createdAt: string, mtime: string, organized: boolean, pageCount: number, pages: Array<{ __typename?: 'Page', id: number, path: string, comicId?: number | null, image: { __typename?: 'Image', id: number, hash: string, aspectRatio: number, width: number, height: number } }>, comics: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, date?: string | null, pageCount: number, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> }; export type ArchiveFragment = { __typename?: 'Archive', id: number, name: string, size: number, pageCount: number, cover: { __typename?: 'Image', hash: string, width: number, height: number } }; @@ -1355,7 +1418,7 @@ export type ComicsQueryVariables = Exact<{ }>; -export type ComicsQuery = { __typename?: 'Query', comics: { __typename?: 'ComicFilterResult', count: number, edges: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> } }; +export type ComicsQuery = { __typename?: 'Query', comics: { __typename?: 'ComicFilterResult', count: number, edges: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, date?: string | null, pageCount: number, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> } }; export type ArchivesQueryVariables = Exact<{ pagination: Pagination; @@ -1371,7 +1434,7 @@ export type ArchiveQueryVariables = Exact<{ }>; -export type ArchiveQuery = { __typename?: 'Query', archive: { __typename?: 'FullArchive', id: number, name: string, path: string, size: number, createdAt: string, mtime: string, organized: boolean, pageCount: number, pages: Array<{ __typename?: 'Page', id: number, path: string, comicId?: number | null, image: { __typename?: 'Image', id: number, hash: string, aspectRatio: number, width: number, height: number } }>, comics: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> } | { __typename?: 'IDNotFoundError', message: string } }; +export type ArchiveQuery = { __typename?: 'Query', archive: { __typename?: 'FullArchive', id: number, name: string, path: string, size: number, createdAt: string, mtime: string, organized: boolean, pageCount: number, pages: Array<{ __typename?: 'Page', id: number, path: string, comicId?: number | null, image: { __typename?: 'Image', id: number, hash: string, aspectRatio: number, width: number, height: number } }>, comics: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, date?: string | null, pageCount: number, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> } | { __typename?: 'IDNotFoundError', message: string } }; export type ComicQueryVariables = Exact<{ id: Scalars['Int']['input']; @@ -1523,10 +1586,17 @@ export type ScrapeComicQueryVariables = Exact<{ export type ScrapeComicQuery = { __typename?: 'Query', scrapeComic: { __typename?: 'IDNotFoundError', message: string } | { __typename?: 'ScrapeComicResult', warnings: Array<string>, data: { __typename?: 'ScrapedComic', artists: Array<string>, category?: Category | null, censorship?: Censorship | null, characters: Array<string>, circles: Array<string>, date?: string | null, direction?: Direction | null, language?: Language | null, layout?: Layout | null, originalTitle?: string | null, url?: string | null, rating?: Rating | null, tags: Array<string>, title?: string | null, worlds: Array<string> } } | { __typename?: 'ScraperError', message: string } | { __typename?: 'ScraperNotAvailableError', message: string } | { __typename?: 'ScraperNotFoundError', message: string } }; -export type FrontpageQueryVariables = Exact<{ [key: string]: never; }>; +export type FrontpageQueryVariables = Exact<{ + seed: Scalars['Int']['input']; +}>; + + +export type FrontpageQuery = { __typename?: 'Query', recent: { __typename?: 'ComicFilterResult', count: number, edges: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, date?: string | null, pageCount: number, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> }, favourites: { __typename?: 'ComicFilterResult', count: number, edges: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, date?: string | null, pageCount: number, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> }, bookmarked: { __typename?: 'ComicFilterResult', count: number, edges: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, date?: string | null, pageCount: number, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> } }; + +export type StatisticsQueryVariables = Exact<{ [key: string]: never; }>; -export type FrontpageQuery = { __typename?: 'Query', recent: { __typename?: 'ComicFilterResult', count: number, edges: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> }, favourites: { __typename?: 'ComicFilterResult', count: number, edges: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> }, bookmarked: { __typename?: 'ComicFilterResult', count: number, edges: Array<{ __typename?: 'Comic', id: number, title: string, originalTitle?: string | null, favourite: boolean, cover: { __typename?: 'Image', hash: string, width: number, height: number }, tags: Array<{ __typename?: 'ComicTag', name: string, description?: string | null }>, artists: Array<{ __typename?: 'Artist', name: string }>, characters: Array<{ __typename?: 'Character', name: string }>, worlds: Array<{ __typename?: 'World', name: string }>, circles: Array<{ __typename?: 'Circle', name: string }> }> } }; +export type StatisticsQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', total: { __typename?: 'Totals', archives: number, artists: number, characters: number, circles: number, comics: number, images: number, scrapers: number, pages: number, namespaces: number, tags: number, worlds: number, comic: { __typename?: 'ComicTotals', artists: number, characters: number, circles: number, tags: number, worlds: number } } } }; export type AddComicMutationVariables = Exact<{ input: AddComicInput; @@ -1707,15 +1777,15 @@ export type DeleteWorldsMutation = { __typename?: 'Mutation', deleteWorlds: { __ export const PageFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Page"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"aspectRatio"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comicId"}}]}}]} as unknown as DocumentNode<PageFragment, unknown>; export const ImageFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode<ImageFragment, unknown>; -export const ComicFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Comic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode<ComicFragment, unknown>; -export const FullArchiveFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FullArchive"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FullArchive"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"mtime"}},{"kind":"Field","name":{"kind":"Name","value":"organized"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"pages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Page"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Page"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"aspectRatio"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comicId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Comic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<FullArchiveFragment, unknown>; +export const ComicFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Comic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode<ComicFragment, unknown>; +export const FullArchiveFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FullArchive"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FullArchive"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"mtime"}},{"kind":"Field","name":{"kind":"Name","value":"organized"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"pages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Page"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Page"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"aspectRatio"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comicId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Comic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<FullArchiveFragment, unknown>; export const ArchiveFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Archive"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Archive"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode<ArchiveFragment, unknown>; export const FullComicFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FullComic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FullComic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"language"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"censorship"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"organized"}},{"kind":"Field","name":{"kind":"Name","value":"bookmarked"}},{"kind":"Field","name":{"kind":"Name","value":"pages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Page"}}]}},{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Page"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"aspectRatio"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comicId"}}]}}]} as unknown as DocumentNode<FullComicFragment, unknown>; export const ComicScraperFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ComicScraper"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ComicScraper"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]} as unknown as DocumentNode<ComicScraperFragment, unknown>; export const ScrapeComicResultFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScrapeComicResult"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ScrapeComicResult"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"artists"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"censorship"}},{"kind":"Field","name":{"kind":"Name","value":"characters"}},{"kind":"Field","name":{"kind":"Name","value":"circles"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"language"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"tags"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"worlds"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}}]}}]} as unknown as DocumentNode<ScrapeComicResultFragment, unknown>; -export const ComicsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"comics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Pagination"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ComicFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sort"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ComicSortInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sort"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Comic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<ComicsQuery, ComicsQueryVariables>; +export const ComicsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"comics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Pagination"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ComicFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sort"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ComicSortInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sort"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Comic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<ComicsQuery, ComicsQueryVariables>; export const ArchivesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"archives"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Pagination"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ArchiveFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sort"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ArchiveSortInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archives"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sort"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Archive"}}]}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Archive"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Archive"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}}]}}]} as unknown as DocumentNode<ArchivesQuery, ArchivesQueryVariables>; -export const ArchiveDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"archive"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archive"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FullArchive"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FullArchive"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Page"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"aspectRatio"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comicId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Comic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FullArchive"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FullArchive"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"mtime"}},{"kind":"Field","name":{"kind":"Name","value":"organized"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"pages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Page"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}}]}}]} as unknown as DocumentNode<ArchiveQuery, ArchiveQueryVariables>; +export const ArchiveDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"archive"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archive"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FullArchive"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FullArchive"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Page"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"aspectRatio"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comicId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Comic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FullArchive"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FullArchive"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"mtime"}},{"kind":"Field","name":{"kind":"Name","value":"organized"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"pages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Page"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}}]}}]} as unknown as DocumentNode<ArchiveQuery, ArchiveQueryVariables>; export const ComicDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"comic"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comic"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FullComic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FullComic"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Page"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"aspectRatio"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comicId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FullComic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FullComic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"language"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"censorship"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"organized"}},{"kind":"Field","name":{"kind":"Name","value":"bookmarked"}},{"kind":"Field","name":{"kind":"Name","value":"pages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Page"}}]}},{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<ComicQuery, ComicQueryVariables>; export const TagDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"tag"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tag"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FullTag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"namespaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]} as unknown as DocumentNode<TagQuery, TagQueryVariables>; export const TagsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"tags"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Pagination"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"TagFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sort"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"TagSortInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tags"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sort"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}}]} as unknown as DocumentNode<TagsQuery, TagsQueryVariables>; @@ -1737,7 +1807,8 @@ export const WorldsDocument = {"kind":"Document","definitions":[{"kind":"Operati export const WorldDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"world"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"world"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"World"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]} as unknown as DocumentNode<WorldQuery, WorldQueryVariables>; export const ComicScrapersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"comicScrapers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comicScrapers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<ComicScrapersQuery, ComicScrapersQueryVariables>; export const ScrapeComicDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"scrapeComic"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"scraper"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scrapeComic"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"scraper"},"value":{"kind":"Variable","name":{"kind":"Name","value":"scraper"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ScrapeComicResult"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScrapeComicResult"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScrapeComicResult"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ScrapeComicResult"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"artists"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"censorship"}},{"kind":"Field","name":{"kind":"Name","value":"characters"}},{"kind":"Field","name":{"kind":"Name","value":"circles"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"language"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"tags"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"worlds"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}}]}}]} as unknown as DocumentNode<ScrapeComicQuery, ScrapeComicQueryVariables>; -export const FrontpageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"frontpage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"recent"},"name":{"kind":"Name","value":"comics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"items"},"value":{"kind":"IntValue","value":"6"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"on"},"value":{"kind":"EnumValue","value":"CREATED_AT"}},{"kind":"ObjectField","name":{"kind":"Name","value":"direction"},"value":{"kind":"EnumValue","value":"DESCENDING"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"favourites"},"name":{"kind":"Name","value":"comics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"items"},"value":{"kind":"IntValue","value":"6"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"include"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"favourite"},"value":{"kind":"BooleanValue","value":true}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"on"},"value":{"kind":"EnumValue","value":"RANDOM"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"bookmarked"},"name":{"kind":"Name","value":"comics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"items"},"value":{"kind":"IntValue","value":"6"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"include"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"bookmarked"},"value":{"kind":"BooleanValue","value":true}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"on"},"value":{"kind":"EnumValue","value":"RANDOM"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Comic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<FrontpageQuery, FrontpageQueryVariables>; +export const FrontpageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"frontpage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"seed"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"recent"},"name":{"kind":"Name","value":"comics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"items"},"value":{"kind":"IntValue","value":"6"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"on"},"value":{"kind":"EnumValue","value":"CREATED_AT"}},{"kind":"ObjectField","name":{"kind":"Name","value":"direction"},"value":{"kind":"EnumValue","value":"DESCENDING"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"favourites"},"name":{"kind":"Name","value":"comics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"items"},"value":{"kind":"IntValue","value":"6"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"include"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"favourite"},"value":{"kind":"BooleanValue","value":true}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"on"},"value":{"kind":"EnumValue","value":"RANDOM"}},{"kind":"ObjectField","name":{"kind":"Name","value":"seed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"seed"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"bookmarked"},"name":{"kind":"Name","value":"comics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"items"},"value":{"kind":"IntValue","value":"6"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"include"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"bookmarked"},"value":{"kind":"BooleanValue","value":true}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"on"},"value":{"kind":"EnumValue","value":"RANDOM"}},{"kind":"ObjectField","name":{"kind":"Name","value":"seed"},"value":{"kind":"Variable","name":{"kind":"Name","value":"seed"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Comic"}}]}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Image"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Comic"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"originalTitle"}},{"kind":"Field","name":{"kind":"Name","value":"favourite"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"pageCount"}},{"kind":"Field","name":{"kind":"Name","value":"cover"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"artists"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"characters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"worlds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode<FrontpageQuery, FrontpageQueryVariables>; +export const StatisticsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"statistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"statistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archives"}},{"kind":"Field","name":{"kind":"Name","value":"artists"}},{"kind":"Field","name":{"kind":"Name","value":"characters"}},{"kind":"Field","name":{"kind":"Name","value":"circles"}},{"kind":"Field","name":{"kind":"Name","value":"comic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"artists"}},{"kind":"Field","name":{"kind":"Name","value":"characters"}},{"kind":"Field","name":{"kind":"Name","value":"circles"}},{"kind":"Field","name":{"kind":"Name","value":"tags"}},{"kind":"Field","name":{"kind":"Name","value":"worlds"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comics"}},{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"scrapers"}},{"kind":"Field","name":{"kind":"Name","value":"pages"}},{"kind":"Field","name":{"kind":"Name","value":"namespaces"}},{"kind":"Field","name":{"kind":"Name","value":"tags"}},{"kind":"Field","name":{"kind":"Name","value":"worlds"}}]}}]}}]}}]} as unknown as DocumentNode<StatisticsQuery, StatisticsQueryVariables>; export const AddComicDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"addComic"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AddComicInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"addComic"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AddComicSuccess"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"archivePagesRemaining"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]} as unknown as DocumentNode<AddComicMutation, AddComicMutationVariables>; export const UpdateArchivesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"updateArchives"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ids"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateArchiveInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateArchives"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ids"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ids"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Success"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]} as unknown as DocumentNode<UpdateArchivesMutation, UpdateArchivesMutationVariables>; export const UpdateComicsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"updateComics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ids"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateComicInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateComics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ids"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ids"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Success"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]} as unknown as DocumentNode<UpdateComicsMutation, UpdateComicsMutationVariables>; diff --git a/frontend/src/lib/Actions.ts b/frontend/src/lib/Actions.ts index 7231c2f..2c15b61 100644 --- a/frontend/src/lib/Actions.ts +++ b/frontend/src/lib/Actions.ts @@ -23,28 +23,6 @@ export function debounce( }; } -export function clickOutside( - node: HTMLElement, - { handler, ignore }: { handler: () => void; ignore?: HTMLElement } -) { - const handle = (event: Event) => { - const target = event.target as HTMLElement; - if (!target || target === ignore) return; - - if (node && !node.contains(target) && !event.defaultPrevented) { - handler(); - } - }; - - document.addEventListener('click', handle, true); - - return { - destroy() { - document.removeEventListener('click', handle, true); - } - }; -} - export const focusableElements = [ 'a[href]', 'area[href]', diff --git a/frontend/src/lib/Enums.ts b/frontend/src/lib/Enums.ts index 876aec8..c557cfa 100644 --- a/frontend/src/lib/Enums.ts +++ b/frontend/src/lib/Enums.ts @@ -10,11 +10,13 @@ import { Language, Layout, NamespaceSort, + Operator, Rating, TagSort, UpdateMode, WorldSort } from '$gql/graphql'; +import type { Key } from './Utils'; export interface EnumOption<T> { id: T; @@ -60,8 +62,8 @@ export const ArchiveSortLabel: Record<ArchiveSort, string> = { [ArchiveSort.Path]: 'Path', [ArchiveSort.Size]: 'File Size', [ArchiveSort.CreatedAt]: 'Created At', - [ArchiveSort.PageCount]: 'Page Count', - [ArchiveSort.Random]: 'Random' + [ArchiveSort.Random]: 'Random', + [ArchiveSort.PageCount]: '# Pages' }; export const ComicSortLabel: Record<ComicSort, string> = { @@ -70,30 +72,37 @@ export const ComicSortLabel: Record<ComicSort, string> = { [ComicSort.Date]: 'Date', [ComicSort.CreatedAt]: 'Created At', [ComicSort.UpdatedAt]: 'Updated At', - [ComicSort.TagCount]: 'Tag Count', - [ComicSort.PageCount]: 'Page Count', - [ComicSort.Random]: 'Random' + [ComicSort.Random]: 'Random', + [ComicSort.ArtistCount]: '# Artists', + [ComicSort.CharacterCount]: '# Characters', + [ComicSort.CircleCount]: '# Circles', + [ComicSort.PageCount]: '# Pages', + [ComicSort.TagCount]: '# Tags', + [ComicSort.WorldCount]: '# Worlds' }; export const ArtistSortLabel: Record<ArtistSort, string> = { [ArtistSort.Name]: 'Name', [ArtistSort.CreatedAt]: 'Created At', [ArtistSort.UpdatedAt]: 'Updated At', - [ArchiveSort.Random]: 'Random' + [ArtistSort.Random]: 'Random', + [ArtistSort.ComicCount]: '# Count' }; export const CharacterSortLabel: Record<CharacterSort, string> = { [CharacterSort.Name]: 'Name', [CharacterSort.CreatedAt]: 'Created At', [CharacterSort.UpdatedAt]: 'Updated At', - [ArchiveSort.Random]: 'Random' + [CharacterSort.Random]: 'Random', + [CharacterSort.ComicCount]: '# Comics' }; export const CircleSortLabel: Record<CircleSort, string> = { [CircleSort.Name]: 'Name', [CircleSort.CreatedAt]: 'Created At', [CircleSort.UpdatedAt]: 'Updated At', - [ArchiveSort.Random]: 'Random' + [CircleSort.Random]: 'Random', + [CircleSort.ComicCount]: '# Comics' }; export const NamespaceSortLabel: Record<NamespaceSort, string> = { @@ -101,21 +110,25 @@ export const NamespaceSortLabel: Record<NamespaceSort, string> = { [NamespaceSort.SortName]: 'Sort Name', [NamespaceSort.CreatedAt]: 'Created At', [NamespaceSort.UpdatedAt]: 'Updated At', - [ArchiveSort.Random]: 'Random' + [NamespaceSort.Random]: 'Random', + [NamespaceSort.TagCount]: '# Tags' }; export const TagSortLabel: Record<TagSort, string> = { [TagSort.Name]: 'Name', [TagSort.CreatedAt]: 'Created At', [TagSort.UpdatedAt]: 'Updated At', - [ArchiveSort.Random]: 'Random' + [TagSort.Random]: 'Random', + [TagSort.ComicCount]: '# Comics', + [TagSort.NamespaceCount]: '# Namespaces' }; export const WorldSortLabel: Record<WorldSort, string> = { [WorldSort.Name]: 'Name', [WorldSort.CreatedAt]: 'Created At', [WorldSort.UpdatedAt]: 'Updated At', - [ArchiveSort.Random]: 'Random' + [WorldSort.Random]: 'Random', + [WorldSort.ComicCount]: '# Comics' }; export const UpdateModeLabel: Record<UpdateMode, string> = { @@ -124,6 +137,12 @@ export const UpdateModeLabel: Record<UpdateMode, string> = { [UpdateMode.Replace]: 'Replace' }; +export const OperatorLabel: Record<Operator, string> = { + [Operator.Equal]: 'Equal', + [Operator.GreaterThan]: 'Greater than', + [Operator.LowerThan]: 'Lower than,' +}; + export const LanguageLabel: Record<Language, string> = { [Language.Ab]: 'Abkhazian', [Language.Aa]: 'Afar', @@ -318,8 +337,6 @@ export const censorships: EnumOption<Censorship>[] = optionsFromLabel(Censorship export const categories: EnumOption<Category>[] = optionsFromLabel(CategoryLabel); export const languages: EnumOption<Language>[] = optionsFromLabel(LanguageLabel); -function optionsFromLabel<T extends string | number | symbol>( - labels: Record<T, string> -): EnumOption<T>[] { +function optionsFromLabel<T extends Key>(labels: Record<T, string>): EnumOption<T>[] { return Object.entries(labels).map(([k, v]) => ({ id: k as T, name: v as string })); } diff --git a/frontend/src/lib/Filter.ts b/frontend/src/lib/Filter.svelte.ts index 8e419f3..390b98a 100644 --- a/frontend/src/lib/Filter.ts +++ b/frontend/src/lib/Filter.svelte.ts @@ -1,40 +1,47 @@ import { + Operator, type ArchiveFilter, type ArchiveFilterInput, type ComicFilter, type ComicFilterInput, + type NamespaceFilter, + type NamespaceFilterInput, type StringFilter, type TagFilter, type TagFilterInput } from '$gql/graphql'; -import { getContext, setContext } from 'svelte'; -import { writable, type Writable } from 'svelte/store'; import { navigate } from './Navigation'; -import { numKeys } from './Utils'; +import { numKeys, type Key } from './Utils'; interface FilterInput<T> { include?: T | null; exclude?: T | null; } -interface BasicFilter { +interface NameFilter { name?: { contains?: string | null } | null; } -type FilterMode = 'any' | 'all' | 'exact'; +interface AssociationCount { + count: { value: number; operator?: Operator | null }; +} -type Key = string | number | symbol; +interface BasicFilter extends NameFilter { + comics?: AssociationCount | null; +} -type Filter<T, K extends Key> = { - [Property in K]?: T | null; -}; +export type FilterType = 'include' | 'exclude'; + +type FilterMode = 'any' | 'all' | 'exact'; + +type Filter<T, K extends Key> = Partial<Record<K, T | null>>; type AssocFilter<T, K extends Key> = Filter< { any?: T[] | null; all?: T[] | null; exact?: T[] | null; - empty?: boolean | null; + count?: { value: number; operator?: Operator | null } | null; }, K >; @@ -52,10 +59,10 @@ interface Integrateable<F> { } class ComplexMember<K extends Key> { - values: unknown[] = []; + values: unknown[] = $state([]); key: K; - mode: FilterMode; - empty?: boolean | null; + mode: FilterMode = $state('all'); + empty?: boolean | null = $state(null); constructor(key: K, mode: FilterMode) { this.key = key; @@ -66,15 +73,11 @@ class ComplexMember<K extends Key> { if (this.values.length > 0) { filter[this.key] = { [this.mode]: this.values }; } - - if (this.empty) { - filter[this.key] = { ...filter[this.key], empty: this.empty }; - } } } export class Association<K extends Key> extends ComplexMember<K> { - values: (string | number)[] = []; + values: (string | number)[] = $state([]); constructor(key: K, mode: FilterMode, filter?: AssocFilter<string | number, K> | null) { super(key, mode); @@ -84,7 +87,9 @@ export class Association<K extends Key> extends ComplexMember<K> { } const prop = filter[key]; - this.empty = prop?.empty; + this.empty = + prop?.count?.value === 0 && + (prop.count.operator === undefined || prop.count.operator === Operator.Equal); if (prop?.all && prop.all.length > 0) { this.mode = 'all'; @@ -97,10 +102,17 @@ export class Association<K extends Key> extends ComplexMember<K> { this.values = prop.exact; } } + + integrate(filter: AssocFilter<unknown, K>) { + super.integrate(filter); + if (this.empty) { + filter[this.key] = { ...filter[this.key], count: { value: 0, operator: Operator.Equal } }; + } + } } export class Enum<K extends Key> extends ComplexMember<K> { - values: string[] = []; + values: string[] = $state([]); constructor(key: K, filter?: EnumFilter<K> | null) { super(key, 'any'); @@ -116,11 +128,18 @@ export class Enum<K extends Key> extends ComplexMember<K> { this.values = prop.any; } } + + integrate(filter: EnumFilter<K>) { + super.integrate(filter); + if (this.empty) { + filter[this.key] = { ...filter[this.key], empty: this.empty }; + } + } } class Bool<K extends Key> { key: K; - value?: boolean = undefined; + value?: boolean = $state(undefined); constructor(key: K, filter?: Filter<boolean, K> | null) { this.key = key; @@ -137,9 +156,30 @@ class Bool<K extends Key> { } } +class Orphan<K extends Key> { + key: K; + value?: boolean = false; + + constructor(key: K, filter?: Filter<AssociationCount, K> | null) { + this.key = key; + + if (filter) { + this.value = + filter[key]?.count?.value === 0 && + (filter[key].count.operator === undefined || filter[key].count.operator === Operator.Equal); + } + } + + integrate(filter: Filter<AssociationCount, K>) { + if (this.value) { + filter[this.key] = { count: { value: 0, operator: Operator.Equal } }; + } + } +} + class Str<K extends Key> { key: K; - contains = ''; + contains = $state(''); constructor(key: K, filter?: Filter<StringFilter, K> | null) { this.key = key; @@ -168,7 +208,7 @@ export class ArchiveFilterControls extends Controls<ArchiveFilter> { path: Str<'path'>; organized: Bool<'organized'>; - constructor(filter: ArchiveFilter | null | undefined) { + constructor(filter?: ArchiveFilter | null) { super(); this.path = new Str('path', filter); @@ -178,6 +218,7 @@ export class ArchiveFilterControls extends Controls<ArchiveFilter> { export class ComicFilterControls extends Controls<ComicFilter> { title: Str<'title'>; + url: Str<'url'>; categories: Enum<'category'>; censorships: Enum<'censorship'>; ratings: Enum<'rating'>; @@ -197,6 +238,7 @@ export class ComicFilterControls extends Controls<ComicFilter> { super(); this.title = new Str('title', filter); + this.url = new Str('url', filter); this.favourite = new Bool('favourite', filter); this.organized = new Bool('organized', filter); this.bookmarked = new Bool('bookmarked', filter); @@ -212,16 +254,26 @@ export class ComicFilterControls extends Controls<ComicFilter> { } } -export class BasicFilterControls extends Controls<BasicFilter> { +export class NameFilterControls extends Controls<NameFilter> { name: Str<'name'>; - constructor(filter?: BasicFilter | null) { + constructor(filter?: NameFilter | null) { super(); this.name = new Str('name', filter); } } +export class BasicFilterControls extends NameFilterControls { + orphan: Orphan<'comics'>; + + constructor(filter?: BasicFilter | null) { + super(filter); + + this.orphan = new Orphan('comics', filter); + } +} + export class TagFilterControls extends BasicFilterControls { namespaces: Association<'namespaces'>; @@ -232,6 +284,16 @@ export class TagFilterControls extends BasicFilterControls { } } +export class NamespaceFilterControls extends NameFilterControls { + orphan: Orphan<'tags'>; + + constructor(filter?: NamespaceFilter | null) { + super(filter); + + this.orphan = new Orphan('tags', filter); + } +} + function buildFilterInput<F>(include?: F, exclude?: F) { const input: FilterInput<F> = {}; @@ -247,103 +309,88 @@ function buildFilterInput<F>(include?: F, exclude?: F) { } abstract class FilterContext<F> { - include!: { controls: Controls<F>; size: number }; - exclude!: { controls: Controls<F>; size: number }; + include!: Controls<F>; + exclude!: Controls<F>; + includes = 0; + excludes = 0; - apply(params: URLSearchParams) { + apply = (params: URLSearchParams) => { navigate( { - filter: buildFilterInput( - this.include.controls.buildFilter(), - this.exclude.controls.buildFilter() - ) + filter: buildFilterInput(this.include.buildFilter(), this.exclude.buildFilter()) }, params ); - } + }; } export class ArchiveFilterContext extends FilterContext<ArchiveFilter> { - include: { controls: ArchiveFilterControls; size: number }; - exclude: { controls: ArchiveFilterControls; size: number }; + include: ArchiveFilterControls; + exclude: ArchiveFilterControls; private static ignore = ['organized']; constructor(filter: ArchiveFilterInput) { super(); - this.include = { - controls: new ArchiveFilterControls(filter.include), - size: numKeys(filter.include, ArchiveFilterContext.ignore) - }; - this.exclude = { - controls: new ArchiveFilterControls(filter.exclude), - size: numKeys(filter.exclude, ArchiveFilterContext.ignore) - }; + this.include = new ArchiveFilterControls(filter.include); + this.exclude = new ArchiveFilterControls(filter.exclude); + this.includes = numKeys(filter.include, ArchiveFilterContext.ignore); + this.excludes = numKeys(filter.exclude, ArchiveFilterContext.ignore); } } export class ComicFilterContext extends FilterContext<ComicFilter> { - include: { controls: ComicFilterControls; size: number }; - exclude: { controls: ComicFilterControls; size: number }; + include: ComicFilterControls; + exclude: ComicFilterControls; private static ignore = ['title', 'favourite', 'organized', 'bookmarked']; constructor(filter: ComicFilterInput) { super(); - this.include = { - controls: new ComicFilterControls(filter.include, 'all'), - size: numKeys(filter.include, ComicFilterContext.ignore) - }; - this.exclude = { - controls: new ComicFilterControls(filter.exclude, 'any'), - size: numKeys(filter.exclude, ComicFilterContext.ignore) - }; + this.include = new ComicFilterControls(filter.include, 'all'); + this.exclude = new ComicFilterControls(filter.exclude, 'any'); + this.includes = numKeys(filter.include, ComicFilterContext.ignore); + this.excludes = numKeys(filter.exclude, ComicFilterContext.ignore); } } export class BasicFilterContext extends FilterContext<BasicFilter> { - include: { controls: BasicFilterControls; size: number }; - exclude: { controls: BasicFilterControls; size: number }; + include: BasicFilterControls; + exclude: BasicFilterControls; constructor(filter: FilterInput<BasicFilter>) { super(); - this.include = { - controls: new BasicFilterControls(filter.include), - size: numKeys(filter.include) - }; - this.exclude = { - controls: new BasicFilterControls(), - size: 0 - }; + this.include = new BasicFilterControls(filter.include); + this.exclude = new BasicFilterControls(); } } export class TagFilterContext extends FilterContext<TagFilter> { - include: { controls: TagFilterControls; size: number }; - exclude: { controls: TagFilterControls; size: number }; - private static ignore = ['name']; + include: TagFilterControls; + exclude: TagFilterControls; + private static ignore = ['name', 'comics']; constructor(filter: TagFilterInput) { super(); - this.include = { - controls: new TagFilterControls(filter.include, 'all'), - size: numKeys(filter.include, TagFilterContext.ignore) - }; - this.exclude = { - controls: new TagFilterControls(filter.exclude, 'any'), - size: numKeys(filter.exclude, TagFilterContext.ignore) - }; + this.include = new TagFilterControls(filter.include, 'all'); + this.exclude = new TagFilterControls(filter.exclude, 'any'); + this.includes = numKeys(filter.include, TagFilterContext.ignore); + this.excludes = numKeys(filter.exclude, TagFilterContext.ignore); } } -export function initFilterContext<F extends FilterContext<unknown>>() { - return setContext<Writable<F>>('filter', writable()); -} +export class NamespaceFilterContext extends FilterContext<NamespaceFilter> { + include: NamespaceFilterControls; + exclude: NamespaceFilterControls; -export function getFilterContext<F extends FilterContext<unknown>>() { - return getContext<Writable<F>>('filter'); + constructor(filter: NamespaceFilterInput) { + super(); + + this.include = new NamespaceFilterControls(filter.include); + this.exclude = new NamespaceFilterControls(); + } } export function cycleBooleanFilter(value: boolean | undefined, tristate = true) { diff --git a/frontend/src/lib/Form.ts b/frontend/src/lib/Form.ts new file mode 100644 index 0000000..b6d06f4 --- /dev/null +++ b/frontend/src/lib/Form.ts @@ -0,0 +1,76 @@ +import type { FullComicFragment, FullTag, Namespace } from '$gql/graphql'; +import type { OmitIdentifiers } from '$gql/Utils'; +import equal from 'fast-deep-equal'; +import type { Snippet } from 'svelte'; + +export interface FormProps<I, P> { + initial: OmitIdentifiers<I>; + submit: (input: P) => void; + children?: Snippet; +} + +interface Item { + id: number | string; + name: string; +} + +function stringPending(a?: string | null, b?: string | null) { + if (a?.length === 0) { + a = null; + } + + if (b?.length === 0) { + b = null; + } + + return a !== b; +} + +function associationPending(as: Item[], bs: Item[]) { + return !equal( + as.map((a) => a.id), + bs.map((b) => b.id) + ); +} + +export function itemPending(initial: OmitIdentifiers<Item>, current: OmitIdentifiers<Item>) { + return stringPending(initial.name, current.name); +} + +export function namespacePending( + initial: OmitIdentifiers<Namespace>, + current: OmitIdentifiers<Namespace> +) { + return itemPending(initial, current) || stringPending(initial.sortName, current.sortName); +} + +export function tagPending(a: OmitIdentifiers<FullTag>, b: OmitIdentifiers<FullTag>) { + return ( + itemPending(a, b) || + stringPending(a.description, b.description) || + associationPending(a.namespaces, b.namespaces) + ); +} + +export function comicPending(a?: FullComicFragment, b?: OmitIdentifiers<FullComicFragment>) { + if (a === undefined) return b !== undefined; + if (b === undefined) return a !== undefined; + + return ( + stringPending(a.title, b.title) || + stringPending(a.originalTitle, b.originalTitle) || + stringPending(a.url, b.url) || + stringPending(a.date, b.date) || + a.category !== b.category || + a.rating !== b.rating || + a.censorship !== b.censorship || + a.language !== b.language || + a.direction !== b.direction || + a.layout !== b.layout || + associationPending(a.artists, b.artists) || + associationPending(a.circles, b.circles) || + associationPending(a.characters, b.characters) || + associationPending(a.tags, b.tags) || + associationPending(a.worlds, b.worlds) + ); +} diff --git a/frontend/src/lib/Meta.ts b/frontend/src/lib/Meta.ts index 8cfad6b..24012cb 100644 --- a/frontend/src/lib/Meta.ts +++ b/frontend/src/lib/Meta.ts @@ -1 +1 @@ -export const codename = 'Satanic Satyr'; +export const codename = 'Profligate Pixie'; diff --git a/frontend/src/lib/Navigation.ts b/frontend/src/lib/Navigation.ts index e6b17cd..4dcb998 100644 --- a/frontend/src/lib/Navigation.ts +++ b/frontend/src/lib/Navigation.ts @@ -1,36 +1,44 @@ import { goto as svelteGoto } from '$app/navigation'; -import { SortDirection } from '$gql/graphql'; +import { SortDirection, type ComicFilter } from '$gql/graphql'; import JsonURL from '@jsonurl/jsonurl'; -import { type PaginationData } from './Pagination'; -import { type SortData } from './Sort'; import { toastError } from './Toasts'; +import type { Key } from './Utils'; -function paramToNum<T>(value: string | null, fallback: T) { - if (value) { - const number = +value; +export interface PaginationData { + page: number; + items: number; +} - if (Number.isNaN(number) || number < 0) { - return fallback; - } +export interface SortData<T extends Key> { + on: T; + direction: SortDirection; + seed: number | undefined; +} + +function number<T>(value: string | null, fallback: T) { + if (!value) return fallback; - return number; + const number = +value; + + if (Number.isNaN(number) || number < 0) { + return fallback; } - return fallback; + return number; } -export function parseSortData<T>(params: URLSearchParams, fallback: T): SortData<T> { +export function parseSortData<T extends Key>(params: URLSearchParams, fallback: T): SortData<T> { return { on: (params.get('s') as T) || fallback, direction: (params.get('d') as SortDirection) || SortDirection.Ascending, - seed: paramToNum(params.get('r'), undefined) + seed: number(params.get('r'), undefined) }; } export function parsePaginationData(params: URLSearchParams, defaultItems = 120): PaginationData { return { - page: paramToNum(params.get('p'), 1), - items: paramToNum(params.get('i'), defaultItems) + page: number(params.get('p'), 1), + items: number(params.get('i'), defaultItems) }; } @@ -41,7 +49,7 @@ export function parseFilter<T>(params: URLSearchParams): T { try { return JsonURL.parse(param, { AQF: true, impliedObject: {} }) as T; - } catch (e) { + } catch { return {} as T; } } @@ -62,7 +70,7 @@ interface NavigationParameters<T> { pagination?: Partial<PaginationData>; } -function paramsFrom<T>( +function parametersFrom<T>( { pagination, filter, sort }: NavigationParameters<T>, current?: URLSearchParams ) { @@ -102,13 +110,17 @@ function paramsFrom<T>( return params; } -export function navigate(parameters: NavigationParameters<object>, current?: URLSearchParams) { +export function navigate(params: NavigationParameters<object>, current?: URLSearchParams) { goto({ - params: paramsFrom(parameters, current), + params: parametersFrom(params, current), options: { noScroll: false, keepFocus: true, replaceState: true } }); } export function href<T>(base: string, params: NavigationParameters<T>) { - return `/${base}/?${paramsFrom(params).toString()}`; + return `/${base}/?${parametersFrom(params).toString()}`; +} + +export function quickComicFilter(id: number | string, filter: keyof ComicFilter) { + window.open(href('comics', { filter: { include: { [filter]: { all: [id] } } } })); } diff --git a/frontend/src/lib/Pagination.ts b/frontend/src/lib/Pagination.ts deleted file mode 100644 index f05492b..0000000 --- a/frontend/src/lib/Pagination.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { navigate } from '$lib/Navigation'; -import { getContext, setContext } from 'svelte'; -import { writable, type Writable } from 'svelte/store'; - -export interface PaginationData { - page: number; - items: number; -} - -export class PaginationContext { - page = 0; - items = 0; - total = 0; - - set update({ page, items }: PaginationData) { - this.page = page; - this.items = items; - } - - apply(params: URLSearchParams) { - navigate({ pagination: { items: this.items } }, params); - } -} - -export function initPaginationContext() { - return setContext<Writable<PaginationContext>>('pagination', writable(new PaginationContext())); -} - -export function getPaginationContext() { - return getContext<Writable<PaginationContext>>('pagination'); -} diff --git a/frontend/src/lib/Reader.ts b/frontend/src/lib/Reader.svelte.ts index 8777b9b..f5a5322 100644 --- a/frontend/src/lib/Reader.ts +++ b/frontend/src/lib/Reader.svelte.ts @@ -1,6 +1,5 @@ import { Layout, type PageFragment } from '$gql/graphql'; import { getContext, setContext } from 'svelte'; -import { writable, type Writable } from 'svelte/store'; export interface Chunk { main: PageFragment; @@ -9,25 +8,23 @@ export interface Chunk { } class ReaderContext { - visible = false; - sidebar = false; - pages: PageFragment[] = []; - page = 0; + visible = $state(false); + sidebar = $state(false); + pages: PageFragment[] = $state([]); + page = $state(0); - open(page: number) { + open = (page: number) => { this.page = page; this.visible = true; - - return this; - } + }; } export function initReaderContext() { - return setContext<Writable<ReaderContext>>('reader', writable(new ReaderContext())); + return setContext<ReaderContext>('reader', new ReaderContext()); } export function getReaderContext() { - return getContext<Writable<ReaderContext>>('reader'); + return getContext<ReaderContext>('reader'); } export function partition(pages: PageFragment[], layout: Layout): [Chunk[], number[]] { diff --git a/frontend/src/lib/Selection.ts b/frontend/src/lib/Selection.ts deleted file mode 100644 index 0ea85cc..0000000 --- a/frontend/src/lib/Selection.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { getContext, hasContext, setContext } from 'svelte'; -import { writable, type Writable } from 'svelte/store'; -import { range } from './Utils'; - -interface Item { - id: number; -} - -export const hasSelectionContext = () => hasContext('selection'); - -export function getSelectionContext<T extends Item>() { - return getContext<Writable<ItemSelection<T>>>('selection'); -} - -export function initSelectionContext<T extends Item>( - typename?: string, - toName?: (item: T) => string -) { - return setContext<Writable<ItemSelection<T>>>( - 'selection', - writable(new ItemSelection(typename, toName)) - ); -} - -export class ItemSelection<T extends Item> { - active = false; - typename: string; - #toName: (item: T) => string; - - #view: T[] = []; - selectable: (item: T) => boolean = () => true; - - #ids = new Set<number>(); - #masked = new Set<number>(); - - constructor(typename?: string, toName?: (item: T) => string) { - this.typename = typename ?? 'unknown'; - this.#toName = toName ?? (() => 'unknown'); - } - - set view(view: T[]) { - this.#view = view; - this.#updateMasked(); - } - - #indexOf = (id: number) => this.#view.findIndex((v) => v.id === id); - - update(index: number, shift: boolean) { - const id = this.#view[index].id; - - const selectableRange = (first: number, last: number) => - range(first, last) - .filter((i) => this.selectable(this.#view[i])) - .map((i) => this.#view[i].id); - - if (shift) { - const indices = this.indices; - - const first = indices.at(0); - const last = indices.at(-1); - - if (first === undefined || last === undefined) { - this.#ids.add(id); - } else if (index === first || index === last) { - this.#ids.clear(); - } else if (index > last) { - this.#ids = new Set([...this.#ids, ...selectableRange(last, index)]); - } else if (index < last) { - this.#ids = new Set([...this.#ids, ...selectableRange(index, last)]); - } - } else { - if (this.#ids.has(id)) { - this.#ids.delete(id); - } else { - this.#ids.add(id); - } - } - - this.#updateMasked(); - - return this; - } - - toggle() { - this.active = !this.active; - - if (!this.active) { - return this.none(); - } - - return this; - } - - all() { - this.#ids = new Set(this.#view.filter(this.selectable).map((i) => i.id)); - this.#updateMasked(); - - return this; - } - - none() { - this.#ids.clear(); - this.#masked.clear(); - - return this; - } - - clear() { - this.active = false; - - return this.none(); - } - - contains(id: number) { - return this.#masked.has(id); - } - - #updateMasked() { - this.#masked = new Set([...this.#ids].filter((i) => this.#indexOf(i) >= 0)); - } - - get ids() { - return [...this.#masked]; - } - - get size() { - return this.#masked.size; - } - - get indices() { - return [...this.#ids].map(this.#indexOf).filter((i) => i >= 0); - } - - get items() { - return this.indices.map((i) => this.#view[i]); - } - - get names() { - return this.items.map(this.#toName); - } -} diff --git a/frontend/src/lib/Shortcuts.ts b/frontend/src/lib/Shortcuts.ts index 063bd40..259500c 100644 --- a/frontend/src/lib/Shortcuts.ts +++ b/frontend/src/lib/Shortcuts.ts @@ -1,5 +1,4 @@ -import { closeModal, modals } from 'svelte-modals'; -import { get } from 'svelte/store'; +import { modals } from 'svelte-modals'; type LowercaseLetter = | 'a' @@ -32,12 +31,12 @@ type UppercaseLetter = Uppercase<LowercaseLetter>; type Letter = LowercaseLetter | UppercaseLetter; type Special = '?' | 'Enter' | 'Escape' | 'Delete'; -const modeSwitches = ['n', 'g', 'i'] as const; +const modeSwitches = ['n', 'g', 'i', 'e'] as const; type ModeSwitch = (typeof modeSwitches)[number]; function isModeSwitch(s: string): s is ModeSwitch { - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument - return modeSwitches.indexOf(s as any) !== -1; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return modeSwitches.includes(s as any); } type Key = Letter | Special; @@ -68,8 +67,8 @@ export function handleShortcuts(event: KeyboardEvent) { } if (event.key === 'Escape') { - if (get(modals).length > 0) { - closeModal(); + if (modals.stack.length > 0) { + modals.close(); event.preventDefault(); event.stopImmediatePropagation(); return; @@ -84,7 +83,7 @@ export function handleShortcuts(event: KeyboardEvent) { const handler = handlers.get(mode === undefined ? event.key : `${mode}${event.key}`); - if (!handler || get(modals).length > 0) { + if (!handler || modals.stack.length > 0) { mode = undefined; return; } diff --git a/frontend/src/lib/Sort.ts b/frontend/src/lib/Sort.ts deleted file mode 100644 index 4c9a353..0000000 --- a/frontend/src/lib/Sort.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { SortDirection } from '$gql/graphql'; -import { getContext, setContext } from 'svelte'; -import { writable, type Writable } from 'svelte/store'; -import { navigate } from './Navigation'; - -export interface SortData<T> { - on: T; - direction: SortDirection; - seed: number | undefined; -} - -export class SortContext<T extends string> { - on: T; - direction: SortDirection; - seed: number | undefined; - labels: Record<T, string>; - - constructor({ on, direction, seed }: SortData<T>, labels: Record<T, string>) { - this.on = on; - this.direction = direction; - this.seed = seed; - this.labels = labels; - } - - set update({ on, direction, seed }: SortData<T>) { - this.on = on; - this.direction = direction; - this.seed = seed; - } - - apply(params: URLSearchParams) { - navigate({ sort: { on: this.on, direction: this.direction, seed: this.seed } }, params); - } -} - -export function initSortContext<T extends string>(sort: SortData<T>, labels: Record<T, string>) { - return setContext<Writable<SortContext<T>>>('sort', writable(new SortContext(sort, labels))); -} - -export function getSortContext<T extends string>() { - return getContext<Writable<SortContext<T>>>('sort'); -} diff --git a/frontend/src/lib/Tabs.ts b/frontend/src/lib/Tabs.ts deleted file mode 100644 index 1c43068..0000000 --- a/frontend/src/lib/Tabs.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getContext, setContext } from 'svelte'; -import { writable, type Writable } from 'svelte/store'; - -type Tab = string; -type Tabs = Record<Tab, { title: string; badge?: boolean }>; - -interface TabContext { - tabs: Tabs; - current: Tab; -} - -export function setTabContext(context: TabContext) { - return setContext<Writable<TabContext>>('tabs', writable(context)); -} - -export function getTabContext() { - return getContext<Writable<TabContext>>('tabs'); -} diff --git a/frontend/src/lib/Toasts.ts b/frontend/src/lib/Toasts.ts index abc9a7d..224989b 100644 --- a/frontend/src/lib/Toasts.ts +++ b/frontend/src/lib/Toasts.ts @@ -15,5 +15,5 @@ export function toastError(message: string) { }); } -// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const toastFinally = (reason: any) => toastError(reason); diff --git a/frontend/src/lib/Transitions.ts b/frontend/src/lib/Transitions.ts index 59ebaf2..4e854f4 100644 --- a/frontend/src/lib/Transitions.ts +++ b/frontend/src/lib/Transitions.ts @@ -1,10 +1,8 @@ -import { quartInOut } from 'svelte/easing'; import type { FadeParams, SlideParams } from 'svelte/transition'; export const fadeFast: FadeParams = { duration: 60 }; export const fadeDefault: FadeParams = { duration: 100 }; -export const slideYDefault: SlideParams = { axis: 'y', duration: 300, easing: quartInOut }; - -export const slideXDefault: SlideParams = { axis: 'x', duration: 300, easing: quartInOut }; +export const slideYDefault: SlideParams = { axis: 'y', duration: 300 }; +export const slideXDefault: SlideParams = { axis: 'x', duration: 300 }; export const slideXFast: SlideParams = { axis: 'x', duration: 200 }; diff --git a/frontend/src/lib/Update.ts b/frontend/src/lib/Update.svelte.ts index 507dd52..1d684d5 100644 --- a/frontend/src/lib/Update.ts +++ b/frontend/src/lib/Update.svelte.ts @@ -4,17 +4,14 @@ import { type UpdateOptions, type UpdateTagInput } from '$gql/graphql'; - -type Key = string | number | symbol; +import type { Key } from './Utils'; interface AssociationUpdate { ids?: number[] | string[] | null; options?: UpdateOptions | null; } -type Input<T, K extends Key> = { - [Property in K]?: T | null; -}; +type Input<T, K extends Key> = Partial<Record<K, T | null>>; abstract class Entry<K extends Key> { key: K; @@ -28,10 +25,10 @@ abstract class Entry<K extends Key> { } class Association<K extends Key> extends Entry<K> { - ids = []; - options = { + ids = $state([]); + options = $state({ mode: UpdateMode.Add - }; + }); constructor(key: K) { super(key); @@ -49,7 +46,7 @@ class Association<K extends Key> extends Entry<K> { } class Enum<K extends Key> extends Entry<K> { - value?: string = undefined; + value?: string = $state(undefined); constructor(key: K) { super(key); @@ -67,13 +64,13 @@ class Enum<K extends Key> extends Entry<K> { } abstract class Controls<I> { - toInput() { + input() { const input = {} as I; Object.values(this).forEach((v: Entry<keyof I>) => v.integrate(input)); return input; } - hasInput() { + pending() { return Object.values(this).some((i: Entry<keyof I>) => i.hasInput()); } } diff --git a/frontend/src/lib/Utils.ts b/frontend/src/lib/Utils.ts index 1a07be1..c347544 100644 --- a/frontend/src/lib/Utils.ts +++ b/frontend/src/lib/Utils.ts @@ -2,7 +2,8 @@ import { isError } from '$gql/Utils'; import type { ImageFragment } from '$gql/graphql'; import type { BeforeNavigate } from '@sveltejs/kit'; import type { OperationResultState } from '@urql/svelte'; -import { openModal } from 'svelte-modals'; +import { modals } from 'svelte-modals'; +import { toastFinally } from './Toasts'; import ConfirmDeletion from './dialogs/ConfirmDeletion.svelte'; export function range(from: number, to: number) { @@ -16,6 +17,8 @@ export function getRandomInt(min: number, max: number) { return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); } +export type Key = string | number | symbol; + export interface ListItem { id: number | string; name: string; @@ -32,7 +35,6 @@ export function getResultState(state: OperationResultState): ResultState { if (state.error) { message = `${state.error.name}: ${state.error.message}`; } else if (state.data) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const obj = Object.values(state.data)[0]; if (isError(obj)) { message = obj.message; @@ -68,11 +70,14 @@ export function confirmDeletion( callback: () => void, warning?: string ) { - openModal( - ConfirmDeletion, - { names: Array.isArray(names) ? names : [names], typename, callback: callback, warning }, - { replace: true } - ); + modals + .open(ConfirmDeletion, { + names: Array.isArray(names) ? names : [names], + typename, + callback, + warning + }) + .catch(toastFinally); } export function idFromLabel(label: string) { @@ -87,7 +92,7 @@ export function formatListSize(word: string, size: number) { return `${size} ${pluralize(word, size)}`; } -export function joinText(items: string[], separator = ', ') { +export function joinText(items: (string | undefined | null)[], separator = ', ') { return items.filter((i) => i).join(separator); } @@ -106,3 +111,12 @@ export function preventOnPending({ to, cancel }: BeforeNavigate, pending: boolea cancel(); } +export function omit<T, K extends keyof T>(obj: T, ...props: K[]): Omit<T, K> { + return props.reduce( + (o, k) => { + delete o[k]; + return o; + }, + { ...obj } + ); +} diff --git a/frontend/src/lib/components/AddButton.svelte b/frontend/src/lib/components/AddButton.svelte index 9c0ab29..7a07bd7 100644 --- a/frontend/src/lib/components/AddButton.svelte +++ b/frontend/src/lib/components/AddButton.svelte @@ -1,7 +1,14 @@ <script lang="ts"> - export let title: string; + import type { MouseEventHandler } from 'svelte/elements'; + + interface Props { + title: string; + onclick: MouseEventHandler<HTMLButtonElement>; + } + + let { title, onclick }: Props = $props(); </script> -<button class="btn-blue" {title} on:click> - <span class="icon-base icon-[material-symbols--add]" /> +<button class="btn-blue" {title} aria-label={title} {onclick}> + <span class="icon-base icon-[material-symbols--add]"></span> </button> diff --git a/frontend/src/lib/components/ArchiveCard.svelte b/frontend/src/lib/components/ArchiveCard.svelte new file mode 100644 index 0000000..c9d283b --- /dev/null +++ b/frontend/src/lib/components/ArchiveCard.svelte @@ -0,0 +1,39 @@ +<script lang="ts"> + import type { ArchiveFragment } from '$gql/graphql'; + import FooterPill from '$lib/pills/FooterPill.svelte'; + import { filesize } from 'filesize'; + import { type Snippet } from 'svelte'; + import Card from './Card.svelte'; + + interface Props { + archive: ArchiveFragment; + overlay?: Snippet; + onclick?: (event: MouseEvent) => void; + } + + let { archive, overlay, onclick }: Props = $props(); + + let details = $derived({ + title: archive.name, + cover: archive.cover + }); + let href = $derived(`/archives/${archive.id.toString()}`); +</script> + +<Card {details} {href} {onclick} {overlay}> + {#snippet footer()} + <div class="flex flex-wrap gap-1"> + <FooterPill text={`${archive.pageCount} pages`}> + {#snippet icon()} + <span class="icon-[material-symbols--description] mr-0.5 text-sm"></span> + {/snippet} + </FooterPill> + <div class="flex grow"></div> + <FooterPill text={filesize(archive.size, { base: 2 })}> + {#snippet icon()} + <span class="icon-[material-symbols--hard-drive] mr-0.5 text-sm"></span> + {/snippet} + </FooterPill> + </div> + {/snippet} +</Card> diff --git a/frontend/src/lib/components/Badge.svelte b/frontend/src/lib/components/Badge.svelte index 7ad3173..8de5e34 100644 --- a/frontend/src/lib/components/Badge.svelte +++ b/frontend/src/lib/components/Badge.svelte @@ -2,12 +2,12 @@ import { fadeDefault } from '$lib/Transitions'; import { fade } from 'svelte/transition'; - export let number: number; + let { number }: { number: number } = $props(); </script> {#if number > 0} <span - class="absolute -right-[3px] -top-[6px] z-[1] rounded-lg bg-teal-600 px-1 text-xs" + class="absolute -top-1.5 -right-1 z-1 rounded-xs bg-teal-600 px-1 text-xs font-semibold drop-shadow-sm" transition:fade={fadeDefault} > {number} diff --git a/frontend/src/lib/components/BookmarkButton.svelte b/frontend/src/lib/components/BookmarkButton.svelte index 89570e6..bdcbd75 100644 --- a/frontend/src/lib/components/BookmarkButton.svelte +++ b/frontend/src/lib/components/BookmarkButton.svelte @@ -1,9 +1,15 @@ <script lang="ts"> import Bookmark from '$lib/icons/Bookmark.svelte'; + import type { MouseEventHandler } from 'svelte/elements'; - export let bookmarked: boolean; + interface Props { + bookmarked: boolean; + onclick: MouseEventHandler<HTMLButtonElement>; + } + + let { bookmarked, onclick }: Props = $props(); </script> -<button type="button" title="Toggle bookmark" class="flex text-base" on:click> +<button type="button" title="Toggle bookmark" class="flex text-base" {onclick}> <Bookmark hoverable {bookmarked} /> </button> diff --git a/frontend/src/lib/components/Card.svelte b/frontend/src/lib/components/Card.svelte index 2384799..8a2b047 100644 --- a/frontend/src/lib/components/Card.svelte +++ b/frontend/src/lib/components/Card.svelte @@ -1,5 +1,8 @@ -<script lang="ts" context="module"> - import type { ComicFragment, ImageFragment } from '$gql/graphql'; +<script lang="ts"> + import type { ImageFragment } from '$gql/graphql'; + import { src } from '$lib/Utils'; + import Star from '$lib/icons/Star.svelte'; + import type { Snippet } from 'svelte'; interface CardDetails { title: string; @@ -8,41 +11,40 @@ cover?: ImageFragment; } - export function comicCard(comic: ComicFragment) { - return { - href: `/comics/${comic.id.toString()}`, - details: { - title: comic.title, - subtitle: comic.originalTitle, - favourite: comic.favourite, - cover: comic.cover - } - }; + interface Props { + href: string; + details: CardDetails; + compact?: boolean; + coverOnly?: boolean; + overlay?: Snippet; + children?: Snippet; + footer?: Snippet; + onclick?: (event: MouseEvent) => void; } -</script> -<script lang="ts"> - import { src } from '$lib/Utils'; - import Star from '$lib/icons/Star.svelte'; - - export let href: string; - export let details: CardDetails; - export let compact = false; - export let coverOnly = false; - export let ellipsis = true; + let { + href, + details, + compact = false, + coverOnly = false, + overlay, + children, + footer, + onclick + }: Props = $props(); </script> <a {href} - class="grid-card-v sm:grid-card-h relative grid overflow-hidden rounded bg-slate-900 shadow-md shadow-slate-950/30" + class="grid-card-v sm:grid-card-h relative grid overflow-hidden rounded-sm bg-slate-900 shadow-md shadow-slate-950/30 focus-visible:outline-4 focus-visible:outline-blue-600" class:compact class:grid-card-cover-only={coverOnly} - on:click + {onclick} > - <slot name="overlay" /> + {@render overlay?.()} {#if details.cover} <img - class="h-full w-full object-cover object-[center_top]" + class="h-full w-full object-cover object-[left_top]" width={details.cover.width} height={details.cover.height} src={src(details.cover)} @@ -51,13 +53,9 @@ /> {/if} {#if !coverOnly} - <article class="flex h-full flex-col gap-2 p-2"> - <header> - <h2 - class:ellipsis-nowrap={ellipsis} - class="self-center text-sm font-medium [grid-area:title]" - title={details.title} - > + <article class="p flex h-full flex-col p-2 pb-1"> + <header class="mb-2"> + <h2 class="self-center text-sm font-medium [grid-area:title]" title={details.title}> {details.title} </h2> {#if details.subtitle} @@ -75,9 +73,15 @@ {/if} </header> - <section class="max-h-full grow overflow-auto border-t border-slate-800/80 pt-2 text-xs"> - <slot /> + <section class="max-h-full grow overflow-auto border-y border-slate-800/80 pt-2 text-xs"> + {@render children?.()} </section> + + {#if footer} + <div class="mt-1 text-xs"> + {@render footer()} + </div> + {/if} </article> {/if} </a> diff --git a/frontend/src/lib/components/Cardlet.svelte b/frontend/src/lib/components/Cardlet.svelte index 04d8599..cfbbd58 100644 --- a/frontend/src/lib/components/Cardlet.svelte +++ b/frontend/src/lib/components/Cardlet.svelte @@ -1,27 +1,25 @@ <script lang="ts"> - import type { ComicFilter } from '$gql/graphql'; - import { href } from '$lib/Navigation'; + import type { Snippet } from 'svelte'; - export let name: string; - export let title: string | null | undefined = undefined; - - export let filter: keyof ComicFilter | undefined = undefined; - export let id: number | string | undefined = undefined; + interface Props { + name: string; + title?: string | null; + overlay?: Snippet; + onclick: (event: MouseEvent) => void; + onauxclick?: (event: MouseEvent) => void; + } - const handleAux = (e: MouseEvent) => { - if (filter === undefined || id === undefined || e.button !== 1) return; - window.open(href('comics', { filter: { include: { [filter]: { all: [id] } } } })); - }; + let { name, title = undefined, overlay, onclick, onauxclick = undefined }: Props = $props(); </script> <button type="button" - class="relative flex overflow-hidden rounded bg-slate-900 text-left shadow-md shadow-slate-950/20" + class="relative flex overflow-hidden rounded-sm bg-slate-900 text-left shadow-md shadow-slate-950/20" {title} - on:click - on:auxclick={handleAux} + {onclick} + {onauxclick} > - <slot name="overlay" /> + {@render overlay?.()} <article class="group h-full grow items-center gap-2 p-2 text-xs"> <h2 class="ellipsis-nowrap text-sm font-medium">{name}</h2> </article> diff --git a/frontend/src/lib/components/ComicCard.svelte b/frontend/src/lib/components/ComicCard.svelte new file mode 100644 index 0000000..1a648b2 --- /dev/null +++ b/frontend/src/lib/components/ComicCard.svelte @@ -0,0 +1,75 @@ +<script lang="ts"> + import type { ComicFragment } from '$gql/graphql'; + import FooterPill from '$lib/pills/FooterPill.svelte'; + import Pill from '$lib/pills/Pill.svelte'; + import TagPill from '$lib/pills/TagPill.svelte'; + import { type Snippet } from 'svelte'; + import Card from './Card.svelte'; + + interface Props { + comic: ComicFragment; + overlay?: Snippet; + compact?: boolean; + coverOnly?: boolean; + onclick?: (event: MouseEvent) => void; + } + + let { comic, overlay, compact, coverOnly, onclick }: Props = $props(); + + let details = $derived({ + title: comic.title, + subtitle: comic.originalTitle, + favourite: comic.favourite, + cover: comic.cover + }); + let href = $derived(`/comics/${comic.id.toString()}`); +</script> + +<Card {details} {href} {compact} {onclick} {overlay} {coverOnly}> + <div class="flex flex-col gap-1"> + {#if comic.artists.length || comic.circles.length} + <div class="flex flex-wrap gap-1"> + {#each comic.artists as { name } (name)} + <Pill {name} style="artist" /> + {/each} + {#each comic.circles as { name } (name)} + <Pill {name} style="circle" /> + {/each} + </div> + {/if} + {#if comic.characters.length || comic.worlds.length} + <div class="flex flex-wrap gap-1"> + {#each comic.worlds as { name } (name)} + <Pill {name} style="world" /> + {/each} + {#each comic.characters as { name } (name)} + <Pill {name} style="character" /> + {/each} + </div> + {/if} + {#if comic.tags.length} + <div class="flex flex-wrap gap-1"> + {#each comic.tags as { name, description } (name)} + <TagPill {name} {description} /> + {/each} + </div> + {/if} + </div> + {#snippet footer()} + <div class="flex flex-wrap gap-1"> + <FooterPill text={`${comic.pageCount} pages`}> + {#snippet icon()} + <span class="icon-[material-symbols--description] mr-0.5 text-sm"></span> + {/snippet} + </FooterPill> + <div class="flex grow"></div> + {#if comic.date} + <FooterPill text={comic.date}> + {#snippet icon()} + <span class="icon-[material-symbols--calendar-today] mr-0.5 text-sm"></span> + {/snippet} + </FooterPill> + {/if} + </div> + {/snippet} +</Card> diff --git a/frontend/src/lib/components/DeleteButton.svelte b/frontend/src/lib/components/DeleteButton.svelte index 8f5f116..4659e13 100644 --- a/frontend/src/lib/components/DeleteButton.svelte +++ b/frontend/src/lib/components/DeleteButton.svelte @@ -1,15 +1,23 @@ -<script> +<script lang="ts"> import { accelerator } from '$lib/Shortcuts'; + import type { MouseEventHandler } from 'svelte/elements'; - export let prominent = false; + interface Props { + prominent?: boolean; + onclick: MouseEventHandler<HTMLButtonElement>; + } + + let { prominent = false, onclick }: Props = $props(); </script> <button type="button" - class={prominent ? 'btn-rose' : 'btn-slate hover:bg-rose-700'} + class:prominent + class="[&.prominent]:btn-rose btn-slate hover:bg-rose-700" title="Delete forever" - on:click + aria-label="Delete forever" + {onclick} use:accelerator={'Delete'} > - <span class="icon-base icon-[material-symbols--delete-forever]" /> + <span class="icon-base icon-[material-symbols--delete-forever]"></span> </button> diff --git a/frontend/src/lib/components/Dialog.svelte b/frontend/src/lib/components/Dialog.svelte index a0bbe5e..ec647ba 100644 --- a/frontend/src/lib/components/Dialog.svelte +++ b/frontend/src/lib/components/Dialog.svelte @@ -1,16 +1,23 @@ <script lang="ts"> import { trapFocus } from '$lib/Actions'; import { fadeDefault } from '$lib/Transitions'; - import { closeModal } from 'svelte-modals'; + import type { Snippet } from 'svelte'; + import type { ModalProps } from 'svelte-modals'; import { fade } from 'svelte/transition'; - export let isOpen: boolean; + interface Props extends ModalProps { + title: string; + children?: Snippet; + } + + // eslint-disable-next-line svelte/no-unused-props + let { isOpen, close, title, children }: Props = $props(); </script> {#if isOpen} <div role="dialog" - class="pointer-events-none fixed bottom-0 left-0 right-0 top-0 z-30 flex items-center justify-center" + class="pointer-events-none fixed top-0 right-0 bottom-0 left-0 z-30 flex items-center justify-center" transition:fade|global={fadeDefault} use:trapFocus > @@ -18,18 +25,19 @@ class="pointer-events-auto flex flex-col rounded-md bg-slate-800 shadow-md shadow-slate-900" > <header class="flex items-center gap-1 border-b-2 border-slate-700/50 p-2"> - <slot name="header" /> + <h2>{title}</h2> <button type="button" class="ml-auto flex items-center text-white/30 hover:text-white" title="Cancel" - on:click={closeModal} + aria-label="Cancel" + onclick={close} > - <span class="icon-base icon-[material-symbols--close]" /> + <span class="icon-base icon-[material-symbols--close]"></span> </button> </header> <main class="m-3 w-80 sm:w-[34rem]"> - <slot /> + {@render children?.()} </main> </div> </div> diff --git a/frontend/src/lib/components/Dropdown.svelte b/frontend/src/lib/components/Dropdown.svelte index 9e935e4..e2979e6 100644 --- a/frontend/src/lib/components/Dropdown.svelte +++ b/frontend/src/lib/components/Dropdown.svelte @@ -1,18 +1,37 @@ <script lang="ts"> - import { clickOutside } from '$lib/Actions'; import { fadeFast } from '$lib/Transitions'; + import type { Snippet } from 'svelte'; import { fade } from 'svelte/transition'; - export let visible: boolean; - export let parent: HTMLElement; + interface Props { + button: Snippet<[() => void]>; + children?: Snippet; + } + + let { button, children }: Props = $props(); + + let visible = $state(false); + + function onfocusout(event: FocusEvent & { currentTarget: EventTarget & HTMLDivElement }) { + if ( + event.relatedTarget instanceof HTMLElement && + event.currentTarget.contains(event.relatedTarget) + ) { + return; + } + + visible = false; + } </script> -{#if visible} - <div - class="absolute z-[1] mt-1 w-max rounded bg-slate-700 p-1 shadow-sm shadow-slate-900" - transition:fade={fadeFast} - use:clickOutside={{ handler: () => (visible = false), ignore: parent }} - > - <slot /> - </div> -{/if} +<div class="relative" {onfocusout}> + {@render button(() => (visible = !visible))} + {#if visible} + <div + class="absolute z-1 mt-1 w-max rounded-sm bg-slate-700 p-1 shadow-xs shadow-slate-900" + transition:fade={fadeFast} + > + {@render children?.()} + </div> + {/if} +</div> diff --git a/frontend/src/lib/components/Expander.svelte b/frontend/src/lib/components/Expander.svelte deleted file mode 100644 index a382658..0000000 --- a/frontend/src/lib/components/Expander.svelte +++ /dev/null @@ -1,17 +0,0 @@ -<script lang="ts"> - export let expanded: boolean; - export let title: string; -</script> - -<button - class="flex items-center text-base hover:text-white" - type="button" - on:click={() => (expanded = !expanded)} -> - {#if expanded} - <span class="icon-base icon-[material-symbols--expand-less]" /> - {:else} - <span class="icon-base icon-[material-symbols--expand-more]" /> - {/if} - {title} -</button> diff --git a/frontend/src/lib/components/Guard.svelte b/frontend/src/lib/components/Guard.svelte index fd7ded4..38cbd65 100644 --- a/frontend/src/lib/components/Guard.svelte +++ b/frontend/src/lib/components/Guard.svelte @@ -1,9 +1,10 @@ <script lang="ts"> import { getResultState } from '$lib/Utils'; + import type { OperationResultStore } from '@urql/svelte'; import Spinner from './Spinner.svelte'; - export let result; - $: state = getResultState($result); + let { result }: { result: OperationResultStore } = $props(); + let state = $derived(getResultState($result)); </script> {#if state.fetching} diff --git a/frontend/src/lib/components/Head.svelte b/frontend/src/lib/components/Head.svelte index b4aed5b..5ddd543 100644 --- a/frontend/src/lib/components/Head.svelte +++ b/frontend/src/lib/components/Head.svelte @@ -1,6 +1,5 @@ <script lang="ts"> - export let section: string; - export let title = ''; + let { section, title = '' }: { section: string; title?: string } = $props(); function formatTitle(section: string, title?: string) { return [title, section, 'hircine'].filter((i) => i).join(' · '); diff --git a/frontend/src/lib/components/Labelled.svelte b/frontend/src/lib/components/Labelled.svelte deleted file mode 100644 index 4b36ad6..0000000 --- a/frontend/src/lib/components/Labelled.svelte +++ /dev/null @@ -1,10 +0,0 @@ -<script lang="ts"> - import { idFromLabel } from '$lib/Utils'; - - export let label: string; - - const id = idFromLabel(label); -</script> - -<label class="self-center" for={id}>{label}</label> -<slot {id} /> diff --git a/frontend/src/lib/components/LabelledBlock.svelte b/frontend/src/lib/components/LabelledBlock.svelte index feb563e..8f93667 100644 --- a/frontend/src/lib/components/LabelledBlock.svelte +++ b/frontend/src/lib/components/LabelledBlock.svelte @@ -1,7 +1,14 @@ <script lang="ts"> import { idFromLabel } from '$lib/Utils'; + import type { Snippet } from 'svelte'; - export let label: string; + interface Props { + label: string; + side?: Snippet; + children?: Snippet<[{ id: string }]>; + } + + let { label, side, children }: Props = $props(); const id = idFromLabel(label); </script> @@ -9,10 +16,10 @@ <div class="flex flex-col"> <div class="flex"> <label for={id}>{label}</label> - {#if $$slots.controls} - <div class="grow" /> - <slot name="controls" /> + {#if side} + <div class="grow"></div> + {@render side?.()} {/if} </div> - <slot {id} /> + {@render children?.({ id })} </div> diff --git a/frontend/src/lib/components/OrganizedButton.svelte b/frontend/src/lib/components/OrganizedButton.svelte index 9be985c..3838f7d 100644 --- a/frontend/src/lib/components/OrganizedButton.svelte +++ b/frontend/src/lib/components/OrganizedButton.svelte @@ -1,9 +1,15 @@ <script lang="ts"> import Organized from '$lib/icons/Organized.svelte'; + import type { MouseEventHandler } from 'svelte/elements'; - export let organized: boolean; + interface Props { + organized: boolean; + onclick: MouseEventHandler<HTMLButtonElement>; + } + + let { organized, onclick }: Props = $props(); </script> -<button type="button" title="Toggle organized" class="flex text-base" on:click> +<button type="button" title="Toggle organized" class="flex text-base" {onclick}> <Organized hoverable {organized} /> </button> diff --git a/frontend/src/lib/components/RefreshButton.svelte b/frontend/src/lib/components/RefreshButton.svelte index afab640..70ee2d1 100644 --- a/frontend/src/lib/components/RefreshButton.svelte +++ b/frontend/src/lib/components/RefreshButton.svelte @@ -1,3 +1,9 @@ -<button class="btn-blue" title="Refresh" on:click> - <span class="icon-base icon-[material-symbols--sync]" /> +<script lang="ts"> + import type { MouseEventHandler } from 'svelte/elements'; + + let { onclick }: { onclick: MouseEventHandler<HTMLButtonElement> } = $props(); +</script> + +<button class="btn-blue" title="Refresh" aria-label="Refresh" {onclick}> + <span class="icon-base icon-[material-symbols--sync]"></span> </button> diff --git a/frontend/src/lib/components/RemovePageButton.svelte b/frontend/src/lib/components/RemovePageButton.svelte index e23c079..8045f32 100644 --- a/frontend/src/lib/components/RemovePageButton.svelte +++ b/frontend/src/lib/components/RemovePageButton.svelte @@ -1,13 +1,17 @@ <script lang="ts"> import { accelerator } from '$lib/Shortcuts'; + import type { MouseEventHandler } from 'svelte/elements'; + + let { onclick }: { onclick: MouseEventHandler<HTMLButtonElement> } = $props(); </script> <button type="button" class="btn-rose" title="Remove selected pages" - on:click + aria-label="Remove selected pages" + {onclick} use:accelerator={'Delete'} > - <span class="icon-base icon-[material-symbols--scan-delete]" /> + <span class="icon-base icon-[material-symbols--scan-delete]"></span> </button> diff --git a/frontend/src/lib/components/Select.svelte b/frontend/src/lib/components/Select.svelte index dece4a5..44828d3 100644 --- a/frontend/src/lib/components/Select.svelte +++ b/frontend/src/lib/components/Select.svelte @@ -2,19 +2,28 @@ import type { ListItem } from '$lib/Utils'; import Svelecte from 'svelecte'; - let inputId: string; - let valueAsObject = false; - let multiple = false; - type Item = number | string | ListItem; type Value = Item | Item[] | undefined | null; - export let clearable = false; - export let placeholder = 'Select...'; - export let options: ListItem[] | undefined; - export let value: Value; + interface Props { + id: string; + object?: boolean; + multi?: boolean; + clearable?: boolean; + placeholder?: string; + options: ListItem[] | undefined; + value: Value; + } - export { inputId as id, valueAsObject as object, multiple as multi }; + let { + id: inputId, + object: valueAsObject = false, + multi: multiple = false, + clearable = false, + placeholder = 'Select...', + options, + value = $bindable() + }: Props = $props(); </script> {#if options !== null && options !== undefined} diff --git a/frontend/src/lib/components/Spinner.svelte b/frontend/src/lib/components/Spinner.svelte index 946329c..d85c4f4 100644 --- a/frontend/src/lib/components/Spinner.svelte +++ b/frontend/src/lib/components/Spinner.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import { onDestroy } from 'svelte'; - let show = false; + let show = $state(false); const timeout = setTimeout(() => (show = true), 150); onDestroy(() => clearTimeout(timeout)); @@ -9,28 +9,7 @@ {#if show} <div class="flex h-full w-full items-center justify-center"> - <span class="spinner" /> + <span class="h-16 w-16 animate-spin rounded-full border-4 border-white/80 border-b-transparent" + ></span> </div> {/if} - -<style lang="postcss"> - .spinner { - width: 64px; - height: 64px; - border: 5px solid theme(colors.gray.200); - border-bottom-color: transparent; - border-radius: 50%; - display: inline-block; - box-sizing: border-box; - animation: rotation 1s linear infinite; - } - - @keyframes rotation { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } - } -</style> diff --git a/frontend/src/lib/components/SubmitButton.svelte b/frontend/src/lib/components/SubmitButton.svelte index 8ac90b9..3b89ba7 100644 --- a/frontend/src/lib/components/SubmitButton.svelte +++ b/frontend/src/lib/components/SubmitButton.svelte @@ -1,7 +1,7 @@ <script lang="ts"> - export let active = false; + let { pending = false }: { pending?: boolean } = $props(); - $: title = active ? 'Save pending changes' : 'Save (no changes pending)'; + let title = $derived(pending ? 'Save pending changes' : 'Save (no changes pending)'); </script> -<button type="submit" class:active class="btn-slate [&.active]:btn-blue" {title}>Save</button> +<button type="submit" class:pending class="btn-slate [&.pending]:btn-blue" {title}>Save</button> diff --git a/frontend/src/lib/components/Titlebar.svelte b/frontend/src/lib/components/Titlebar.svelte index 8aab2dd..bb36d8f 100644 --- a/frontend/src/lib/components/Titlebar.svelte +++ b/frontend/src/lib/components/Titlebar.svelte @@ -1,12 +1,15 @@ <script lang="ts"> import Star from '$lib/icons/Star.svelte'; - import { createEventDispatcher } from 'svelte'; + import type { MouseEventHandler } from 'svelte/elements'; - export let title: string; - export let subtitle: string | null = ''; - export let favourite: boolean | undefined = undefined; + interface Props { + title: string; + subtitle?: string | null; + favourite?: boolean; + onfavourite?: MouseEventHandler<HTMLButtonElement>; + } - const dispatch = createEventDispatcher<{ favourite: null }>(); + let { title, subtitle, favourite, onfavourite }: Props = $props(); </script> <div class="flex flex-wrap gap-x-4"> @@ -14,9 +17,9 @@ {#if favourite !== undefined} <button type="button" - class="mr-1 flex items-center" + class="mr-1 flex items-center focus-visible:bg-yellow-400/20 focus-visible:outline-hidden" title="Toggle favourite" - on:click={() => dispatch('favourite')} + onclick={onfavourite} > <Star large hoverable {favourite} /> </button> diff --git a/frontend/src/lib/containers/Cardlets.svelte b/frontend/src/lib/containers/Cardlets.svelte index 129da61..107ebee 100644 --- a/frontend/src/lib/containers/Cardlets.svelte +++ b/frontend/src/lib/containers/Cardlets.svelte @@ -1,11 +1,13 @@ <script> import { fadeDefault } from '$lib/Transitions'; import { fade } from 'svelte/transition'; + + let { children } = $props(); </script> <div - class="grid gap-4 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 min-[1600px]:grid-cols-8 min-[1920px]:grid-cols-10" + class="3xl:grid-cols-10 grid gap-4 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8" in:fade={fadeDefault} > - <slot /> + {@render children?.()} </div> diff --git a/frontend/src/lib/containers/Cards.svelte b/frontend/src/lib/containers/Cards.svelte index a19e8be..ab1f0fb 100644 --- a/frontend/src/lib/containers/Cards.svelte +++ b/frontend/src/lib/containers/Cards.svelte @@ -1,8 +1,10 @@ <script> import { fadeDefault } from '$lib/Transitions'; import { fade } from 'svelte/transition'; + + let { children } = $props(); </script> -<div class="grid gap-4 xl:grid-cols-2 min-[1920px]:grid-cols-3" in:fade|global={fadeDefault}> - <slot /> +<div class="3xl:grid-cols-3 grid gap-4 xl:grid-cols-2" in:fade|global={fadeDefault}> + {@render children?.()} </div> diff --git a/frontend/src/lib/containers/Carousel.svelte b/frontend/src/lib/containers/Carousel.svelte index 1268a78..fb05b7d 100644 --- a/frontend/src/lib/containers/Carousel.svelte +++ b/frontend/src/lib/containers/Carousel.svelte @@ -1,6 +1,13 @@ <script lang="ts"> - export let title: string; - export let href: string; + import type { Snippet } from 'svelte'; + + interface Props { + title: string; + href: string; + children?: Snippet; + } + + let { title, href, children }: Props = $props(); </script> <div class="flex flex-col gap-1"> @@ -10,6 +17,6 @@ </a> </h2> <div class="flex flex-wrap gap-5"> - <slot /> + {@render children?.()} </div> </div> diff --git a/frontend/src/lib/containers/Column.svelte b/frontend/src/lib/containers/Column.svelte index 05daece..fe5ac47 100644 --- a/frontend/src/lib/containers/Column.svelte +++ b/frontend/src/lib/containers/Column.svelte @@ -1,3 +1,7 @@ +<script> + let { children } = $props(); +</script> + <div class="flex flex-col gap-4"> - <slot /> + {@render children?.()} </div> diff --git a/frontend/src/lib/containers/Grid.svelte b/frontend/src/lib/containers/Grid.svelte index 1224156..af5125a 100644 --- a/frontend/src/lib/containers/Grid.svelte +++ b/frontend/src/lib/containers/Grid.svelte @@ -1,14 +1,16 @@ -<script> +<script lang="ts"> import { fadeDefault } from '$lib/Transitions'; - + import type { Snippet } from 'svelte'; import { fade } from 'svelte/transition'; + + let { children }: { children?: Snippet } = $props(); </script> <div class="flex flex-col gap-1 lg:grid lg:h-full lg:max-h-full lg:overflow-auto" in:fade|global={fadeDefault} > - <slot /> + {@render children?.()} </div> <style> diff --git a/frontend/src/lib/dialogs/AddArtist.svelte b/frontend/src/lib/dialogs/AddArtist.svelte index 6ec93c5..9fc2ca1 100644 --- a/frontend/src/lib/dialogs/AddArtist.svelte +++ b/frontend/src/lib/dialogs/AddArtist.svelte @@ -1,30 +1,22 @@ <script lang="ts"> - import { addArtist, type ArtistInput } from '$gql/Mutations'; + import type { AddArtistInput } from '$gql/graphql'; + import { addArtist } from '$gql/Mutations'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import ArtistForm from '$lib/forms/ArtistForm.svelte'; import { toastFinally } from '$lib/Toasts'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + let modal: ModalProps = $props(); + const initial = { name: '' }; - let artist = { name: '' }; - - function add(event: CustomEvent<ArtistInput>) { - addArtist(client, { input: event.detail }).then(closeModal).catch(toastFinally); + function submit(input: AddArtistInput) { + addArtist(client, { input }).then(modal.close).catch(toastFinally); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Add Artist</h2> - </svelte:fragment> - <ArtistForm bind:artist on:submit={add}> - <div class="flex justify-end gap-4"> - <SubmitButton active={artist.name.length > 0} /> - </div> - </ArtistForm> +<Dialog title="Add Artist" {...modal}> + <ArtistForm {initial} {submit} /> </Dialog> diff --git a/frontend/src/lib/dialogs/AddCharacter.svelte b/frontend/src/lib/dialogs/AddCharacter.svelte index 23fea08..1585e34 100644 --- a/frontend/src/lib/dialogs/AddCharacter.svelte +++ b/frontend/src/lib/dialogs/AddCharacter.svelte @@ -1,30 +1,22 @@ <script lang="ts"> - import { addCharacter, type CharacterInput } from '$gql/Mutations'; + import type { AddCharacterInput } from '$gql/graphql'; + import { addCharacter } from '$gql/Mutations'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import CharacterForm from '$lib/forms/CharacterForm.svelte'; import { toastFinally } from '$lib/Toasts'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + let modal: ModalProps = $props(); + const initial = { name: '' }; - let character = { name: '' }; - - function add(event: CustomEvent<CharacterInput>) { - addCharacter(client, { input: event.detail }).then(closeModal).catch(toastFinally); + function submit(input: AddCharacterInput) { + addCharacter(client, { input }).then(modal.close).catch(toastFinally); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Add Character</h2> - </svelte:fragment> - <CharacterForm bind:character on:submit={add}> - <div class="flex justify-end gap-4"> - <SubmitButton active={character.name.length > 0} /> - </div> - </CharacterForm> +<Dialog title="Add Character" {...modal}> + <CharacterForm {initial} {submit} /> </Dialog> diff --git a/frontend/src/lib/dialogs/AddCircle.svelte b/frontend/src/lib/dialogs/AddCircle.svelte index f0ef014..faffc63 100644 --- a/frontend/src/lib/dialogs/AddCircle.svelte +++ b/frontend/src/lib/dialogs/AddCircle.svelte @@ -1,30 +1,22 @@ <script lang="ts"> - import { addCircle, type CircleInput } from '$gql/Mutations'; + import type { AddCircleInput } from '$gql/graphql'; + import { addCircle } from '$gql/Mutations'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import CircleForm from '$lib/forms/CircleForm.svelte'; import { toastFinally } from '$lib/Toasts'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + let modal: ModalProps = $props(); + const initial = { name: '' }; - let circle = { name: '' }; - - function add(event: CustomEvent<CircleInput>) { - addCircle(client, { input: event.detail }).then(closeModal).catch(toastFinally); + function submit(input: AddCircleInput) { + addCircle(client, { input }).then(modal.close).catch(toastFinally); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Add Circle</h2> - </svelte:fragment> - <CircleForm bind:circle on:submit={add}> - <div class="flex justify-end gap-4"> - <SubmitButton active={circle.name.length > 0} /> - </div> - </CircleForm> +<Dialog title="Add Circle" {...modal}> + <CircleForm {initial} {submit} /> </Dialog> diff --git a/frontend/src/lib/dialogs/AddNamespace.svelte b/frontend/src/lib/dialogs/AddNamespace.svelte index e81b22a..45183f4 100644 --- a/frontend/src/lib/dialogs/AddNamespace.svelte +++ b/frontend/src/lib/dialogs/AddNamespace.svelte @@ -1,30 +1,22 @@ <script lang="ts"> - import { addNamespace, type NamespaceInput } from '$gql/Mutations'; + import type { AddNamespaceInput } from '$gql/graphql'; + import { addNamespace } from '$gql/Mutations'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import NamespaceForm from '$lib/forms/NamespaceForm.svelte'; import { toastFinally } from '$lib/Toasts'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + let modal: ModalProps = $props(); + const initial = { name: '' }; - let namespace = { name: '' }; - - function add(event: CustomEvent<NamespaceInput>) { - addNamespace(client, { input: event.detail }).then(closeModal).catch(toastFinally); + function submit(input: AddNamespaceInput) { + addNamespace(client, { input }).then(modal.close).catch(toastFinally); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Add Namespace</h2> - </svelte:fragment> - <NamespaceForm bind:namespace on:submit={add}> - <div class="flex justify-end gap-4"> - <SubmitButton active={namespace.name.length > 0} /> - </div> - </NamespaceForm> +<Dialog title="Add Namespace" {...modal}> + <NamespaceForm {initial} {submit} /> </Dialog> diff --git a/frontend/src/lib/dialogs/AddTag.svelte b/frontend/src/lib/dialogs/AddTag.svelte index 00d3a03..da78bce 100644 --- a/frontend/src/lib/dialogs/AddTag.svelte +++ b/frontend/src/lib/dialogs/AddTag.svelte @@ -1,30 +1,22 @@ <script lang="ts"> - import { addTag, type TagInput } from '$gql/Mutations'; + import type { AddTagInput } from '$gql/graphql'; + import { addTag } from '$gql/Mutations'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import TagForm from '$lib/forms/TagForm.svelte'; import { toastFinally } from '$lib/Toasts'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import { type ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + let modal: ModalProps = $props(); + const initial = { name: '', namespaces: [] }; - let tag = { name: '', namespaces: [] }; - - function add(event: CustomEvent<TagInput>) { - addTag(client, { input: event.detail }).then(closeModal).catch(toastFinally); + function submit(input: AddTagInput) { + addTag(client, { input }).then(modal.close).catch(toastFinally); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Add Tag</h2> - </svelte:fragment> - <TagForm bind:tag on:submit={add}> - <div class="flex justify-end gap-4"> - <SubmitButton active={tag.name.length > 0} /> - </div> - </TagForm> +<Dialog title="Add Tag" {...modal}> + <TagForm {initial} {submit} /> </Dialog> diff --git a/frontend/src/lib/dialogs/AddWorld.svelte b/frontend/src/lib/dialogs/AddWorld.svelte index ceb946e..075d872 100644 --- a/frontend/src/lib/dialogs/AddWorld.svelte +++ b/frontend/src/lib/dialogs/AddWorld.svelte @@ -1,30 +1,22 @@ <script lang="ts"> - import { addWorld, type WorldInput } from '$gql/Mutations'; + import type { AddWorldInput } from '$gql/graphql'; + import { addWorld } from '$gql/Mutations'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import WorldForm from '$lib/forms/WorldForm.svelte'; import { toastFinally } from '$lib/Toasts'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + let modal: ModalProps = $props(); + const initial = { name: '' }; - let world = { name: '' }; - - function add(event: CustomEvent<WorldInput>) { - addWorld(client, { input: event.detail }).then(closeModal).catch(toastFinally); + function submit(input: AddWorldInput) { + addWorld(client, { input }).then(modal.close).catch(toastFinally); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Add World</h2> - </svelte:fragment> - <WorldForm bind:world on:submit={add}> - <div class="flex justify-end gap-4"> - <SubmitButton active={world.name.length > 0} /> - </div> - </WorldForm> +<Dialog title="Add World" {...modal}> + <WorldForm {initial} {submit} /> </Dialog> diff --git a/frontend/src/lib/dialogs/ConfirmDeletion.svelte b/frontend/src/lib/dialogs/ConfirmDeletion.svelte index 6b0cbf8..53b1dd4 100644 --- a/frontend/src/lib/dialogs/ConfirmDeletion.svelte +++ b/frontend/src/lib/dialogs/ConfirmDeletion.svelte @@ -1,36 +1,37 @@ <script lang="ts"> import { accelerator } from '$lib/Shortcuts'; import Dialog from '$lib/components/Dialog.svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; - export let isOpen: boolean; - export let callback: () => void; + interface Props extends ModalProps { + callback: () => void; + names: string[]; + typename: string; + warning?: string; + } + + let { callback, names, typename, warning = undefined, ...modal }: Props = $props(); - export let names: string[]; - export let typename: string; - export let warning: string | undefined = undefined; const multiple = names.length > 1; const formattedTypename = multiple ? `${typename}s` : typename; const formattedNames = multiple ? `${names.length} ${formattedTypename}` : names[0]; - function confirm() { + function confirm(event: SubmitEvent) { + event.preventDefault(); callback(); - closeModal(); + modal.close(); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Delete {formattedTypename}</h2> - </svelte:fragment> - <form on:submit|preventDefault={confirm}> +<Dialog title="Delete {formattedTypename}" {...modal}> + <form onsubmit={confirm}> <div class="flex flex-col"> <p class="mb-3"> Are you sure you want to delete <span class="font-semibold">{formattedNames}</span>? </p> {#if multiple} <ul class="mb-3 ml-8 list-disc"> - {#each names.slice(0, 10) as name} + {#each names.slice(0, 10) as name (name)} <li>{name}</li> {/each} </ul> @@ -39,13 +40,15 @@ {/if} {/if} {#if warning} - <p class="font-medium text-red-600">Warning: {warning}</p> + <p class="rounded-sm border border-rose-700 bg-rose-800/70 p-2 text-white"> + {warning} + </p> {/if} </div> <div class="flex justify-end gap-4"> <button type="submit" class="btn-rose" use:accelerator={'Enter'}>Delete</button> - <button type="button" on:click={closeModal} class="btn-slate">Cancel</button> + <button type="button" onclick={() => modal.close()} class="btn-slate">Cancel</button> </div> </form> </Dialog> diff --git a/frontend/src/lib/dialogs/EditArtist.svelte b/frontend/src/lib/dialogs/EditArtist.svelte index dd08bc6..fa5c143 100644 --- a/frontend/src/lib/dialogs/EditArtist.svelte +++ b/frontend/src/lib/dialogs/EditArtist.svelte @@ -1,46 +1,37 @@ <script lang="ts"> - import { deleteArtists, updateArtists, type ArtistInput } from '$gql/Mutations'; - import { itemEquals } from '$gql/Utils'; - import { type Artist } from '$gql/graphql'; + import { deleteArtists, updateArtists } from '$gql/Mutations'; + import { omitIdentifiers } from '$gql/Utils'; + import type { Artist, UpdateArtistInput } from '$gql/graphql'; import { toastFinally } from '$lib/Toasts'; import { confirmDeletion } from '$lib/Utils'; import DeleteButton from '$lib/components/DeleteButton.svelte'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import ArtistForm from '$lib/forms/ArtistForm.svelte'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + interface Props extends ModalProps { + artist: Artist; + } - export let artist: Artist; - const original = structuredClone(artist); - $: pending = !itemEquals(artist, original); + let { artist, ...modal }: Props = $props(); + const initial = omitIdentifiers(artist); - function save(event: CustomEvent<ArtistInput>) { - updateArtists(client, { ids: artist.id, input: event.detail }) - .then(closeModal) - .catch(toastFinally); + function submit(input: UpdateArtistInput) { + updateArtists(client, { ids: artist.id, input }).then(modal.close).catch(toastFinally); } function deleteArtist() { confirmDeletion('Artist', artist.name, () => { - deleteArtists(client, { ids: artist.id }).then(closeModal).catch(toastFinally); + deleteArtists(client, { ids: artist.id }).then(modal.close).catch(toastFinally); }); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Edit Artist</h2> - </svelte:fragment> - <ArtistForm bind:artist on:submit={save}> - <div class="flex gap-4"> - <DeleteButton on:click={deleteArtist} /> - <div class="grow" /> - <SubmitButton active={pending} /> - </div> +<Dialog title="Edit Artist" {...modal}> + <ArtistForm {initial} {submit}> + <DeleteButton onclick={deleteArtist} /> </ArtistForm> </Dialog> diff --git a/frontend/src/lib/dialogs/EditCharacter.svelte b/frontend/src/lib/dialogs/EditCharacter.svelte index 3b45e78..71125db 100644 --- a/frontend/src/lib/dialogs/EditCharacter.svelte +++ b/frontend/src/lib/dialogs/EditCharacter.svelte @@ -1,46 +1,37 @@ <script lang="ts"> - import { deleteCharacters, updateCharacters, type CharacterInput } from '$gql/Mutations'; - import { itemEquals } from '$gql/Utils'; - import { type Character } from '$gql/graphql'; + import { deleteCharacters, updateCharacters } from '$gql/Mutations'; + import { omitIdentifiers } from '$gql/Utils'; + import type { Character, UpdateCharacterInput } from '$gql/graphql'; import { toastFinally } from '$lib/Toasts'; import { confirmDeletion } from '$lib/Utils'; import DeleteButton from '$lib/components/DeleteButton.svelte'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import CharacterForm from '$lib/forms/CharacterForm.svelte'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + interface Props extends ModalProps { + character: Character; + } - export let character: Character; - const original = structuredClone(character); - $: pending = !itemEquals(original, character); + let { character, ...modal }: Props = $props(); + const initial = omitIdentifiers(character); - function save(event: CustomEvent<CharacterInput>) { - updateCharacters(client, { ids: character.id, input: event.detail }) - .then(closeModal) - .catch(toastFinally); + function submit(input: UpdateCharacterInput) { + updateCharacters(client, { ids: character.id, input }).then(modal.close).catch(toastFinally); } function deleteCharacter() { confirmDeletion('Character', character.name, () => { - deleteCharacters(client, { ids: character.id }).then(closeModal).catch(toastFinally); + deleteCharacters(client, { ids: character.id }).then(modal.close).catch(toastFinally); }); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Edit Character</h2> - </svelte:fragment> - <CharacterForm bind:character on:submit={save}> - <div class="flex gap-4"> - <DeleteButton on:click={deleteCharacter} /> - <div class="grow" /> - <SubmitButton active={pending} /> - </div> +<Dialog title="Edit Character" {...modal}> + <CharacterForm {initial} {submit}> + <DeleteButton onclick={deleteCharacter} /> </CharacterForm> </Dialog> diff --git a/frontend/src/lib/dialogs/EditCircle.svelte b/frontend/src/lib/dialogs/EditCircle.svelte index bdc1217..7cb0f14 100644 --- a/frontend/src/lib/dialogs/EditCircle.svelte +++ b/frontend/src/lib/dialogs/EditCircle.svelte @@ -1,46 +1,37 @@ <script lang="ts"> - import { deleteCircles, updateCircles, type CircleInput } from '$gql/Mutations'; - import { itemEquals } from '$gql/Utils'; - import { type Circle } from '$gql/graphql'; + import { deleteCircles, updateCircles } from '$gql/Mutations'; + import { omitIdentifiers } from '$gql/Utils'; + import type { Circle, UpdateCircleInput } from '$gql/graphql'; import { toastFinally } from '$lib/Toasts'; import { confirmDeletion } from '$lib/Utils'; import DeleteButton from '$lib/components/DeleteButton.svelte'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import CircleForm from '$lib/forms/CircleForm.svelte'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + interface Props extends ModalProps { + circle: Circle; + } - export let circle: Circle; - const original = structuredClone(circle); - $: pending = !itemEquals(original, circle); + let { circle, ...modal }: Props = $props(); + const initial = omitIdentifiers(circle); - function save(event: CustomEvent<CircleInput>) { - updateCircles(client, { ids: circle.id, input: event.detail }) - .then(closeModal) - .catch(toastFinally); + function submit(input: UpdateCircleInput) { + updateCircles(client, { ids: circle.id, input }).then(modal.close).catch(toastFinally); } function deleteCircle() { confirmDeletion('Circle', circle.name, () => { - deleteCircles(client, { ids: circle.id }).then(closeModal).catch(toastFinally); + deleteCircles(client, { ids: circle.id }).then(modal.close).catch(toastFinally); }); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Edit Circle</h2> - </svelte:fragment> - <CircleForm bind:circle on:submit={save}> - <div class="flex gap-4"> - <DeleteButton on:click={deleteCircle} /> - <div class="grow" /> - <SubmitButton active={pending} /> - </div> +<Dialog title="Edit Circle" {...modal}> + <CircleForm {initial} {submit}> + <DeleteButton onclick={deleteCircle} /> </CircleForm> </Dialog> diff --git a/frontend/src/lib/dialogs/EditNamespace.svelte b/frontend/src/lib/dialogs/EditNamespace.svelte index f398b21..b104f83 100644 --- a/frontend/src/lib/dialogs/EditNamespace.svelte +++ b/frontend/src/lib/dialogs/EditNamespace.svelte @@ -1,46 +1,37 @@ <script lang="ts"> - import { deleteNamespaces, updateNamespaces, type NamespaceInput } from '$gql/Mutations'; - import { itemEquals } from '$gql/Utils'; - import { type Namespace } from '$gql/graphql'; + import { deleteNamespaces, updateNamespaces } from '$gql/Mutations'; + import { omitIdentifiers } from '$gql/Utils'; + import type { Namespace, UpdateNamespaceInput } from '$gql/graphql'; import { toastFinally } from '$lib/Toasts'; import { confirmDeletion } from '$lib/Utils'; import DeleteButton from '$lib/components/DeleteButton.svelte'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import NamespaceForm from '$lib/forms/NamespaceForm.svelte'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + interface Props extends ModalProps { + namespace: Namespace; + } - export let namespace: Namespace; - const original = structuredClone(namespace); - $: pending = !itemEquals(original, namespace); + let { namespace, ...modal }: Props = $props(); + const initial = omitIdentifiers(namespace); - function save(event: CustomEvent<NamespaceInput>) { - updateNamespaces(client, { ids: namespace.id, input: event.detail }) - .then(closeModal) - .catch(toastFinally); + function submit(input: UpdateNamespaceInput) { + updateNamespaces(client, { ids: namespace.id, input }).then(modal.close).catch(toastFinally); } function deleteNamespace() { confirmDeletion('Namespace', namespace.name, () => { - deleteNamespaces(client, { ids: namespace.id }).then(closeModal).catch(toastFinally); + deleteNamespaces(client, { ids: namespace.id }).then(modal.close).catch(toastFinally); }); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Edit Namespace</h2> - </svelte:fragment> - <NamespaceForm bind:namespace on:submit={save}> - <div class="flex gap-4"> - <DeleteButton on:click={deleteNamespace} /> - <div class="grow" /> - <SubmitButton active={pending} /> - </div> +<Dialog title="Edit Namespace" {...modal}> + <NamespaceForm {initial} {submit}> + <DeleteButton onclick={deleteNamespace} /> </NamespaceForm> </Dialog> diff --git a/frontend/src/lib/dialogs/EditTag.svelte b/frontend/src/lib/dialogs/EditTag.svelte index d2d0013..555d6d1 100644 --- a/frontend/src/lib/dialogs/EditTag.svelte +++ b/frontend/src/lib/dialogs/EditTag.svelte @@ -1,44 +1,37 @@ <script lang="ts"> - import { deleteTags, updateTags, type TagInput } from '$gql/Mutations'; - import { tagEquals } from '$gql/Utils'; - import { type FullTag } from '$gql/graphql'; + import { deleteTags, updateTags } from '$gql/Mutations'; + import { omitIdentifiers } from '$gql/Utils'; + import { type FullTag, type UpdateTagInput } from '$gql/graphql'; import { toastFinally } from '$lib/Toasts'; import { confirmDeletion } from '$lib/Utils'; import DeleteButton from '$lib/components/DeleteButton.svelte'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import TagForm from '$lib/forms/TagForm.svelte'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import { type ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + interface Props extends ModalProps { + tag: FullTag; + } - export let tag: FullTag; - const original = structuredClone(tag); - $: pending = !tagEquals(original, tag); + let { tag, ...modal }: Props = $props(); + const initial = omitIdentifiers(tag); - function save(event: CustomEvent<TagInput>) { - updateTags(client, { ids: tag.id, input: event.detail }).then(closeModal).catch(toastFinally); + function submit(input: UpdateTagInput) { + updateTags(client, { ids: tag.id, input }).then(modal.close).catch(toastFinally); } function deleteTag() { confirmDeletion('Tag', tag.name, () => { - deleteTags(client, { ids: tag.id }).then(closeModal).catch(toastFinally); + deleteTags(client, { ids: tag.id }).then(modal.close).catch(toastFinally); }); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Edit Tag</h2> - </svelte:fragment> - <TagForm bind:tag on:submit={save}> - <div class="flex gap-4"> - <DeleteButton on:click={deleteTag} /> - <div class="grow" /> - <SubmitButton active={pending} /> - </div> +<Dialog title="Edit Tag" {...modal}> + <TagForm {initial} {submit}> + <DeleteButton onclick={deleteTag} /> </TagForm> </Dialog> diff --git a/frontend/src/lib/dialogs/EditWorld.svelte b/frontend/src/lib/dialogs/EditWorld.svelte index 82afe6a..869dc21 100644 --- a/frontend/src/lib/dialogs/EditWorld.svelte +++ b/frontend/src/lib/dialogs/EditWorld.svelte @@ -1,46 +1,37 @@ <script lang="ts"> - import { type World } from '$gql/graphql'; - import { deleteWorlds, updateWorlds, type WorldInput } from '$gql/Mutations'; - import { itemEquals } from '$gql/Utils'; + import { deleteWorlds, updateWorlds } from '$gql/Mutations'; + import { omitIdentifiers } from '$gql/Utils'; + import type { UpdateWorldInput, World } from '$gql/graphql'; + import { toastFinally } from '$lib/Toasts'; + import { confirmDeletion } from '$lib/Utils'; import DeleteButton from '$lib/components/DeleteButton.svelte'; import Dialog from '$lib/components/Dialog.svelte'; - import SubmitButton from '$lib/components/SubmitButton.svelte'; import WorldForm from '$lib/forms/WorldForm.svelte'; - import { toastFinally } from '$lib/Toasts'; - import { confirmDeletion } from '$lib/Utils'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; const client = getContextClient(); - export let isOpen: boolean; + interface Props extends ModalProps { + world: World; + } - export let world: World; - const original = structuredClone(world); - $: pending = !itemEquals(original, world); + let { world, ...modal }: Props = $props(); + const initial = omitIdentifiers(world); - function save(event: CustomEvent<WorldInput>) { - updateWorlds(client, { ids: world.id, input: event.detail }) - .then(closeModal) - .catch(toastFinally); + function submit(input: UpdateWorldInput) { + updateWorlds(client, { ids: world.id, input }).then(modal.close).catch(toastFinally); } function deleteWorld() { confirmDeletion('World', world.name, () => { - deleteWorlds(client, { ids: world.id }).then(closeModal).catch(toastFinally); + deleteWorlds(client, { ids: world.id }).then(modal.close).catch(toastFinally); }); } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Edit World</h2> - </svelte:fragment> - <WorldForm bind:world on:submit={save}> - <div class="flex gap-4"> - <DeleteButton on:click={deleteWorld} /> - <div class="grow" /> - <SubmitButton active={pending} /> - </div> +<Dialog title="Edit World" {...modal}> + <WorldForm {initial} {submit}> + <DeleteButton onclick={deleteWorld} /> </WorldForm> </Dialog> diff --git a/frontend/src/lib/dialogs/UpdateComics.svelte b/frontend/src/lib/dialogs/UpdateComics.svelte index 8de9622..483e379 100644 --- a/frontend/src/lib/dialogs/UpdateComics.svelte +++ b/frontend/src/lib/dialogs/UpdateComics.svelte @@ -3,94 +3,109 @@ import { artistList, characterList, circleList, comicTagList, worldList } from '$gql/Queries'; import { categories, censorships, directions, languages, layouts, ratings } from '$lib/Enums'; import { toastFinally } from '$lib/Toasts'; - import { UpdateComicsControls } from '$lib/Update'; + import { UpdateComicsControls } from '$lib/Update.svelte'; import Dialog from '$lib/components/Dialog.svelte'; - import Labelled from '$lib/components/Labelled.svelte'; import LabelledBlock from '$lib/components/LabelledBlock.svelte'; import Select from '$lib/components/Select.svelte'; import SubmitButton from '$lib/components/SubmitButton.svelte'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import type { ModalProps } from 'svelte-modals'; import UpdateModeSelector from './components/UpdateModeSelector.svelte'; const client = getContextClient(); - export let isOpen: boolean; - export let ids: number[]; + interface Props extends ModalProps { + ids: number[]; + } - $: tagsQuery = comicTagList(client); - $: artistsQuery = artistList(client); - $: charactersQuery = characterList(client); - $: circlesQuery = circleList(client); - $: worldsQuery = worldList(client); + let { ids, ...modal }: Props = $props(); - $: tags = $tagsQuery.data?.comicTags.edges; - $: artists = $artistsQuery.data?.artists.edges; - $: characters = $charactersQuery.data?.characters.edges; - $: circles = $circlesQuery.data?.circles.edges; - $: worlds = $worldsQuery.data?.worlds.edges; + let tagsQuery = $derived(comicTagList(client)); + let artistsQuery = $derived(artistList(client)); + let charactersQuery = $derived(characterList(client)); + let circlesQuery = $derived(circleList(client)); + let worldsQuery = $derived(worldList(client)); + + let tags = $derived($tagsQuery.data?.comicTags.edges); + let artists = $derived($artistsQuery.data?.artists.edges); + let characters = $derived($charactersQuery.data?.characters.edges); + let circles = $derived($circlesQuery.data?.circles.edges); + let worlds = $derived($worldsQuery.data?.worlds.edges); const controls = new UpdateComicsControls(); - const update = () => { - updateComics(client, { - ids: ids, - input: controls.toInput() - }) - .then(closeModal) - .catch(toastFinally); - }; + function update(event: SubmitEvent) { + event.preventDefault(); + + updateComics(client, { ids, input: controls.input() }).then(modal.close).catch(toastFinally); + } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Edit Comics</h2> - </svelte:fragment> - <form on:submit|preventDefault={update}> +<Dialog title="Edit Comics" {...modal}> + <form onsubmit={update}> <div class="grid-labels"> - <Labelled label="Category" let:id> - <Select clearable {id} options={categories} bind:value={controls.category.value} /> - </Labelled> - <Labelled label="Rating" let:id> - <Select clearable {id} options={ratings} bind:value={controls.rating.value} /> - </Labelled> - <Labelled label="Censorship" let:id> - <Select clearable {id} options={censorships} bind:value={controls.censorship.value} /> - </Labelled> - <Labelled label="Language" let:id> - <Select clearable {id} options={languages} bind:value={controls.language.value} /> - </Labelled> - <Labelled label="Direction" let:id> - <Select clearable {id} options={directions} bind:value={controls.direction.value} /> - </Labelled> - <Labelled label="Layout" let:id> - <Select clearable {id} options={layouts} bind:value={controls.layout.value} /> - </Labelled> + <label class="self-center" for="category">Category</label> + <Select clearable id="category" options={categories} bind:value={controls.category.value} /> + + <label class="self-center" for="rating">Rating</label> + <Select clearable id="rating" options={ratings} bind:value={controls.rating.value} /> + + <label class="self-center" for="censor">Censorship</label> + <Select clearable id="censor" options={censorships} bind:value={controls.censorship.value} /> + + <label class="self-center" for="language">Language</label> + <Select clearable id="language" options={languages} bind:value={controls.language.value} /> + + <label class="self-center" for="direction">Direction</label> + <Select clearable id="direction" options={directions} bind:value={controls.direction.value} /> + + <label class="self-center" for="layout">Layout</label> + <Select clearable id="layout" options={layouts} bind:value={controls.layout.value} /> </div> - <LabelledBlock label="Artists" let:id> - <Select multi {id} options={artists} bind:value={controls.artists.ids} /> - <UpdateModeSelector bind:mode={controls.artists.options.mode} slot="controls" /> + <LabelledBlock label="Artists"> + {#snippet children({ id })} + <Select multi {id} options={artists} bind:value={controls.artists.ids} /> + {/snippet} + {#snippet side()} + <UpdateModeSelector bind:mode={controls.artists.options.mode} /> + {/snippet} </LabelledBlock> - <LabelledBlock label="Circles" let:id> - <Select multi {id} options={circles} bind:value={controls.circles.ids} /> - <UpdateModeSelector bind:mode={controls.circles.options.mode} slot="controls" /> + <LabelledBlock label="Circles"> + {#snippet children({ id })} + <Select multi {id} options={circles} bind:value={controls.circles.ids} /> + {/snippet} + {#snippet side()} + <UpdateModeSelector bind:mode={controls.circles.options.mode} /> + {/snippet} </LabelledBlock> - <LabelledBlock label="Characters" let:id> - <Select multi {id} options={characters} bind:value={controls.characters.ids} /> - <UpdateModeSelector bind:mode={controls.characters.options.mode} slot="controls" /> + <LabelledBlock label="Characters"> + {#snippet children({ id })} + <Select multi {id} options={characters} bind:value={controls.characters.ids} /> + {/snippet} + {#snippet side()} + <UpdateModeSelector bind:mode={controls.characters.options.mode} /> + {/snippet} </LabelledBlock> - <LabelledBlock label="Worlds" let:id> - <Select multi {id} options={worlds} bind:value={controls.worlds.ids} /> - <UpdateModeSelector bind:mode={controls.worlds.options.mode} slot="controls" /> + <LabelledBlock label="Worlds"> + {#snippet children({ id })} + <Select multi {id} options={worlds} bind:value={controls.worlds.ids} /> + {/snippet} + {#snippet side()} + <UpdateModeSelector bind:mode={controls.worlds.options.mode} /> + {/snippet} </LabelledBlock> - <LabelledBlock label="Tags" let:id> - <Select multi {id} options={tags} bind:value={controls.tags.ids} /> - <UpdateModeSelector bind:mode={controls.tags.options.mode} slot="controls" /> + <LabelledBlock label="Tags"> + {#snippet children({ id })} + <Select multi {id} options={tags} bind:value={controls.tags.ids} /> + {/snippet} + {#snippet side()} + <UpdateModeSelector bind:mode={controls.tags.options.mode} /> + {/snippet} </LabelledBlock> <div class="flex justify-end gap-4"> - <SubmitButton active={controls.hasInput()} /> + <SubmitButton pending={controls.pending()} /> </div> </form> </Dialog> diff --git a/frontend/src/lib/dialogs/UpdateTags.svelte b/frontend/src/lib/dialogs/UpdateTags.svelte index f753c7f..840e92e 100644 --- a/frontend/src/lib/dialogs/UpdateTags.svelte +++ b/frontend/src/lib/dialogs/UpdateTags.svelte @@ -2,44 +2,49 @@ import { updateTags } from '$gql/Mutations'; import { namespaceList } from '$gql/Queries'; import { toastFinally } from '$lib/Toasts'; - import { UpdateTagsControls } from '$lib/Update'; + import { UpdateTagsControls } from '$lib/Update.svelte'; import Dialog from '$lib/components/Dialog.svelte'; import LabelledBlock from '$lib/components/LabelledBlock.svelte'; import Select from '$lib/components/Select.svelte'; import SubmitButton from '$lib/components/SubmitButton.svelte'; import { getContextClient } from '@urql/svelte'; - import { closeModal } from 'svelte-modals'; + import { modals, type ModalProps } from 'svelte-modals'; import UpdateModeSelector from './components/UpdateModeSelector.svelte'; const client = getContextClient(); - $: namespaceQuery = namespaceList(client); - $: namespaces = $namespaceQuery.data?.namespaces.edges; + let namespaceQuery = $derived(namespaceList(client)); + let namespaces = $derived($namespaceQuery.data?.namespaces.edges); - export let isOpen: boolean; - export let ids: number[]; + interface Props extends ModalProps { + ids: number[]; + } - const controls = new UpdateTagsControls(); + let { ids, ...modal }: Props = $props(); + let controls = new UpdateTagsControls(); - const update = () => { - updateTags(client, { ids: ids, input: controls.toInput() }) - .then(closeModal) + function update(event: SubmitEvent) { + event.preventDefault(); + + updateTags(client, { ids, input: controls.input() }) + .then(() => modals.close()) .catch(toastFinally); - }; + } </script> -<Dialog {isOpen}> - <svelte:fragment slot="header"> - <h2>Edit Tags</h2> - </svelte:fragment> - <form on:submit|preventDefault={update}> - <LabelledBlock label="Namespaces" let:id> - <Select multi {id} options={namespaces} bind:value={controls.namespaces.ids} /> - <UpdateModeSelector bind:mode={controls.namespaces.options.mode} slot="controls" /> +<Dialog title="Edit Tags" {...modal}> + <form onsubmit={update}> + <LabelledBlock label="Namespaces"> + {#snippet children({ id })} + <Select multi {id} options={namespaces} bind:value={controls.namespaces.ids} /> + {/snippet} + {#snippet side()} + <UpdateModeSelector bind:mode={controls.namespaces.options.mode} /> + {/snippet} </LabelledBlock> <div class="flex justify-end gap-4"> - <SubmitButton active={controls.hasInput()} /> + <SubmitButton pending={controls.pending()} /> </div> </form> </Dialog> diff --git a/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte b/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte index e4b4479..876657e 100644 --- a/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte +++ b/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte @@ -2,21 +2,17 @@ import { UpdateMode } from '$gql/graphql'; import { UpdateModeLabel } from '$lib/Enums'; - export let mode: UpdateMode; - - function select(e: string) { - mode = e as UpdateMode; - } + let { mode = $bindable() }: { mode: UpdateMode } = $props(); </script> <div class="flex gap-1 pb-1 text-xs"> - {#each Object.entries(UpdateModeLabel) as [e, label]} + {#each Object.entries(UpdateModeLabel) as [e, label] (e)} <button type="button" class:active={mode === e} class:dangerous={mode !== UpdateMode.Add} - class="btn btn-xs hover:bg-slate-700 [&.active.dangerous]:bg-rose-800 [&.active]:bg-indigo-700" - on:click={() => select(e)} + class="btn-xs hover:bg-slate-700 [&.active]:bg-indigo-700 [&.active.dangerous]:bg-rose-800" + onclick={() => (mode = e as UpdateMode)} > {label} </button> diff --git a/frontend/src/lib/filter/ComicFilterForm.svelte b/frontend/src/lib/filter/ComicFilterForm.svelte index 13b5320..b8be75b 100644 --- a/frontend/src/lib/filter/ComicFilterForm.svelte +++ b/frontend/src/lib/filter/ComicFilterForm.svelte @@ -1,48 +1,73 @@ <script lang="ts"> - import { page } from '$app/stores'; import { artistList, characterList, circleList, comicTagList, worldList } from '$gql/Queries'; - import { ComicFilterContext, getFilterContext } from '$lib/Filter'; + import { categories, censorships, languages, ratings } from '$lib/Enums'; + import { ComicFilterContext } from '$lib/Filter.svelte'; + import { accelerator } from '$lib/Shortcuts'; import { getContextClient } from '@urql/svelte'; - import ComicFilterGroup from './components/ComicFilterGroup.svelte'; + import Filter from './components/Filter.svelte'; import FilterForm from './components/FilterForm.svelte'; const client = getContextClient(); - $: tagsQuery = comicTagList(client, { forFilter: true }); - $: artistsQuery = artistList(client); - $: charactersQuery = characterList(client); - $: circlesQuery = circleList(client); - $: worldsQuery = worldList(client); + let { filter }: { filter: ComicFilterContext } = $props(); - $: tags = $tagsQuery.data?.comicTags.edges; - $: artists = $artistsQuery.data?.artists.edges; - $: characters = $charactersQuery.data?.characters.edges; - $: circles = $circlesQuery.data?.circles.edges; - $: worlds = $worldsQuery.data?.worlds.edges; + let tagsQuery = $derived(comicTagList(client, { forFilter: true })); + let artistsQuery = $derived(artistList(client)); + let charactersQuery = $derived(characterList(client)); + let circlesQuery = $derived(circleList(client)); + let worldsQuery = $derived(worldList(client)); - const filter = getFilterContext<ComicFilterContext>(); - const apply = () => $filter.apply($page.url.searchParams); + let tags = $derived($tagsQuery.data?.comicTags.edges); + let artists = $derived($artistsQuery.data?.artists.edges); + let characters = $derived($charactersQuery.data?.characters.edges); + let circles = $derived($circlesQuery.data?.circles.edges); + let worlds = $derived($worldsQuery.data?.worlds.edges); + + let inc = $derived(filter.include); + let exc = $derived(filter.exclude); </script> -<FilterForm type="grid" on:submit={apply}> - <ComicFilterGroup - slot="include" - type="include" - bind:controls={$filter.include.controls} - {tags} - {artists} - {characters} - {circles} - {worlds} - /> - <ComicFilterGroup - slot="exclude" - type="exclude" - bind:controls={$filter.exclude.controls} - {tags} - {artists} - {characters} - {circles} - {worlds} - /> +<FilterForm type="grid" apply={filter.apply}> + {#snippet include(type)} + <Filter {type} wide title="Tags" options={tags} filter={inc.tags} accel="it" /> + <Filter {type} title="Artists" options={artists} filter={inc.artists} accel="ia" /> + <Filter {type} title="Circles" options={circles} filter={inc.circles} accel="ii" /> + <Filter {type} title="Characters" options={characters} filter={inc.characters} accel="ih" /> + <Filter {type} title="Worlds" options={worlds} filter={inc.worlds} accel="iw" /> + <Filter {type} title="Categories" options={categories} filter={inc.categories} accel="ig" /> + <Filter {type} title="Ratings" options={ratings} filter={inc.ratings} accel="ir" /> + <Filter {type} title="Censorship" options={censorships} filter={inc.censorships} accel="is" /> + <Filter {type} title="Languages" options={languages} filter={inc.languages} accel="il" /> + <div class="flex flex-col"> + <label for="include-url">URL</label> + <input + use:accelerator={'iu'} + id="include-url" + class="h-full" + placeholder="Contains..." + bind:value={inc.url.contains} + /> + </div> + {/snippet} + {#snippet exclude(type)} + <Filter {type} wide title="Tags" options={tags} filter={exc.tags} accel="et" /> + <Filter {type} title="Artists" options={artists} filter={exc.artists} accel="ea" /> + <Filter {type} title="Circles" options={circles} filter={exc.circles} accel="ei" /> + <Filter {type} title="Characters" options={characters} filter={exc.characters} accel="eh" /> + <Filter {type} title="Worlds" options={worlds} filter={exc.worlds} accel="ew" /> + <Filter {type} title="Categories" options={categories} filter={exc.categories} accel="eg" /> + <Filter {type} title="Ratings" options={ratings} filter={exc.ratings} accel="er" /> + <Filter {type} title="Censorship" options={censorships} filter={exc.censorships} accel="es" /> + <Filter {type} title="Languages" options={languages} filter={exc.languages} accel="el" /> + <div class="flex flex-col"> + <label for="exclude-url">URL</label> + <input + use:accelerator={'eu'} + id="exclude-url" + class="h-full border border-red-900 outline-none focus:border-red-500" + placeholder="Does not contain..." + bind:value={exc.url.contains} + /> + </div> + {/snippet} </FilterForm> diff --git a/frontend/src/lib/filter/TagFilterForm.svelte b/frontend/src/lib/filter/TagFilterForm.svelte index be5996e..c514163 100644 --- a/frontend/src/lib/filter/TagFilterForm.svelte +++ b/frontend/src/lib/filter/TagFilterForm.svelte @@ -1,31 +1,26 @@ <script lang="ts"> - import { page } from '$app/stores'; import { namespaceList } from '$gql/Queries'; - import { TagFilterContext, getFilterContext } from '$lib/Filter'; + import { TagFilterContext } from '$lib/Filter.svelte'; import { getContextClient } from '@urql/svelte'; + import Filter from './components/Filter.svelte'; import FilterForm from './components/FilterForm.svelte'; - import TagFilterGroup from './components/TagFilterGroup.svelte'; const client = getContextClient(); - $: namespaceQuery = namespaceList(client); - $: namespaces = $namespaceQuery.data?.namespaces.edges; + let { filter }: { filter: TagFilterContext } = $props(); - const filter = getFilterContext<TagFilterContext>(); - const apply = () => $filter.apply($page.url.searchParams); + let namespaceQuery = $derived(namespaceList(client)); + let namespaces = $derived($namespaceQuery.data?.namespaces.edges); + + let inc = $derived(filter.include); + let exc = $derived(filter.exclude); </script> -<FilterForm on:submit={apply}> - <TagFilterGroup - slot="include" - type="include" - bind:controls={$filter.include.controls} - {namespaces} - /> - <TagFilterGroup - slot="exclude" - type="exclude" - bind:controls={$filter.exclude.controls} - {namespaces} - /> +<FilterForm apply={filter.apply}> + {#snippet include(type)} + <Filter {type} title="Namespaces" options={namespaces} filter={inc.namespaces} accel="in" /> + {/snippet} + {#snippet exclude(type)} + <Filter {type} title="Namespaces" options={namespaces} filter={exc.namespaces} accel="en" /> + {/snippet} </FilterForm> diff --git a/frontend/src/lib/filter/components/ComicFilterGroup.svelte b/frontend/src/lib/filter/components/ComicFilterGroup.svelte deleted file mode 100644 index d302de4..0000000 --- a/frontend/src/lib/filter/components/ComicFilterGroup.svelte +++ /dev/null @@ -1,27 +0,0 @@ -<script lang="ts"> - import { categories, censorships, languages, ratings } from '$lib/Enums'; - import { ComicFilterControls } from '$lib/Filter'; - import type { ListItem } from '$lib/Utils'; - import { setContext } from 'svelte'; - import Filter from './Filter.svelte'; - - export let tags: ListItem[] | undefined; - export let artists: ListItem[] | undefined; - export let circles: ListItem[] | undefined; - export let characters: ListItem[] | undefined; - export let worlds: ListItem[] | undefined; - export let controls: ComicFilterControls; - export let type: 'include' | 'exclude'; - - setContext('filter-type', type); -</script> - -<Filter title="Tags" options={tags} bind:filter={controls.tags} --grid-column="span 2" /> -<Filter title="Artists" options={artists} bind:filter={controls.artists} /> -<Filter title="Circles" options={circles} bind:filter={controls.circles} /> -<Filter title="Characters" options={characters} bind:filter={controls.characters} /> -<Filter title="Worlds" options={worlds} bind:filter={controls.worlds} /> -<Filter title="Categories" options={categories} bind:filter={controls.categories} /> -<Filter title="Ratings" options={ratings} bind:filter={controls.ratings} /> -<Filter title="Censorship" options={censorships} bind:filter={controls.censorships} /> -<Filter title="Languages" options={languages} bind:filter={controls.languages} /> diff --git a/frontend/src/lib/filter/components/Filter.svelte b/frontend/src/lib/filter/components/Filter.svelte index ead5c4d..cf7252b 100644 --- a/frontend/src/lib/filter/components/Filter.svelte +++ b/frontend/src/lib/filter/components/Filter.svelte @@ -1,48 +1,54 @@ <script lang="ts"> - import { Association, Enum } from '$lib/Filter'; + import { Association, Enum, type FilterType } from '$lib/Filter.svelte'; + import { accelerator, type Shortcut } from '$lib/Shortcuts'; import type { ListItem } from '$lib/Utils'; import Select from '$lib/components/Select.svelte'; - import { getContext } from 'svelte'; - export let title: string; - const context: 'include' | 'exclude' = getContext('filter-type'); - $: exclude = context === 'exclude'; + interface Props { + title: string; + type: FilterType; + options: ListItem[] | undefined; + filter: Association<string> | Enum<string>; + accel: Shortcut; + wide?: boolean; + } - const id = `${context}-${title.toLowerCase()}`; + let { title, type, options, filter, accel, wide = false }: Props = $props(); + let exclude = $derived(type === 'exclude'); + let placeholder = $derived(exclude ? 'Exclude...' : 'Include...'); - export let options: ListItem[] | undefined; - export let filter: Association<string> | Enum<string>; + const id = `${type}-${title.toLowerCase()}`; </script> -<div class:exclude class="filter-container"> +<div class:exclude class:wide class="[&.wide]:col-span-2"> <div class="flex gap-2"> - <label for={id}>{title}</label> + <label use:accelerator={accel} for={id}>{title}</label> <div class="ml-auto flex items-center gap-1 self-center text-xs"> {#if filter instanceof Association} <button type="button" - title="matches all" + title="match all" class:active={filter.mode === 'all'} - class="btn btn-xs" - on:click={() => (filter.mode = 'all')} + class="btn-xs hover:bg-slate-700 [&.active]:bg-indigo-800" + onclick={() => (filter.mode = 'all')} > ∀ </button> <button type="button" - title="matches any of" + title="match any" class:active={filter.mode === 'any'} - class="btn btn-xs" - on:click={() => (filter.mode = 'any')} + class="btn-xs hover:bg-slate-700 [&.active]:bg-indigo-800" + onclick={() => (filter.mode = 'any')} > ∃ </button> <button type="button" - title="matches exactly" + title="match exactly" class:active={filter.mode === 'exact'} - class="btn btn-xs" - on:click={() => (filter.mode = 'exact')} + class="btn-xs hover:bg-slate-700 [&.active]:bg-indigo-800" + onclick={() => (filter.mode = 'exact')} > = </button> @@ -50,28 +56,14 @@ {/if} <button type="button" - title="empty" + title="match empty" class:active={filter.empty} - class="btn btn-xs" - on:click={() => (filter.empty = !filter.empty)} + class="btn-xs hover:bg-slate-700 [&.active]:bg-indigo-800" + onclick={() => (filter.empty = !filter.empty)} > ∅ </button> </div> </div> - <Select multi clearable {options} {id} bind:value={filter.values} /> + <Select multi clearable {placeholder} {options} {id} bind:value={filter.values} /> </div> - -<style lang="postcss"> - button:hover { - @apply bg-slate-700; - } - - button.active { - @apply bg-indigo-800; - } - - .filter-container { - grid-column: var(--grid-column); - } -</style> diff --git a/frontend/src/lib/filter/components/FilterForm.svelte b/frontend/src/lib/filter/components/FilterForm.svelte index 6fc4c90..717a56d 100644 --- a/frontend/src/lib/filter/components/FilterForm.svelte +++ b/frontend/src/lib/filter/components/FilterForm.svelte @@ -1,41 +1,41 @@ <script lang="ts"> - import Expander from '$lib/components/Expander.svelte'; - import { getFilterContext } from '$lib/Filter'; + import { page } from '$app/state'; + import type { FilterType } from '$lib/Filter.svelte'; + import type { Snippet } from 'svelte'; - const filter = getFilterContext(); - export let type: 'grid' | 'row' = 'row'; + interface Props { + type?: 'grid' | 'row'; + include?: Snippet<[FilterType]>; + exclude?: Snippet<[FilterType]>; + apply: (params: URLSearchParams) => void; + } - let exclude = false; + let { type = 'row', include, exclude, apply }: Props = $props(); - $: if ($filter.exclude.size > 0) { - exclude = true; + function onsubmit(event: SubmitEvent) { + event.preventDefault(); + apply(page.url.searchParams); } </script> -<form on:submit|preventDefault class="gap-0"> +<form {onsubmit} class="gap-4"> {#if type === 'grid'} <div class="flex flex-col gap-4 px-2 md:grid md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6"> - <slot name="include" /> - </div> - <div class="my-2 flex justify-start"> - <Expander title="Exclude" bind:expanded={exclude} /> + {@render include?.('include')} </div> - {#if exclude} - <div - class="flex flex-col gap-4 bg-rose-950/50 p-2 md:grid md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6" - > - <slot name="exclude" /> - </div> - {/if} - {:else} + <div - class="flex flex-wrap justify-center gap-2 [&>*]:basis-full xl:[&>*]:basis-1/3 2xl:[&>*]:basis-1/5" + class="flex flex-col gap-4 bg-rose-950/50 p-2 md:grid md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6" > + {@render exclude?.('exclude')} + </div> + {:else} + <div class="flex flex-wrap justify-center gap-2 *:basis-full xl:*:basis-1/3 2xl:*:basis-1/5"> <div class="p-2"> - <slot name="include" /> + {@render include?.('include')} </div> <div class="bg-rose-950/50 p-2"> - <slot name="exclude" /> + {@render exclude?.('exclude')} </div> </div> {/if} diff --git a/frontend/src/lib/filter/components/TagFilterGroup.svelte b/frontend/src/lib/filter/components/TagFilterGroup.svelte deleted file mode 100644 index 83b6997..0000000 --- a/frontend/src/lib/filter/components/TagFilterGroup.svelte +++ /dev/null @@ -1,14 +0,0 @@ -<script lang="ts"> - import { TagFilterControls } from '$lib/Filter'; - import type { ListItem } from '$lib/Utils'; - import { setContext } from 'svelte'; - import Filter from './Filter.svelte'; - - export let namespaces: ListItem[] | undefined; - export let controls: TagFilterControls; - export let type: 'include' | 'exclude'; - - setContext('filter-type', type); -</script> - -<Filter title="Namespaces" options={namespaces} bind:filter={controls.namespaces} /> diff --git a/frontend/src/lib/forms/ArtistForm.svelte b/frontend/src/lib/forms/ArtistForm.svelte index 7df5e8b..663c3ae 100644 --- a/frontend/src/lib/forms/ArtistForm.svelte +++ b/frontend/src/lib/forms/ArtistForm.svelte @@ -1,25 +1,29 @@ <script lang="ts"> - import { type ArtistInput } from '$gql/Mutations'; - import { type OmitIdentifiers } from '$gql/Utils'; - import { type Artist } from '$gql/graphql'; - import Labelled from '$lib/components/Labelled.svelte'; - import { createEventDispatcher } from 'svelte'; + import type { AddArtistInput, Artist } from '$gql/graphql'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import { itemPending, type FormProps } from '$lib/Form'; - const dispatch = createEventDispatcher<{ submit: ArtistInput }>(); + let { initial, submit, children }: FormProps<Artist, AddArtistInput> = $props(); - export let artist: OmitIdentifiers<Artist>; + let input = $state(initial); + let pending = $derived(input.name.length > 0 && itemPending(initial, input)); - function submit() { - dispatch('submit', { name: artist.name }); + function onsubmit(event: SubmitEvent) { + event.preventDefault(); + + submit({ ...input }); } </script> -<form on:submit|preventDefault={submit}> +<form {onsubmit}> <div class="grid-labels"> - <Labelled label="Name" let:id> - <!-- svelte-ignore a11y-autofocus --> - <input autofocus required {id} bind:value={artist.name} /> - </Labelled> + <label class="self-center" for="name">Name</label> + <!-- svelte-ignore a11y_autofocus --> + <input autofocus required id="name" bind:value={input.name} /> + </div> + <div class="flex gap-4"> + {@render children?.()} + <div class="grow"></div> + <SubmitButton {pending} /> </div> - <slot /> </form> diff --git a/frontend/src/lib/forms/CharacterForm.svelte b/frontend/src/lib/forms/CharacterForm.svelte index 4cec37c..23b3ef7 100644 --- a/frontend/src/lib/forms/CharacterForm.svelte +++ b/frontend/src/lib/forms/CharacterForm.svelte @@ -1,25 +1,29 @@ <script lang="ts"> - import { type CharacterInput } from '$gql/Mutations'; - import { type OmitIdentifiers } from '$gql/Utils'; - import { type Character } from '$gql/graphql'; - import Labelled from '$lib/components/Labelled.svelte'; - import { createEventDispatcher } from 'svelte'; + import type { AddCharacterInput, Character } from '$gql/graphql'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import { itemPending, type FormProps } from '$lib/Form'; - const dispatch = createEventDispatcher<{ submit: CharacterInput }>(); + let { initial, submit, children }: FormProps<Character, AddCharacterInput> = $props(); - export let character: OmitIdentifiers<Character>; + let input = $state(initial); + let pending = $derived(input.name.length > 0 && itemPending(initial, input)); - function submit() { - dispatch('submit', { name: character.name }); + function onsubmit(event: SubmitEvent) { + event.preventDefault(); + + submit({ ...input }); } </script> -<form on:submit|preventDefault={submit}> +<form {onsubmit}> <div class="grid-labels"> - <Labelled label="Name" let:id> - <!-- svelte-ignore a11y-autofocus --> - <input autofocus required {id} bind:value={character.name} /> - </Labelled> + <label class="self-center" for="name">Name</label> + <!-- svelte-ignore a11y_autofocus --> + <input autofocus required id="name" bind:value={input.name} /> + </div> + <div class="flex gap-4"> + {@render children?.()} + <div class="grow"></div> + <SubmitButton {pending} /> </div> - <slot /> </form> diff --git a/frontend/src/lib/forms/CircleForm.svelte b/frontend/src/lib/forms/CircleForm.svelte index b71256c..ba6013a 100644 --- a/frontend/src/lib/forms/CircleForm.svelte +++ b/frontend/src/lib/forms/CircleForm.svelte @@ -1,25 +1,29 @@ <script lang="ts"> - import { type CircleInput } from '$gql/Mutations'; - import { type OmitIdentifiers } from '$gql/Utils'; - import { type Circle } from '$gql/graphql'; - import Labelled from '$lib/components/Labelled.svelte'; - import { createEventDispatcher } from 'svelte'; + import type { AddCircleInput, Circle } from '$gql/graphql'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import { itemPending, type FormProps } from '$lib/Form'; - const dispatch = createEventDispatcher<{ submit: CircleInput }>(); + let { initial, submit, children }: FormProps<Circle, AddCircleInput> = $props(); - export let circle: OmitIdentifiers<Circle>; + let input = $state(initial); + let pending = $derived(input.name.length > 0 && itemPending(initial, input)); - function submit() { - dispatch('submit', { name: circle.name }); + function onsubmit(event: SubmitEvent) { + event.preventDefault(); + + submit({ ...input }); } </script> -<form on:submit|preventDefault={submit}> +<form {onsubmit}> <div class="grid-labels"> - <Labelled label="Name" let:id> - <!-- svelte-ignore a11y-autofocus --> - <input required autofocus {id} bind:value={circle.name} /> - </Labelled> + <label class="self-center" for="name">Name</label> + <!-- svelte-ignore a11y_autofocus --> + <input autofocus required id="name" bind:value={input.name} /> + </div> + <div class="flex gap-4"> + {@render children?.()} + <div class="grow"></div> + <SubmitButton {pending} /> </div> - <slot /> </form> diff --git a/frontend/src/lib/forms/ComicForm.svelte b/frontend/src/lib/forms/ComicForm.svelte index 74051c8..adc6a34 100644 --- a/frontend/src/lib/forms/ComicForm.svelte +++ b/frontend/src/lib/forms/ComicForm.svelte @@ -3,98 +3,113 @@ import { type OmitIdentifiers } from '$gql/Utils'; import type { FullComicFragment, UpdateComicInput } from '$gql/graphql'; import { categories, censorships, directions, languages, layouts, ratings } from '$lib/Enums'; - import Labelled from '$lib/components/Labelled.svelte'; import LabelledBlock from '$lib/components/LabelledBlock.svelte'; import Select from '$lib/components/Select.svelte'; import { getContextClient } from '@urql/svelte'; - import { createEventDispatcher } from 'svelte'; + import { type Snippet } from 'svelte'; const client = getContextClient(); - const dispatch = createEventDispatcher<{ submit: UpdateComicInput }>(); - - export let comic: OmitIdentifiers<FullComicFragment>; - - $: tagsQuery = comicTagList(client); - $: artistsQuery = artistList(client); - $: charactersQuery = characterList(client); - $: circlesQuery = circleList(client); - $: worldsQuery = worldList(client); - - $: tags = $tagsQuery.data?.comicTags.edges; - $: artists = $artistsQuery.data?.artists.edges; - $: characters = $charactersQuery.data?.characters.edges; - $: circles = $circlesQuery.data?.circles.edges; - $: worlds = $worldsQuery.data?.worlds.edges; - - function submit() { - dispatch('submit', { - direction: comic.direction, - layout: comic.layout, - rating: comic.rating, - category: comic.category, - censorship: comic.censorship, - title: comic.title, - originalTitle: comic.originalTitle, - url: comic.url, - date: comic.date === '' ? null : comic.date, - language: comic.language, - tags: { ids: comic.tags.map((t) => t.id) }, - artists: { ids: comic.artists.map((a) => a.id) }, - characters: { ids: comic.characters.map((c) => c.id) }, - circles: { ids: comic.circles.map((c) => c.id) }, - worlds: { ids: comic.worlds.map((w) => w.id) } + + interface Props { + input: OmitIdentifiers<FullComicFragment>; + submit: (input: UpdateComicInput) => void; + children?: Snippet; + } + + let { input = $bindable(), submit, children }: Props = $props(); + + let tagsQuery = $derived(comicTagList(client)); + let artistsQuery = $derived(artistList(client)); + let charactersQuery = $derived(characterList(client)); + let circlesQuery = $derived(circleList(client)); + let worldsQuery = $derived(worldList(client)); + + let tags = $derived($tagsQuery.data?.comicTags.edges); + let artists = $derived($artistsQuery.data?.artists.edges); + let characters = $derived($charactersQuery.data?.characters.edges); + let circles = $derived($circlesQuery.data?.circles.edges); + let worlds = $derived($worldsQuery.data?.worlds.edges); + + function onsubmit(event: SubmitEvent) { + event.preventDefault(); + + submit({ + direction: input.direction, + layout: input.layout, + rating: input.rating, + category: input.category, + censorship: input.censorship, + title: input.title, + originalTitle: input.originalTitle, + url: input.url, + date: input.date === '' ? null : input.date, + language: input.language, + tags: { ids: input.tags.map((t) => t.id) }, + artists: { ids: input.artists.map((a) => a.id) }, + characters: { ids: input.characters.map((c) => c.id) }, + circles: { ids: input.circles.map((c) => c.id) }, + worlds: { ids: input.worlds.map((w) => w.id) } }); } </script> -<form on:submit|preventDefault={submit}> +<form {onsubmit}> <div class="grid-labels"> - <Labelled label="Title" let:id> - <input required {id} bind:value={comic.title} title={comic.title} /> - </Labelled> - <Labelled label="Original Title" let:id> - <input {id} bind:value={comic.originalTitle} title={comic.originalTitle} /> - </Labelled> - <Labelled label="URL" let:id> - <input {id} bind:value={comic.url} /> - </Labelled> - <Labelled label="Date" let:id> - <input {id} type="date" bind:value={comic.date} pattern={'d{4}-d{2}-d{2}'} /> - </Labelled> - <Labelled label="Category" let:id> - <Select {id} options={categories} bind:value={comic.category} /> - </Labelled> - <Labelled label="Rating" let:id> - <Select {id} options={ratings} bind:value={comic.rating} /> - </Labelled> - <Labelled label="Censorship" let:id> - <Select {id} options={censorships} bind:value={comic.censorship} /> - </Labelled> - <Labelled label="Language" let:id> - <Select {id} options={languages} bind:value={comic.language} /> - </Labelled> - <Labelled label="Direction" let:id> - <Select {id} options={directions} bind:value={comic.direction} /> - </Labelled> - <Labelled label="Layout" let:id> - <Select {id} options={layouts} bind:value={comic.layout} /> - </Labelled> + <label class="self-center" for="title">Title</label> + <input required id="title" bind:value={input.title} title={input.title} /> + + <label class="self-center" for="original-title">Original Title</label> + <input id="original-title" bind:value={input.originalTitle} title={input.originalTitle} /> + + <label class="self-center" for="url">URL</label> + <input id="url" bind:value={input.url} /> + + <label class="self-center" for="date">Date</label> + <input id="date" type="date" bind:value={input.date} pattern={'d{4}-d{2}-d{2}'} /> + + <label class="self-center" for="category">Category</label> + <Select id="category" options={categories} bind:value={input.category} /> + + <label class="self-center" for="rating">Rating</label> + <Select id="rating" options={ratings} bind:value={input.rating} /> + + <label class="self-center" for="censorship">Censorship</label> + <Select id="censorship" options={censorships} bind:value={input.censorship} /> + + <label class="self-center" for="language">Language</label> + <Select id="language" options={languages} bind:value={input.language} /> + + <label class="self-center" for="direction">Direction</label> + <Select id="direction" options={directions} bind:value={input.direction} /> + + <label class="self-center" for="layout">Layout</label> + <Select id="layout" options={layouts} bind:value={input.layout} /> </div> - <LabelledBlock label="Artists" let:id> - <Select multi object {id} options={artists} bind:value={comic.artists} /> + <LabelledBlock label="Artists"> + {#snippet children({ id })} + <Select multi object {id} options={artists} bind:value={input.artists} /> + {/snippet} </LabelledBlock> - <LabelledBlock label="Circles" let:id> - <Select multi object {id} options={circles} bind:value={comic.circles} /> + <LabelledBlock label="Circles"> + {#snippet children({ id })} + <Select multi object {id} options={circles} bind:value={input.circles} /> + {/snippet} </LabelledBlock> - <LabelledBlock label="Characters" let:id> - <Select multi object {id} options={characters} bind:value={comic.characters} /> + <LabelledBlock label="Characters"> + {#snippet children({ id })} + <Select multi object {id} options={characters} bind:value={input.characters} /> + {/snippet} </LabelledBlock> - <LabelledBlock label="Worlds" let:id> - <Select multi object {id} options={worlds} bind:value={comic.worlds} /> + <LabelledBlock label="Worlds"> + {#snippet children({ id })} + <Select multi object {id} options={worlds} bind:value={input.worlds} /> + {/snippet} </LabelledBlock> - <LabelledBlock label="Tags" let:id> - <Select multi object {id} options={tags} bind:value={comic.tags} /> + <LabelledBlock label="Tags"> + {#snippet children({ id })} + <Select multi object {id} options={tags} bind:value={input.tags} /> + {/snippet} </LabelledBlock> - <slot /> + {@render children?.()} </form> diff --git a/frontend/src/lib/forms/NamespaceForm.svelte b/frontend/src/lib/forms/NamespaceForm.svelte index c05b6d8..3631d84 100644 --- a/frontend/src/lib/forms/NamespaceForm.svelte +++ b/frontend/src/lib/forms/NamespaceForm.svelte @@ -1,28 +1,31 @@ <script lang="ts"> - import { type NamespaceInput } from '$gql/Mutations'; - import { type OmitIdentifiers } from '$gql/Utils'; - import { type Namespace } from '$gql/graphql'; - import Labelled from '$lib/components/Labelled.svelte'; - import { createEventDispatcher } from 'svelte'; + import type { AddNamespaceInput, Namespace } from '$gql/graphql'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import { namespacePending, type FormProps } from '$lib/Form'; - const dispatch = createEventDispatcher<{ submit: NamespaceInput }>(); + let { initial, submit, children }: FormProps<Namespace, AddNamespaceInput> = $props(); - export let namespace: OmitIdentifiers<Namespace>; + let input = $state(initial); + let pending = $derived(input.name.length > 0 && namespacePending(initial, input)); - function submit() { - dispatch('submit', { name: namespace.name, sortName: namespace.sortName }); + function onsubmit(event: SubmitEvent) { + event.preventDefault(); + + submit({ ...input }); } </script> -<form on:submit|preventDefault={submit}> +<form {onsubmit}> <div class="grid-labels"> - <Labelled label="Name" let:id> - <!-- svelte-ignore a11y-autofocus --> - <input required autofocus {id} bind:value={namespace.name} /> - </Labelled> - <Labelled label="Sort name" let:id> - <input {id} bind:value={namespace.sortName} /> - </Labelled> + <label class="self-center" for="name">Name</label> + <!-- svelte-ignore a11y_autofocus --> + <input autofocus required id="name" bind:value={input.name} /> + <label class="self-center" for="sort-name">Sort name</label> + <input id="name" bind:value={input.sortName} /> + </div> + <div class="flex gap-4"> + {@render children?.()} + <div class="grow"></div> + <SubmitButton {pending} /> </div> - <slot /> </form> diff --git a/frontend/src/lib/forms/TagForm.svelte b/frontend/src/lib/forms/TagForm.svelte index 6cc2227..2789587 100644 --- a/frontend/src/lib/forms/TagForm.svelte +++ b/frontend/src/lib/forms/TagForm.svelte @@ -1,42 +1,41 @@ <script lang="ts"> - import type { TagInput } from '$gql/Mutations'; import { namespaceList } from '$gql/Queries'; - import type { OmitIdentifiers } from '$gql/Utils'; - import type { FullTag } from '$gql/graphql'; - import Labelled from '$lib/components/Labelled.svelte'; + import type { AddTagInput, FullTag } from '$gql/graphql'; + import { tagPending, type FormProps } from '$lib/Form'; import Select from '$lib/components/Select.svelte'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; import { getContextClient } from '@urql/svelte'; - import { createEventDispatcher } from 'svelte'; - const client = getContextClient(); - const dispatch = createEventDispatcher<{ submit: TagInput }>(); + let { initial, submit, children }: FormProps<FullTag, AddTagInput> = $props(); - export let tag: OmitIdentifiers<FullTag>; + let input = $state(initial); + let pending = $derived(input.name.length > 0 && tagPending(initial, input)); - $: namespaceQuery = namespaceList(client); - $: namespaces = $namespaceQuery.data?.namespaces.edges; + let namespaceQuery = $derived(namespaceList(getContextClient())); + let namespaces = $derived($namespaceQuery.data?.namespaces.edges); - function submit() { - dispatch('submit', { - name: tag.name, - description: tag.description, - namespaces: { ids: tag.namespaces.map((n) => n.id) } - }); + function onsubmit(event: SubmitEvent) { + event.preventDefault(); + + submit({ ...input, namespaces: { ids: input.namespaces.map((n) => n.id) } }); } </script> -<form on:submit|preventDefault={submit}> +<form {onsubmit}> <div class="grid-labels"> - <Labelled label="Name" let:id> - <!-- svelte-ignore a11y-autofocus --> - <input autofocus required {id} bind:value={tag.name} /> - </Labelled> - <Labelled label="Description" let:id> - <textarea rows={3} {id} bind:value={tag.description} /> - </Labelled> - <Labelled label="Namespaces" let:id> - <Select multi object {id} options={namespaces} bind:value={tag.namespaces} /> - </Labelled> + <label class="self-center" for="name">Name</label> + <!-- svelte-ignore a11y_autofocus --> + <input autofocus required id="name" bind:value={input.name} /> + + <label class="self-center" for="description">Description</label> + <textarea rows={3} id="description" bind:value={input.description}></textarea> + + <label class="self-center" for="namespaces">Namespaces</label> + <Select multi object id="namespaces" options={namespaces} bind:value={input.namespaces} /> + </div> + <div class="flex gap-4"> + {@render children?.()} + <div class="grow"></div> + <SubmitButton {pending} /> </div> - <slot /> </form> diff --git a/frontend/src/lib/forms/WorldForm.svelte b/frontend/src/lib/forms/WorldForm.svelte index 103dd5b..e6b821f 100644 --- a/frontend/src/lib/forms/WorldForm.svelte +++ b/frontend/src/lib/forms/WorldForm.svelte @@ -1,25 +1,29 @@ <script lang="ts"> - import { type WorldInput } from '$gql/Mutations'; - import { type OmitIdentifiers } from '$gql/Utils'; - import { type World } from '$gql/graphql'; - import Labelled from '$lib/components/Labelled.svelte'; - import { createEventDispatcher } from 'svelte'; + import type { AddWorldInput, World } from '$gql/graphql'; + import SubmitButton from '$lib/components/SubmitButton.svelte'; + import { itemPending, type FormProps } from '$lib/Form'; - const dispatch = createEventDispatcher<{ submit: WorldInput }>(); + let { initial, submit, children }: FormProps<World, AddWorldInput> = $props(); - export let world: OmitIdentifiers<World>; + let input = $state(initial); + let pending = $derived(input.name.length > 0 && itemPending(initial, input)); - function submit() { - dispatch('submit', { name: world.name }); + function onsubmit(event: SubmitEvent) { + event.preventDefault(); + + submit({ ...input }); } </script> -<form on:submit|preventDefault={submit}> +<form {onsubmit}> <div class="grid-labels"> - <Labelled label="Name" let:id> - <!-- svelte-ignore a11y-autofocus --> - <input autofocus required {id} bind:value={world.name} /> - </Labelled> + <label class="self-center" for="name">Name</label> + <!-- svelte-ignore a11y_autofocus --> + <input autofocus required id="name" bind:value={input.name} /> + </div> + <div class="flex gap-4"> + {@render children?.()} + <div class="grow"></div> + <SubmitButton {pending} /> </div> - <slot /> </form> diff --git a/frontend/src/lib/gallery/Gallery.svelte b/frontend/src/lib/gallery/Gallery.svelte index c3b6386..f94747a 100644 --- a/frontend/src/lib/gallery/Gallery.svelte +++ b/frontend/src/lib/gallery/Gallery.svelte @@ -2,12 +2,18 @@ import type { PageFragment } from '$gql/graphql'; import GalleryPage from './GalleryPage.svelte'; - export let pages: PageFragment[]; + interface Props { + pages: PageFragment[]; + open: (page: number) => void; + updateCover: (page: number) => void; + } + + let { pages, open, updateCover }: Props = $props(); </script> -<div class="max-h-full gap-2 overflow-auto p-1 pr-3"> - {#each pages as page, index} - <GalleryPage {page} {index} on:open on:cover /> +<div class="max-h-full gap-2 overflow-auto p-1 pr-3" tabindex="-1"> + {#each pages as page, index (page.id)} + <GalleryPage {page} {index} {open} {updateCover} /> {/each} </div> diff --git a/frontend/src/lib/gallery/GalleryPage.svelte b/frontend/src/lib/gallery/GalleryPage.svelte index 449321c..13bbfc8 100644 --- a/frontend/src/lib/gallery/GalleryPage.svelte +++ b/frontend/src/lib/gallery/GalleryPage.svelte @@ -1,65 +1,64 @@ <script lang="ts"> import type { PageFragment } from '$gql/graphql'; - import { getSelectionContext } from '$lib/Selection'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionOverlay from '$lib/selection/SelectionOverlay.svelte'; import { src } from '$lib/Utils'; - import { createEventDispatcher } from 'svelte'; - export let page: PageFragment; - export let index: number; - - const selection = getSelectionContext<PageFragment>(); + interface Props { + page: PageFragment; + index: number; + open: (page: number) => void; + updateCover: (page: number) => void; + } - let span: 'single' | 'double' | 'triple'; + let { page, index, open, updateCover }: Props = $props(); - $: page.image.aspectRatio, updateSpan(); + const selection = getSelectionContext<PageFragment>(); - function updateSpan() { + let span: 'single' | 'double' | 'triple' = $derived.by(() => { const aspectRatio = page.image.aspectRatio; if (aspectRatio <= 1) { - span = 'single'; + return 'single'; } else if (aspectRatio > 1 && aspectRatio <= 2) { - span = 'double'; - } else if (aspectRatio > 2) { - span = 'triple'; + return 'double'; + } else { + return 'triple'; } - } - - const dispatch = createEventDispatcher<{ open: number; cover: number }>(); + }); - function press(event: MouseEvent | KeyboardEvent) { + function onclick(event: MouseEvent | KeyboardEvent) { if (event instanceof KeyboardEvent && event.key !== 'Enter') { return; } - if ($selection.active) { + if (selection.active) { if (event.ctrlKey) { - dispatch('open', index); + open(index); } else if (selectable) { - $selection = $selection.update(index, event.shiftKey); + selection.update(index, event.shiftKey); } } else if (event.ctrlKey) { - dispatch('cover', page.id); + updateCover(page.id); } else { - dispatch('open', index); + open(index); } event.preventDefault(); } - $: selectable = $selection.selectable(page); - $: dim = $selection.active && !selectable; - $: selected = $selection.contains(page.id); + let selectable = $derived(selection.selectable(page)); + let dim = $derived(selection.active && !selectable); + let selected = $derived(selection.contains(page.id)); </script> <div class:dim role="button" tabindex="0" - class="{span} relative overflow-hidden rounded" - on:click={press} - on:keydown={press} + class="{span} relative overflow-hidden rounded-sm focus-visible:outline-4 focus-visible:outline-blue-600" + {onclick} + onkeydown={onclick} > <SelectionOverlay position="top" {selected} /> <img diff --git a/frontend/src/lib/icons/Bookmark.svelte b/frontend/src/lib/icons/Bookmark.svelte index 6f8e192..21b54ed 100644 --- a/frontend/src/lib/icons/Bookmark.svelte +++ b/frontend/src/lib/icons/Bookmark.svelte @@ -1,10 +1,15 @@ <script lang="ts"> - export let bookmarked: boolean | undefined = undefined; - export let hoverable = false; + interface Props { + bookmarked?: boolean; + hoverable?: boolean; + } + + let { bookmarked, hoverable = false }: Props = $props(); </script> {#if bookmarked} - <span class:hoverable class="icon-gray icon-base icon-[material-symbols--bookmark]" /> + <span class:hoverable class="icon-gray icon-base icon-[material-symbols--bookmark]"></span> {:else} - <span class:hoverable class="icon-gray icon-base dim icon-[material-symbols--bookmark-outline]" /> + <span class:hoverable class="icon-gray icon-base dim icon-[material-symbols--bookmark-outline]" + ></span> {/if} diff --git a/frontend/src/lib/icons/Female.svelte b/frontend/src/lib/icons/Female.svelte deleted file mode 100644 index c772a6a..0000000 --- a/frontend/src/lib/icons/Female.svelte +++ /dev/null @@ -1 +0,0 @@ -<span class="icon-xs icon-[material-symbols--female] -mx-[3px]" /> diff --git a/frontend/src/lib/icons/Location.svelte b/frontend/src/lib/icons/Location.svelte deleted file mode 100644 index e345f83..0000000 --- a/frontend/src/lib/icons/Location.svelte +++ /dev/null @@ -1 +0,0 @@ -<span class="icon-xs icon-[material-symbols--location-on-outline]" /> diff --git a/frontend/src/lib/icons/Male.svelte b/frontend/src/lib/icons/Male.svelte deleted file mode 100644 index e3578b7..0000000 --- a/frontend/src/lib/icons/Male.svelte +++ /dev/null @@ -1 +0,0 @@ -<span class="icon-xs icon-[material-symbols--male] -mx-px" /> diff --git a/frontend/src/lib/icons/Organized.svelte b/frontend/src/lib/icons/Organized.svelte index 66b5b00..ff177fa 100644 --- a/frontend/src/lib/icons/Organized.svelte +++ b/frontend/src/lib/icons/Organized.svelte @@ -1,21 +1,22 @@ <script lang="ts"> - export let organized: boolean | undefined = undefined; - export let hoverable = false; - export let tristate = false; - export let dim = false; + interface Props { + organized?: boolean; + hoverable?: boolean; + tristate?: boolean; + dim?: boolean; + } + + let { organized, hoverable = false, tristate = false, dim = false }: Props = $props(); </script> {#if organized} - <span class:hoverable class="icon-gray icon-base icon-[material-symbols--check-circle]" /> + <span class:hoverable class="icon-gray icon-base icon-[material-symbols--check-circle]"></span> {:else if organized === undefined || !tristate} <span class:hoverable class="icon-gray dim icon-base icon-[material-symbols--check-circle-outline]" - /> + ></span> {:else} - <span - class:hoverable - class:dim - class="icon-gray icon-base icon-[material-symbols--unpublished]" - /> + <span class:hoverable class:dim class="icon-gray icon-base icon-[material-symbols--unpublished]" + ></span> {/if} diff --git a/frontend/src/lib/icons/Orphan.svelte b/frontend/src/lib/icons/Orphan.svelte new file mode 100644 index 0000000..7d947d2 --- /dev/null +++ b/frontend/src/lib/icons/Orphan.svelte @@ -0,0 +1,15 @@ +<script lang="ts"> + interface Props { + orphaned?: boolean; + hoverable?: boolean; + } + + let { orphaned, hoverable = false }: Props = $props(); +</script> + +{#if orphaned} + <span class:hoverable class="icon-gray icon-base icon-[material-symbols--fmd-bad]"></span> +{:else} + <span class:hoverable class="icon-gray icon-base dim icon-[material-symbols--fmd-bad-outline]" + ></span> +{/if} diff --git a/frontend/src/lib/icons/Star.svelte b/frontend/src/lib/icons/Star.svelte index 7613c55..acce54d 100644 --- a/frontend/src/lib/icons/Star.svelte +++ b/frontend/src/lib/icons/Star.svelte @@ -1,22 +1,27 @@ <script lang="ts"> - export let large = false; - export let favourite: boolean | undefined = undefined; - export let hoverable = false; + interface Props { + large?: boolean; + favourite?: boolean; + hoverable?: boolean; + } + + let { large = false, favourite, hoverable = false }: Props = $props(); </script> {#if favourite} - <span class:hoverable class:large class="icon-yellow icon-[material-symbols--star-rounded]" /> + <span class:hoverable class:large class="icon-yellow icon-[material-symbols--star-rounded]" + ></span> {:else} <span class:hoverable class:large class="icon-yellow dim icon-[material-symbols--star-outline-rounded]" - /> + ></span> {/if} <style lang="postcss"> span { - @apply -m-px -translate-y-px text-[26px]; + @apply -m-px text-[26px]; } span.large { diff --git a/frontend/src/lib/icons/Transgender.svelte b/frontend/src/lib/icons/Transgender.svelte deleted file mode 100644 index 7d9adc6..0000000 --- a/frontend/src/lib/icons/Transgender.svelte +++ /dev/null @@ -1 +0,0 @@ -<span class="icon-xs icon-[material-symbols--transgender]" /> diff --git a/frontend/src/lib/navigation/Link.svelte b/frontend/src/lib/navigation/Link.svelte index 7297a69..d18fe3e 100644 --- a/frontend/src/lib/navigation/Link.svelte +++ b/frontend/src/lib/navigation/Link.svelte @@ -1,20 +1,37 @@ <script lang="ts"> - import { page } from '$app/stores'; + import { page } from '$app/state'; import { accelerator, type Shortcut } from '$lib/Shortcuts'; - import type { HTMLAttributeAnchorTarget } from 'svelte/elements'; + import type { Snippet } from 'svelte'; + import type { HTMLAnchorAttributes } from 'svelte/elements'; - export let href: string; - export let title: string; - export let accel: Shortcut; - export let matchExact = false; - export let target: HTMLAttributeAnchorTarget | undefined = undefined; - $: active = matchExact ? $page.url.pathname === href : $page.url.pathname.startsWith(href); + interface Props extends Pick<HTMLAnchorAttributes, 'title' | 'target'> { + href: string; + accel: Shortcut; + matchExact?: boolean; + children?: Snippet; + } + + let { href, title, accel, matchExact = false, target, children }: Props = $props(); + + let active = $derived.by(() => { + if (matchExact) { + return page.url.pathname === href; + } else { + return page.url.pathname.startsWith(href); + } + }); </script> -<li class:active class="items-center hover:bg-indigo-700 [&.active]:bg-indigo-700"> - <a class="flex items-center" {target} {title} {href} use:accelerator={accel}> +<li class:active class="items-center hover:bg-slate-600 [&.active]:bg-indigo-700"> + <a + class="flex items-center focus-visible:bg-slate-600 focus-visible:outline-hidden" + {target} + {title} + {href} + use:accelerator={accel} + > <div class="flex p-3"> - <slot /> + {@render children?.()} </div> </a> </li> diff --git a/frontend/src/lib/navigation/Navigation.svelte b/frontend/src/lib/navigation/Navigation.svelte index 76096c8..6734272 100644 --- a/frontend/src/lib/navigation/Navigation.svelte +++ b/frontend/src/lib/navigation/Navigation.svelte @@ -1,5 +1,11 @@ +<script lang="ts"> + import type { Snippet } from 'svelte'; + + let { children }: { children?: Snippet } = $props(); +</script> + <nav> <ul class="flex h-full flex-col bg-slate-700/70 font-medium"> - <slot /> + {@render children?.()} </ul> </nav> diff --git a/frontend/src/lib/pagination/Pagination.svelte b/frontend/src/lib/pagination/Pagination.svelte index 51612f4..28fbeb2 100644 --- a/frontend/src/lib/pagination/Pagination.svelte +++ b/frontend/src/lib/pagination/Pagination.svelte @@ -1,45 +1,52 @@ <script lang="ts"> - import { getPaginationContext } from '$lib/Pagination'; + import type { PaginationData } from '$lib/Navigation'; import Target from './Target.svelte'; - const pagination = getPaginationContext(); - export let context = 2; + interface Props { + context?: number; + pagination: PaginationData; + total: number; + } - $: totalPages = Math.ceil($pagination.total / $pagination.items); - $: rightBoundary = $pagination.page - context; - $: leftBoundary = $pagination.page + context; + let { context = 2, pagination, total }: Props = $props(); - $: shiftRight = leftBoundary - totalPages; - $: shiftLeft = 1 - rightBoundary; + let totalPages = $derived(Math.ceil(total / pagination.items)); + let rightBoundary = $derived(pagination.page - context); + let leftBoundary = $derived(pagination.page + context); - $: containedLeft = leftBoundary <= totalPages; - $: containedRight = rightBoundary > 0; + let shiftRight = $derived(leftBoundary - totalPages); + let shiftLeft = $derived(1 - rightBoundary); - $: start = Math.max(1, containedLeft ? rightBoundary : rightBoundary - shiftRight); - $: end = Math.min(totalPages, containedRight ? leftBoundary : leftBoundary + shiftLeft); + let containedLeft = $derived(leftBoundary <= totalPages); + let containedRight = $derived(rightBoundary > 0); - $: leftmost = $pagination.page <= 1; - $: rightmost = $pagination.page >= totalPages; + let start = $derived(Math.max(1, containedLeft ? rightBoundary : rightBoundary - shiftRight)); + let end = $derived( + Math.min(totalPages, containedRight ? leftBoundary : leftBoundary + shiftLeft) + ); + + let leftmost = $derived(pagination.page <= 1); + let rightmost = $derived(pagination.page >= totalPages); </script> {#if totalPages > 1} <div class="flex justify-center gap-2"> - <Target disabled={leftmost} page={1}> - <span class="icon-base icon-[material-symbols--keyboard-double-arrow-left]" /> + <Target disabled={leftmost} target={1}> + <span class="icon-base icon-[material-symbols--keyboard-double-arrow-left]"></span> </Target> - <Target disabled={leftmost} page={$pagination.page - 1}> - <span class="icon-base icon-[material-symbols--keyboard-arrow-left]" /> + <Target disabled={leftmost} target={pagination.page - 1}> + <span class="icon-base icon-[material-symbols--keyboard-arrow-left]"></span> </Target> - {#each Array.from({ length: end + 1 - start }, (_, i) => i + start) as page} - <Target active={$pagination.page === page} {page}> - <p>{page.toString()}</p> + {#each Array.from({ length: end + 1 - start }, (_, i) => i + start) as target (target)} + <Target active={pagination.page === target} {target}> + <p>{target.toString()}</p> </Target> {/each} - <Target disabled={rightmost} page={$pagination.page + 1}> - <span class="icon-base icon-[material-symbols--keyboard-arrow-right]" /> + <Target disabled={rightmost} target={pagination.page + 1}> + <span class="icon-base icon-[material-symbols--keyboard-arrow-right]"></span> </Target> - <Target disabled={rightmost} page={totalPages}> - <span class="icon-base icon-[material-symbols--keyboard-double-arrow-right]" /> + <Target disabled={rightmost} target={totalPages}> + <span class="icon-base icon-[material-symbols--keyboard-double-arrow-right]"></span> </Target> </div> {/if} diff --git a/frontend/src/lib/pagination/Target.svelte b/frontend/src/lib/pagination/Target.svelte index 9044bb9..76e0d9e 100644 --- a/frontend/src/lib/pagination/Target.svelte +++ b/frontend/src/lib/pagination/Target.svelte @@ -1,21 +1,27 @@ <script lang="ts"> - import { page as pageStore } from '$app/stores'; + import { page } from '$app/state'; import { navigate } from '$lib/Navigation'; + import type { Snippet } from 'svelte'; - export let active = false; + interface Props { + active?: boolean; + disabled?: boolean; + target: number; + children?: Snippet; + } - export let disabled = false; - export let page: number; + let { active = false, disabled = false, target, children }: Props = $props(); + + function onclick() { + navigate({ pagination: { page: target } }, page.url.searchParams); + } </script> <button - on:click={() => { - navigate({ pagination: { page: page } }, $pageStore.url.searchParams); - }} - class:bg-slate-700={active} - class:bg-slate-800={!active} - class="flex h-8 w-8 items-center justify-center rounded-sm p-0 text-base hover:text-white disabled:text-slate-600" + {onclick} + class:active + class="flex h-8 w-8 items-center justify-center rounded-sm p-0 text-base font-medium hover:bg-slate-700/50 hover:text-white disabled:text-slate-600 disabled:hover:bg-inherit [&.active]:bg-slate-700 [&:not(active)]:bg-slate-800" {disabled} > - <slot /> + {@render children?.()} </button> diff --git a/frontend/src/lib/pills/AssociationPill.svelte b/frontend/src/lib/pills/AssociationPill.svelte deleted file mode 100644 index 85dbe39..0000000 --- a/frontend/src/lib/pills/AssociationPill.svelte +++ /dev/null @@ -1,30 +0,0 @@ -<script lang="ts"> - import Pill from './Pill.svelte'; - - type Association = 'artist' | 'circle' | 'world' | 'character'; - - export let name: string; - export let type: Association; -</script> - -<Pill {name}> - <span class={`${type} icon-xs`} slot="icon" /> -</Pill> - -<style lang="postcss"> - .artist { - @apply icon-[material-symbols--person] -mx-px; - } - - .character { - @apply icon-[material-symbols--face]; - } - - .circle { - @apply icon-[material-symbols--group] mx-px; - } - - .world { - @apply icon-[material-symbols--public]; - } -</style> diff --git a/frontend/src/lib/pills/ComicPills.svelte b/frontend/src/lib/pills/ComicPills.svelte deleted file mode 100644 index 671bbf2..0000000 --- a/frontend/src/lib/pills/ComicPills.svelte +++ /dev/null @@ -1,37 +0,0 @@ -<script lang="ts"> - import type { ComicFragment } from '$gql/graphql'; - import AssociationPill from '$lib/pills/AssociationPill.svelte'; - import TagPill from '$lib/pills/TagPill.svelte'; - - export let comic: ComicFragment; -</script> - -<div class="flex flex-col gap-1"> - {#if comic.artists.length || comic.circles.length} - <div class="flex flex-wrap gap-1"> - {#each comic.artists as { name } (name)} - <AssociationPill {name} type="artist" /> - {/each} - {#each comic.circles as { name } (name)} - <AssociationPill {name} type="circle" /> - {/each} - </div> - {/if} - {#if comic.characters.length || comic.worlds.length} - <div class="flex flex-wrap gap-1"> - {#each comic.worlds as { name } (name)} - <AssociationPill {name} type="world" /> - {/each} - {#each comic.characters as { name } (name)} - <AssociationPill {name} type="character" /> - {/each} - </div> - {/if} - {#if comic.tags.length} - <div class="flex flex-wrap gap-1"> - {#each comic.tags as { name, description } (name)} - <TagPill {name} {description} /> - {/each} - </div> - {/if} -</div> diff --git a/frontend/src/lib/pills/FooterPill.svelte b/frontend/src/lib/pills/FooterPill.svelte new file mode 100644 index 0000000..3da1811 --- /dev/null +++ b/frontend/src/lib/pills/FooterPill.svelte @@ -0,0 +1,15 @@ +<script lang="ts"> + import type { Snippet } from 'svelte'; + + interface Props { + text: string; + icon?: Snippet; + } + + let { text, icon }: Props = $props(); +</script> + +<div class="flex items-center rounded-sm p-0.5 text-zinc-300"> + {@render icon?.()} + <span>{text}</span> +</div> diff --git a/frontend/src/lib/pills/Pill.svelte b/frontend/src/lib/pills/Pill.svelte index 7aa9670..98d9b5a 100644 --- a/frontend/src/lib/pills/Pill.svelte +++ b/frontend/src/lib/pills/Pill.svelte @@ -1,40 +1,83 @@ -<script lang="ts" context="module"> - export type PillColour = 'pink' | 'blue' | 'violet' | 'amber' | 'zinc' | 'sky'; -</script> - <script lang="ts"> - export let name: string; - export let tooltip: string | null | undefined = undefined; - export let colour: PillColour = 'zinc'; + interface Props { + name: string; + tooltip?: string | null; + style: string; + highlight?: boolean; + } + + let { name, tooltip, style, highlight = false }: Props = $props(); </script> -<div class="flex items-center rounded border p-0.5 {colour}" title={tooltip}> - <slot name="icon" /> +<div class:highlight class="flex items-center rounded-sm border p-0.5 {style}" title={tooltip}> + {#if style === 'female'} + <span class="icon-xs icon-[material-symbols--female] -mx-[3px]"></span> + {:else if style === 'male'} + <span class="icon-xs icon-[material-symbols--male] -mx-px"></span> + {:else if style === 'trans'} + <span class="icon-xs icon-[material-symbols--transgender]"></span> + {:else if style === 'location'} + <span class="icon-xs icon-[material-symbols--location-on-outline]"></span> + {:else if style === 'artist'} + <span class="icon-xs icon-[material-symbols--person] -mx-px"></span> + {:else if style === 'character'} + <span class="icon-xs icon-[material-symbols--face]"></span> + {:else if style === 'circle'} + <span class="icon-xs icon-[material-symbols--group] mx-px"></span> + {:else if style === 'world'} + <span class="icon-xs icon-[material-symbols--public]"></span> + {/if} <span>{name}</span> </div> <style lang="postcss"> - .pink { + @reference "tailwindcss/theme"; + + div { + @apply border-zinc-700 bg-zinc-700/20 text-zinc-300; + } + + div.highlight { + @apply transition-colors hover:border-zinc-600 hover:bg-zinc-500/20 hover:text-zinc-200; + } + + .female { @apply border-pink-800 bg-pink-800/20 text-pink-200; } - .blue { + .female.highlight { + @apply hover:border-pink-700 hover:bg-pink-600/20 hover:text-pink-100; + } + + .male { @apply border-blue-800 bg-blue-800/20 text-blue-200; } - .violet { + .male.highlight { + @apply hover:border-blue-700 hover:bg-blue-600/20 hover:text-blue-100; + } + + .trans { @apply border-violet-800 bg-violet-800/20 text-violet-200; } - .amber { + .trans.highlight { + @apply hover:border-violet-600 hover:bg-violet-600/20 hover:text-violet-100; + } + + .mixed { @apply border-amber-800 bg-amber-800/20 text-amber-200; } - .sky { + .mixed.highlight { + @apply hover:border-amber-700 hover:bg-amber-600/20 hover:text-amber-100; + } + + .location { @apply border-sky-800 bg-sky-800/20 text-sky-200; } - .zinc { - @apply border-zinc-700 bg-zinc-700/20 text-zinc-300; + .location.highlight { + @apply hover:border-sky-700 hover:bg-sky-600/20 hover:text-sky-100; } </style> diff --git a/frontend/src/lib/pills/TagPill.svelte b/frontend/src/lib/pills/TagPill.svelte index 60221bd..bbd3c55 100644 --- a/frontend/src/lib/pills/TagPill.svelte +++ b/frontend/src/lib/pills/TagPill.svelte @@ -1,40 +1,16 @@ <script lang="ts"> - import Female from '$lib/icons/Female.svelte'; - import Location from '$lib/icons/Location.svelte'; - import Male from '$lib/icons/Male.svelte'; - import Transgender from '$lib/icons/Transgender.svelte'; - import { SvelteComponent } from 'svelte'; - import Pill, { type PillColour } from './Pill.svelte'; + import type { ComicTag } from '$gql/graphql'; + import { joinText } from '$lib/Utils'; + import Pill from './Pill.svelte'; - export let name: string; - export let description: string | undefined | null = undefined; - - let [namespace, tag] = name.split(':'); - - const styles: Record<string, PillColour> = { - female: 'pink', - male: 'blue', - trans: 'violet', - mixed: 'amber', - location: 'sky', - rest: 'zinc' - }; - - const icons: Record<string, typeof SvelteComponent<Record<string, unknown>>> = { - female: Female, - male: Male, - trans: Transgender, - location: Location - }; + interface Props extends Pick<ComicTag, 'name' | 'description'> { + highlight?: boolean; + } - const colour = styles[namespace] ?? styles.rest; - const icon = icons[namespace]; + let { name, description, highlight = false }: Props = $props(); - function formatTooltip() { - return [name, description].filter((v) => v).join('\n\n'); - } + let [namespace, tag] = name.split(':'); + let tooltip = joinText([name, description], '\n\n'); </script> -<Pill name={tag} tooltip={formatTooltip()} {colour}> - <svelte:component this={icon} slot="icon" /> -</Pill> +<Pill {highlight} name={tag} style={namespace} {tooltip}></Pill> diff --git a/frontend/src/lib/reader/PageView.svelte b/frontend/src/lib/reader/PageView.svelte index 08764b7..2b61a78 100644 --- a/frontend/src/lib/reader/PageView.svelte +++ b/frontend/src/lib/reader/PageView.svelte @@ -1,45 +1,45 @@ <script lang="ts"> import { Direction, Layout, type PageFragment } from '$gql/graphql'; - import { getReaderContext, partition, type Chunk } from '$lib/Reader'; + import { type Chunk, getReaderContext, partition } from '$lib/Reader.svelte'; import { binds } from '$lib/Shortcuts'; import { src } from '$lib/Utils'; import ReaderPage from './ReaderPage.svelte'; + import SliderMargin from './components/SliderMargin.svelte'; + import SliderTooltip from './components/SliderTooltip.svelte'; const reader = getReaderContext(); - export let direction: Direction; - export let layout: Layout; - - let chunks: Chunk[] = []; - let lookup: number[] = []; - - let main: PageFragment; - let secondary: PageFragment | undefined; + let { direction, layout }: { direction: Direction; layout: Layout } = $props(); function gotoChunk(to: number) { if (to < 0 || to >= chunks.length) return; - $reader.page = chunks[to].index; + reader.page = chunks[to].index; } function pagesAround(around: number) { - const peek = (at: number) => { - if (at < 0 || at >= chunks.length) return []; + const pages: PageFragment[] = []; - const pages = [chunks[at].main]; + const push = (at: number) => { + if (at < 0 || at >= chunks.length) return; + + pages.push(chunks[at].main); if (chunks[at].secondary) { pages.push(chunks[at].secondary); } - - return pages; }; - return [...peek(lookup[around] + 1), ...peek(lookup[around] - 1)]; + for (let i = 1; i <= 2; i++) { + push(lookup[around] + i); + push(lookup[around] - i); + } + + return pages; } - const next = () => gotoChunk(lookup[$reader.page] + 1); - const prev = () => gotoChunk(lookup[$reader.page] - 1); + const next = () => gotoChunk(lookup[reader.page] + 1); + const prev = () => gotoChunk(lookup[reader.page] - 1); const clickLeft = () => (direction === Direction.LeftToRight ? prev() : next()); const clickRight = () => (direction === Direction.RightToLeft ? prev() : next()); @@ -56,8 +56,11 @@ } } - $: [chunks, lookup] = partition($reader.pages, layout); - $: layout, ({ main, secondary } = chunks[lookup[$reader.page]]); + let [chunks, lookup] = $derived(partition(reader.pages, layout)); + let currentChunk = $derived(chunks[lookup[reader.page]]); + let { main, secondary } = $derived(currentChunk); + + let reverse = $derived(direction === Direction.RightToLeft); </script> <svelte:document @@ -76,16 +79,64 @@ /> {#if !secondary} - <ReaderPage page={main} on:click={clickMain} --justify="center" /> -{:else if direction === Direction.LeftToRight} - <ReaderPage page={main} on:click={prev} --justify="flex-end" /> - <ReaderPage page={secondary} on:click={next} --justify="flex-start" /> + <ReaderPage page={main} onclick={clickMain} --justify="center" /> +{:else if reverse} + <ReaderPage page={secondary} onclick={next} --justify="flex-end" /> + <ReaderPage page={main} onclick={prev} --justify="flex-start" /> {:else} - <ReaderPage page={secondary} on:click={next} --justify="flex-end" /> - <ReaderPage page={main} on:click={prev} --justify="flex-start" /> + <ReaderPage page={main} onclick={prev} --justify="flex-end" /> + <ReaderPage page={secondary} onclick={next} --justify="flex-start" /> {/if} + +{#snippet pagesIn(chunk: Chunk)} + {#if chunk.secondary} + {chunk.index + 1} - {chunk.index + 2} + {:else} + {chunk.index + 1} + {/if} +{/snippet} + +<div class="group/slider absolute bottom-0 z-1 flex w-full pt-20"> + <div class:reverse class="flex h-1 w-full transition-[height] group-hover/slider:h-8"> + <SliderMargin> + {@render pagesIn(currentChunk)} + </SliderMargin> + <div class:reverse class="flex w-full bg-gray-400/60 backdrop-blur-2xl"> + <!-- eslint-disable-next-line svelte/require-each-key --> + {#each chunks as chunk, index} + <button + type="button" + class:read={index <= lookup[reader.page]} + class="group/page relative grow" + onclick={() => reader.open(chunk.index)} + aria-label={`Open page ${chunk.index + 1}`} + > + <SliderTooltip> + {@render pagesIn(chunk)} + </SliderTooltip> + </button> + {/each} + </div> + <SliderMargin> + {reader.pages.length} + </SliderMargin> + </div> +</div> + <div class="invisible absolute"> - {#each pagesAround($reader.page) as page} + {#each pagesAround(reader.page) as page (page.id)} <img src={src(page.image, 'full')} alt="" /> {/each} </div> + +<style lang="postcss"> + @reference "tailwindcss/theme"; + + .reverse { + flex-direction: row-reverse; + } + + button.read { + @apply bg-blue-600/60; + } +</style> diff --git a/frontend/src/lib/reader/Reader.svelte b/frontend/src/lib/reader/Reader.svelte index 15ebdf4..a720a77 100644 --- a/frontend/src/lib/reader/Reader.svelte +++ b/frontend/src/lib/reader/Reader.svelte @@ -1,43 +1,52 @@ <script lang="ts"> import { trapFocus } from '$lib/Actions'; - import { getReaderContext } from '$lib/Reader'; + import { getReaderContext } from '$lib/Reader.svelte'; import { fadeDefault, slideXDefault } from '$lib/Transitions'; + import type { Snippet } from 'svelte'; import { fade, slide } from 'svelte/transition'; import CloseReaderButton from './components/CloseReaderButton.svelte'; - import PageIndicator from './components/PageIndicator.svelte'; import ReaderMenuButton from './components/ReaderMenuButton.svelte'; + import ToggleFullscreenButton from './components/ToggleFullscreenButton.svelte'; + + let { sidebar, children }: { sidebar?: Snippet; children?: Snippet } = $props(); const reader = getReaderContext(); + + let dialog: HTMLDivElement | undefined = $state(); </script> -{#if $reader.visible} +{#if reader.visible} <div role="dialog" - class="fixed bottom-0 left-0 right-0 top-0 z-10 flex h-full w-full bg-black" + class="fixed top-0 right-0 bottom-0 left-0 z-10 flex h-full w-full bg-black" transition:fade={fadeDefault} use:trapFocus + bind:this={dialog} > - {#if $$slots.sidebar && $reader.sidebar} - <aside class="w-[36rem] shrink-0 bg-slate-800" transition:slide={slideXDefault}> + {#if sidebar && reader.sidebar} + <aside + class="z-10 w-[36rem] shrink-0 bg-slate-800 shadow-md shadow-slate-800" + transition:slide={slideXDefault} + > <div class="flex h-full min-w-[36rem] flex-col gap-4 overflow-auto p-4"> - <slot name="sidebar" /> + {@render sidebar?.()} </div> </aside> {/if} <main class="relative flex grow"> - <div class="absolute z-10 flex w-full p-1 text-lg [&>*:last-child]:ml-auto"> - {#if $$slots.sidebar} - <ReaderMenuButton /> - {/if} - <CloseReaderButton /> - </div> - <div class="absolute bottom-0 right-0 z-10 flex p-1 text-lg"> - <PageIndicator /> + <div class="absolute flex w-full p-1 text-lg"> + <div class="flex flex-col gap-1"> + {#if sidebar} + <ReaderMenuButton /> + {/if} + </div> + <div class="ml-auto flex flex-col gap-1"> + <CloseReaderButton /> + <ToggleFullscreenButton {dialog} /> + </div> </div> - <div class="flex grow"> - <slot /> - </div> + {@render children?.()} </main> </div> {/if} diff --git a/frontend/src/lib/reader/ReaderPage.svelte b/frontend/src/lib/reader/ReaderPage.svelte index c86414d..4a19c6e 100644 --- a/frontend/src/lib/reader/ReaderPage.svelte +++ b/frontend/src/lib/reader/ReaderPage.svelte @@ -1,48 +1,25 @@ <script lang="ts"> import type { PageFragment } from '$gql/graphql'; - import Spinner from '$lib/components/Spinner.svelte'; import { src } from '$lib/Utils'; - import { onDestroy } from 'svelte'; + import type { MouseEventHandler } from 'svelte/elements'; - export let page: PageFragment; - - let loading = false; - let loadingTimeout: NodeJS.Timeout; - let lastId = -1; - - $: page.id, updateLoadingState(); - - function updateLoadingState() { - if (page.id === lastId) return; - lastId = page.id; - - loadingTimeout = setTimeout(() => (loading = true), 150); - } - - function finishLoading() { - clearTimeout(loadingTimeout); - loading = false; + interface Props { + page: PageFragment; + onclick: MouseEventHandler<HTMLDivElement>; } - onDestroy(() => clearTimeout(loadingTimeout)); + let { page, onclick }: Props = $props(); </script> -<!-- svelte-ignore a11y-click-events-have-key-events --> -<!-- svelte-ignore a11y-no-static-element-interactions --> -<div class="relative flex grow" on:click> - <div class="absolute right-0 top-0 z-0 h-full w-full"> - {#if loading} - <Spinner /> - {/if} - </div> +<!-- svelte-ignore a11y_click_events_have_key_events --> +<!-- svelte-ignore a11y_no_static_element_interactions --> +<div class="flex w-full" {onclick}> <img - class="h-auto w-auto object-contain transition-opacity duration-200" - class:opacity-0={loading} + class="h-auto w-auto object-contain" width={page.image.width} height={page.image.height} src={src(page.image, 'full')} - alt="" - on:load={finishLoading} + alt={page.path} /> </div> diff --git a/frontend/src/lib/reader/components/CloseReaderButton.svelte b/frontend/src/lib/reader/components/CloseReaderButton.svelte index 0c88323..6a31fd2 100644 --- a/frontend/src/lib/reader/components/CloseReaderButton.svelte +++ b/frontend/src/lib/reader/components/CloseReaderButton.svelte @@ -1,19 +1,22 @@ <script lang="ts"> - import { getReaderContext } from '$lib/Reader'; + import { getReaderContext } from '$lib/Reader.svelte'; import { accelerator } from '$lib/Shortcuts'; const reader = getReaderContext(); + + function onclick() { + reader.visible = false; + reader.sidebar = false; + } </script> <button type="button" - class="btn floating" + class="btn-transparent" title="Close reader" - on:click={() => { - $reader.visible = false; - $reader.sidebar = false; - }} + aria-label="Close reader" + {onclick} use:accelerator={'Escape'} > - <span class="icon-lg icon-[material-symbols--close]" /> + <span class="icon-lg icon-[material-symbols--close]"></span> </button> diff --git a/frontend/src/lib/reader/components/PageIndicator.svelte b/frontend/src/lib/reader/components/PageIndicator.svelte deleted file mode 100644 index f79fc00..0000000 --- a/frontend/src/lib/reader/components/PageIndicator.svelte +++ /dev/null @@ -1,9 +0,0 @@ -<script lang="ts"> - import { getReaderContext } from '$lib/Reader'; - - const reader = getReaderContext(); -</script> - -<div class="floating !p-2"> - {$reader.page + 1}/{$reader.pages.length} -</div> diff --git a/frontend/src/lib/reader/components/ReaderMenuButton.svelte b/frontend/src/lib/reader/components/ReaderMenuButton.svelte index aa20206..924342f 100644 --- a/frontend/src/lib/reader/components/ReaderMenuButton.svelte +++ b/frontend/src/lib/reader/components/ReaderMenuButton.svelte @@ -1,16 +1,19 @@ <script lang="ts"> - import { getReaderContext } from '$lib/Reader'; + import { getReaderContext } from '$lib/Reader.svelte'; import { accelerator } from '$lib/Shortcuts'; const reader = getReaderContext(); + + let title = $derived(`${reader.sidebar ? 'Hide' : 'Show'} menu`); </script> <button type="button" - class="btn floating invisible xl:visible" - title={`${$reader.sidebar ? 'Hide' : 'Show'} menu`} - on:click={() => ($reader.sidebar = !$reader.sidebar)} + class="btn-transparent hidden xl:flex" + {title} + aria-label={title} + onclick={() => (reader.sidebar = !reader.sidebar)} use:accelerator={'z'} > - <span class="icon-lg icon-[material-symbols--dock-to-right]" /> + <span class="icon-lg icon-[material-symbols--dock-to-right]"></span> </button> diff --git a/frontend/src/lib/reader/components/SliderMargin.svelte b/frontend/src/lib/reader/components/SliderMargin.svelte new file mode 100644 index 0000000..c2f9a55 --- /dev/null +++ b/frontend/src/lib/reader/components/SliderMargin.svelte @@ -0,0 +1,11 @@ +<script lang="ts"> + import type { Snippet } from 'svelte'; + + let { children }: { children: Snippet } = $props(); +</script> + +<div + class="flex h-full w-22 items-center justify-center px-1 font-semibold text-white/0 transition-colors group-hover/slider:bg-black group-hover/slider:text-white" +> + {@render children()} +</div> diff --git a/frontend/src/lib/reader/components/SliderTooltip.svelte b/frontend/src/lib/reader/components/SliderTooltip.svelte new file mode 100644 index 0000000..9e0322d --- /dev/null +++ b/frontend/src/lib/reader/components/SliderTooltip.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import type { Snippet } from 'svelte'; + + let { children }: { children: Snippet } = $props(); +</script> + +<div + class="invisible absolute bottom-10 w-20 rounded-xl bg-blue-500 p-1 font-semibold text-white drop-shadow-md group-hover/page:visible" +> + {@render children()} +</div> + +<style lang="postcss"> + div { + left: calc(50% - 2.5rem); + } +</style> diff --git a/frontend/src/lib/reader/components/ToggleFullscreenButton.svelte b/frontend/src/lib/reader/components/ToggleFullscreenButton.svelte new file mode 100644 index 0000000..9ad4ce6 --- /dev/null +++ b/frontend/src/lib/reader/components/ToggleFullscreenButton.svelte @@ -0,0 +1,34 @@ +<script lang="ts"> + import { accelerator } from '$lib/Shortcuts'; + import { toastFinally } from '$lib/Toasts'; + + let { dialog }: { dialog?: HTMLElement } = $props(); + + function onclick() { + if (isFullscreen) { + document.exitFullscreen().catch(toastFinally); + } else if (dialog?.requestFullscreen) { + dialog.requestFullscreen().catch(toastFinally); + } + } + + let fullscreenElement: HTMLElement | null = $state(null); + let isFullscreen = $derived(fullscreenElement !== null); +</script> + +<svelte:document bind:fullscreenElement /> + +<button + type="button" + class="btn-transparent" + title="Toggle fullscreen" + aria-label="Toggle fullscreen" + {onclick} + use:accelerator={'f'} +> + {#if isFullscreen} + <span class="icon-lg icon-[material-symbols--fullscreen-exit]"></span> + {:else} + <span class="icon-lg icon-[material-symbols--fullscreen]"></span> + {/if} +</button> diff --git a/frontend/src/lib/scraper/ComicScrapeForm.svelte b/frontend/src/lib/scraper/ComicScrapeForm.svelte index 30ad89b..6f995a9 100644 --- a/frontend/src/lib/scraper/ComicScrapeForm.svelte +++ b/frontend/src/lib/scraper/ComicScrapeForm.svelte @@ -2,60 +2,69 @@ import { upsertComics } from '$gql/Mutations'; import { comicScrapersQuery, scrapeComic } from '$gql/Queries'; import { isError } from '$gql/Utils'; - import { OnMissing, type FullComicFragment } from '$gql/graphql'; - import { ScrapedComicSelector, getScraperContext } from '$lib/Scraper'; + import { OnMissing, type FullComicFragment, type ScrapeComicQuery } from '$gql/graphql'; import { toastError, toastFinally } from '$lib/Toasts'; import Select from '$lib/components/Select.svelte'; import Spinner from '$lib/components/Spinner.svelte'; - import { getContextClient } from '@urql/svelte'; + import { getContextClient, type OperationResult } from '@urql/svelte'; + import { getScraperContext, ScrapedComicSelector } from './Scraper.svelte'; import SelectorGroup from './components/SelectorGroup.svelte'; import SelectorItem from './components/SelectorItem.svelte'; let client = getContextClient(); const context = getScraperContext(); - export let comic: FullComicFragment; - let createMissing = false; - let loading = false; + interface Props { + comic: FullComicFragment; + onupsert: () => void; + } - $: scrapersResult = comicScrapersQuery(client, { id: comic.id }); - $: scrapers = $scrapersResult.data?.comicScrapers; + let { comic, onupsert }: Props = $props(); + let createMissing = $state(false); + let loading = $state(false); - function scrape() { - loading = true; - scrapeComic(client, { id: comic.id, scraper: $context.scraper }) - .then((result) => { - if (result.error) { - toastError(result.error.message); - return; - } + let scrapersResult = $derived(comicScrapersQuery(client, { id: comic.id })); + let scrapers = $derived($scrapersResult.data?.comicScrapers); - if (result.data) { - if (isError(result.data.scrapeComic)) { - toastError(result.data.scrapeComic.message); - return; - } + function scrape(event: SubmitEvent) { + event.preventDefault(); + if (!context.scraper) return; - if (result.data.scrapeComic.__typename === 'ScrapeComicResult') { - $context.selector = new ScrapedComicSelector(result.data.scrapeComic.data, comic); - $context.warnings = result.data.scrapeComic.warnings; - } - } - }) + loading = true; + scrapeComic(client, { id: comic.id, scraper: context.scraper }) + .then(handleScrapeResult) .catch(toastFinally) .finally(() => (loading = false)); } - function updateFromScrape(createMissing: boolean) { - if (!$context.selector) return; + function handleScrapeResult(result: OperationResult<ScrapeComicQuery>) { + if (result.error) { + toastError(result.error.message); + return; + } + + if (result.data) { + if (isError(result.data.scrapeComic)) { + toastError(result.data.scrapeComic.message); + return; + } + + if (result.data.scrapeComic.__typename === 'ScrapeComicResult') { + context.selector = new ScrapedComicSelector(result.data.scrapeComic.data, comic); + context.warnings = result.data.scrapeComic.warnings; + } + } + } + + function upsert(event: SubmitEvent) { + event.preventDefault(); + if (!context.selector) return; - upsertComics(client, { - ids: comic.id, - input: $context.selector.toInput(createMissing ? OnMissing.Create : OnMissing.Ignore) - }) + const input = context.selector.input(createMissing ? OnMissing.Create : OnMissing.Ignore); + upsertComics(client, { ids: comic.id, input }) .then(() => { - $context.selector = undefined; - $context.warnings = []; + onupsert(); + context.reset(); }) .catch(toastFinally); } @@ -65,56 +74,57 @@ {#if scrapers && scrapers.length === 0} <h2 class="text-base">No scrapers available.</h2> {:else} - <form on:submit|preventDefault={scrape}> + <form onsubmit={scrape}> <div class="grid grid-cols-6 gap-2"> <div class="col-span-5"> <Select id="scrapers" options={scrapers} - placeholder={'Select scraper...'} - bind:value={$context.scraper} + placeholder="Select scraper..." + bind:value={context.scraper} /> </div> - <button type="submit" disabled={!$context.scraper} class="btn-blue">Scrape</button> + <button type="submit" disabled={!context.scraper} class="btn-blue">Scrape</button> </div> </form> {/if} {#if loading} <Spinner /> - {:else if $context.selector} - {#if $context.warnings.length > 0} + {:else if context.selector} + {#if context.warnings.length > 0} <div class="flex flex-col gap-2"> <h2 class="flex gap-1 border-b border-slate-700 text-base font-medium">Warnings</h2> <ul class="ml-2 list-inside list-disc"> - {#each $context.warnings as warning} + <!-- eslint-disable-next-line svelte/require-each-key --> + {#each context.warnings as warning} <li>{warning}</li> {/each} </ul> </div> {/if} - {#if !$context.selector.hasData()} + {#if !context.selector.pending()} <h2 class="text-base">No data to merge.</h2> {:else} <div class="flex flex-col gap-2"> <h2 class="border-b border-slate-700 text-base font-medium">Results</h2> - <form on:submit|preventDefault={() => updateFromScrape(createMissing)}> + <form onsubmit={upsert}> <div class="grid grid-cols-6 gap-4 pb-2"> - <SelectorItem title="Title" selector={$context.selector.title} /> - <SelectorItem title="Original Title" selector={$context.selector.originalTitle} /> - <SelectorItem title="URL" selector={$context.selector.url} /> - <SelectorItem title="Date" selector={$context.selector.date} --span="2" /> - <SelectorItem title="Category" selector={$context.selector.category} --span="2" /> - <SelectorItem title="Language" selector={$context.selector.language} --span="2" /> - <SelectorItem title="Rating" selector={$context.selector.rating} --span="2" /> - <SelectorItem title="Censorship" selector={$context.selector.censorship} --span="2" /> - <SelectorItem title="Direction" selector={$context.selector.direction} --span="2" /> - <SelectorItem title="Layout" selector={$context.selector.layout} --span="2" /> - <SelectorGroup title="Artists" selectors={$context.selector.artists} /> - <SelectorGroup title="Circles" selectors={$context.selector.circles} /> - <SelectorGroup title="Characters" selectors={$context.selector.characters} /> - <SelectorGroup title="Worlds" selectors={$context.selector.worlds} /> - <SelectorGroup title="Tags" selectors={$context.selector.tags} /> + <SelectorItem title="Title" selector={context.selector.title} /> + <SelectorItem title="Original Title" selector={context.selector.originalTitle} /> + <SelectorItem title="URL" selector={context.selector.url} /> + <SelectorItem title="Date" selector={context.selector.date} --span="2" /> + <SelectorItem title="Category" selector={context.selector.category} --span="2" /> + <SelectorItem title="Language" selector={context.selector.language} --span="2" /> + <SelectorItem title="Rating" selector={context.selector.rating} --span="2" /> + <SelectorItem title="Censorship" selector={context.selector.censorship} --span="2" /> + <SelectorItem title="Direction" selector={context.selector.direction} --span="2" /> + <SelectorItem title="Layout" selector={context.selector.layout} --span="2" /> + <SelectorGroup title="Artists" selectors={context.selector.artists} /> + <SelectorGroup title="Circles" selectors={context.selector.circles} /> + <SelectorGroup title="Characters" selectors={context.selector.characters} /> + <SelectorGroup title="Worlds" selectors={context.selector.worlds} /> + <SelectorGroup title="Tags" selectors={context.selector.tags} /> </div> <div class="flex flex-col gap-2"> <h2 class="border-b border-slate-700 text-base font-medium">Options</h2> diff --git a/frontend/src/lib/Scraper.ts b/frontend/src/lib/scraper/Scraper.svelte.ts index 4baf370..93e756b 100644 --- a/frontend/src/lib/Scraper.ts +++ b/frontend/src/lib/scraper/Scraper.svelte.ts @@ -20,24 +20,28 @@ import { RatingLabel } from '$lib/Enums'; import { getContext, setContext } from 'svelte'; -import { writable, type Writable } from 'svelte/store'; -interface ScraperContext { - scraper: string; - warnings: string[]; - selector?: ScrapedComicSelector; +class ScraperContext { + scraper?: string = $state(); + warnings: string[] = $state([]); + selector?: ScrapedComicSelector = $state(); + + reset = () => { + this.selector = undefined; + this.warnings = []; + }; } export function initScraperContext() { - return setContext<Writable<ScraperContext>>('scraper', writable({ scraper: '', warnings: [] })); + return setContext<ScraperContext>('scraper', new ScraperContext()); } export function getScraperContext() { - return getContext<Writable<ScraperContext>>('scraper'); + return getContext<ScraperContext>('scraper'); } export class Selector<T extends string> { - keep = true; + keep = $state(true); value: T; display: string | undefined; @@ -46,6 +50,10 @@ export class Selector<T extends string> { this.display = display; } + toggle = () => { + this.keep = !this.keep; + }; + toString() { return this.display ?? this.value; } @@ -121,7 +129,7 @@ export class ScrapedComicSelector { this.worlds = Selector.fromList(scraped.worlds, comic.worlds); } - hasData() { + pending() { return ( Object.values(this).filter((i) => { if (i === undefined) { @@ -134,7 +142,7 @@ export class ScrapedComicSelector { ); } - toInput(onMissing: OnMissing): UpsertComicInput { + input(onMissing: OnMissing): UpsertComicInput { return { title: keepItem(this.title), originalTitle: keepItem(this.originalTitle), diff --git a/frontend/src/lib/scraper/components/SelectorButton.svelte b/frontend/src/lib/scraper/components/SelectorButton.svelte index b786f89..48b2d66 100644 --- a/frontend/src/lib/scraper/components/SelectorButton.svelte +++ b/frontend/src/lib/scraper/components/SelectorButton.svelte @@ -1,19 +1,19 @@ <script lang="ts"> - import { Selector } from '$lib/Scraper'; + import { Selector } from '../Scraper.svelte'; - export let selector: Selector<string>; + let { selector }: { selector: Selector<string> } = $props(); </script> <button type="button" - class="ml-1 flex rounded-sm border-slate-700 bg-slate-900 hover:brightness-110" - on:click={() => (selector.keep = !selector.keep)} + class="ml-1 flex rounded-xs border-slate-700 bg-slate-900 hover:brightness-110" + onclick={selector.toggle} > <div class="flex self-center pl-1"> {#if selector.keep} - <span class="icon-base icon-[material-symbols--check] text-green-400" /> + <span class="icon-base icon-[material-symbols--check] text-green-400"></span> {:else} - <span class="icon-base icon-[material-symbols--close] text-red-400" /> + <span class="icon-base icon-[material-symbols--close] text-red-400"></span> {/if} </div> <p class:opacity-50={!selector.keep} class="p-1 text-left"> diff --git a/frontend/src/lib/scraper/components/SelectorGroup.svelte b/frontend/src/lib/scraper/components/SelectorGroup.svelte index ae7287a..5cf0cf0 100644 --- a/frontend/src/lib/scraper/components/SelectorGroup.svelte +++ b/frontend/src/lib/scraper/components/SelectorGroup.svelte @@ -1,9 +1,13 @@ <script lang="ts"> - import { Selector } from '$lib/Scraper'; + import { Selector } from '../Scraper.svelte'; import SelectorButton from './SelectorButton.svelte'; - export let title: string; - export let selectors: Selector<string>[]; + interface Props { + title: string; + selectors: Selector<string>[]; + } + + let { title, selectors = $bindable() }: Props = $props(); function invert() { for (let selector of selectors) { @@ -19,14 +23,16 @@ <h2>{title}</h2> <button type="button" - class="flex items-end opacity-0 brightness-75 transition-opacity hover:brightness-110 group-hover:opacity-100" - on:click={invert} + class="flex items-end opacity-75 brightness-75 transition-opacity hover:opacity-100 hover:brightness-110 focus-visible:opacity-100" + onclick={invert} title="Invert selection" + aria-label="Invert selection" > <span class="icon-xs icon-[material-symbols--compare-arrows]"></span> </button> </div> <div class="flex flex-wrap gap-y-1"> + <!-- eslint-disable-next-line svelte/require-each-key --> {#each selectors as selector} <SelectorButton {selector} /> {/each} diff --git a/frontend/src/lib/scraper/components/SelectorItem.svelte b/frontend/src/lib/scraper/components/SelectorItem.svelte index dd3f5b4..5beba50 100644 --- a/frontend/src/lib/scraper/components/SelectorItem.svelte +++ b/frontend/src/lib/scraper/components/SelectorItem.svelte @@ -1,9 +1,8 @@ <script lang="ts"> - import { Selector } from '$lib/Scraper'; + import { Selector } from '../Scraper.svelte'; import SelectorButton from './SelectorButton.svelte'; - export let title: string; - export let selector: Selector<string> | undefined; + let { title, selector }: { title: string; selector?: Selector<string> } = $props(); </script> {#if selector} diff --git a/frontend/src/lib/selection/Selectable.svelte b/frontend/src/lib/selection/Selectable.svelte index 48b6ac7..439d6b7 100644 --- a/frontend/src/lib/selection/Selectable.svelte +++ b/frontend/src/lib/selection/Selectable.svelte @@ -1,24 +1,48 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; + import type { Snippet } from 'svelte'; + import { getSelectionContext } from './Selection.svelte'; - export let id: number; - export let index: number; + interface SnippetProps { + onclick: (event: MouseEvent) => void; + onauxclick: (event: MouseEvent) => void; + selected: boolean; + } - export let edit: ((id: number) => void) | undefined = undefined; + interface Props { + id: number; + index: number; + onclick?: (id: number) => void; + onauxclick?: (id: number) => void; + children?: Snippet<[SnippetProps]>; + } - const selection = getSelectionContext(); + let { + id, + index, + onclick: onclick = undefined, + onauxclick = undefined, + children + }: Props = $props(); - $: selected = $selection.contains(id); + let selection = getSelectionContext(); - const handle = (event: MouseEvent) => { - if ($selection.active) { - $selection = $selection.update(index, event.shiftKey); + const click = (event: MouseEvent) => { + if (selection.active) { + selection.update(index, event.shiftKey); event.preventDefault(); - } else if (edit) { - edit(id); + } else if (event.ctrlKey && onauxclick) { + onauxclick(id); + } else if (onclick) { + onclick(id); event.preventDefault(); } }; + + const auxclick = (event: MouseEvent) => { + if (event.button === 1 && onauxclick) { + onauxclick(id); + } + }; </script> -<slot {handle} {selected} /> +{@render children?.({ onclick: click, onauxclick: auxclick, selected: selection.contains(id) })} diff --git a/frontend/src/lib/selection/Selection.svelte.ts b/frontend/src/lib/selection/Selection.svelte.ts new file mode 100644 index 0000000..dc294d0 --- /dev/null +++ b/frontend/src/lib/selection/Selection.svelte.ts @@ -0,0 +1,121 @@ +import { getContext, setContext } from 'svelte'; +import { SvelteSet } from 'svelte/reactivity'; +import { range } from '../Utils'; + +interface Selectable { + id: number; +} + +export function initSelectionContext<T extends Selectable>( + typename: string, + toName: (item: T) => string, + selectable?: (item: T) => boolean +) { + return setContext('selection', new ItemSelection(typename, toName, selectable)); +} + +export function getSelectionContext<T extends Selectable>() { + return getContext<ItemSelection<T>>('selection'); +} + +export class ItemSelection<T extends Selectable> { + active = $state(false); + view: T[] = $state([]); + + #ids = $state(new SvelteSet<number>()); + #masked = $derived(new SvelteSet([...this.#ids].filter((i) => this.#indexOf(i) >= 0))); + + typename: string; + #toName: (item: T) => string; + selectable: (item: T) => boolean; + + constructor( + typename: string, + toName: (item: T) => string, + selectable: (item: T) => boolean = () => true + ) { + this.typename = typename; + this.#toName = toName; + this.selectable = selectable; + } + + #indexOf = (id: number) => this.view.findIndex((v) => v.id === id); + + update = (index: number, shift: boolean) => { + const id = this.view[index].id; + + const selectableRange = (first: number, last: number) => + range(first, last) + .filter((i) => this.selectable(this.view[i])) + .map((i) => this.view[i].id); + + if (shift) { + const indices = this.indices; + + const first = indices.at(0); + const last = indices.at(-1); + + if (first === undefined || last === undefined) { + this.#ids.add(id); + } else if (index === first || index === last) { + this.#ids.clear(); + } else if (index > last) { + this.#ids = new SvelteSet([...this.#ids, ...selectableRange(last, index)]); + } else if (index < last) { + this.#ids = new SvelteSet([...this.#ids, ...selectableRange(index, last)]); + } + } else { + if (this.#ids.has(id)) { + this.#ids.delete(id); + } else { + this.#ids.add(id); + } + } + }; + + toggle = () => { + this.active = !this.active; + + if (!this.active) { + this.none(); + } + }; + + all = () => { + this.#ids = new SvelteSet(this.view.filter(this.selectable).map((i) => i.id)); + }; + + none = () => { + this.#ids.clear(); + this.#masked.clear(); + }; + + clear = () => { + this.active = false; + this.none(); + }; + + contains(id: number) { + return this.#masked.has(id); + } + + get ids() { + return [...this.#masked]; + } + + get size() { + return this.#masked.size; + } + + get indices() { + return [...this.#ids].map(this.#indexOf).filter((i) => i >= 0); + } + + get items() { + return this.indices.map((i) => this.view[i]); + } + + get names() { + return this.items.map(this.#toName); + } +} diff --git a/frontend/src/lib/selection/SelectionOverlay.svelte b/frontend/src/lib/selection/SelectionOverlay.svelte index 04ff382..e172e16 100644 --- a/frontend/src/lib/selection/SelectionOverlay.svelte +++ b/frontend/src/lib/selection/SelectionOverlay.svelte @@ -1,15 +1,19 @@ <script lang="ts"> - export let selected: boolean; - export let position: 'top' | 'right' | 'left' | 'bottom'; - export let centered = false; + interface Props { + selected: boolean; + position: 'top' | 'right' | 'left' | 'bottom'; + centered?: boolean; + } + + let { selected, position, centered = false }: Props = $props(); </script> {#if selected} <div class:items-center={centered} - class="{position} pointer-events-none absolute z-[1] flex bg-emerald-700/95" + class="{position} pointer-events-none absolute z-1 flex bg-emerald-700/95" > - <span class="icon-base icon-[material-symbols--check] text-[2rem]" /> + <span class="icon-[material-symbols--check] text-[2rem]"></span> </div> {/if} diff --git a/frontend/src/lib/statistics/Stat.svelte b/frontend/src/lib/statistics/Stat.svelte new file mode 100644 index 0000000..7e03e09 --- /dev/null +++ b/frontend/src/lib/statistics/Stat.svelte @@ -0,0 +1,31 @@ +<script lang="ts"> + interface Props { + title: string; + value: number; + precision?: number; + unit?: string; + } + + let { title, value, precision = 0, unit = '' }: Props = $props(); + + function format(value: number) { + if (Number.isNaN(value) || !Number.isFinite(value)) { + return 0; + } + + if (Number.isInteger(value)) { + return value; + } else { + return value.toFixed(precision); + } + } +</script> + +<div class="flex flex-col"> + <h2 class="text-lg font-medium"> + {title} + </h2> + <span class="text-base font-medium"> + {format(value)}{unit} + </span> +</div> diff --git a/frontend/src/lib/statistics/StatGroup.svelte b/frontend/src/lib/statistics/StatGroup.svelte new file mode 100644 index 0000000..91f8d3d --- /dev/null +++ b/frontend/src/lib/statistics/StatGroup.svelte @@ -0,0 +1,14 @@ +<script lang="ts"> + import type { Snippet } from 'svelte'; + + let { title, children }: { title: string; children?: Snippet } = $props(); +</script> + +<section + class="flex flex-col gap-2 rounded-sm bg-slate-900 p-2 font-medium shadow-md shadow-slate-950/30" +> + <h2 class="text-2xl">{title}</h2> + <div class="flex flex-row flex-wrap gap-10"> + {@render children?.()} + </div> +</section> diff --git a/frontend/src/lib/tabs/AddOverlay.svelte b/frontend/src/lib/tabs/AddOverlay.svelte index b1c98bf..4d5ec49 100644 --- a/frontend/src/lib/tabs/AddOverlay.svelte +++ b/frontend/src/lib/tabs/AddOverlay.svelte @@ -1,7 +1,7 @@ <script lang="ts"> - import { updateComics } from '$gql/Mutations'; import { UpdateMode } from '$gql/graphql'; - import { getSelectionContext } from '$lib/Selection'; + import { updateComics } from '$gql/Mutations'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import { toastFinally } from '$lib/Toasts'; import { fadeDefault } from '$lib/Transitions'; import { getContextClient } from '@urql/svelte'; @@ -10,27 +10,30 @@ const client = getContextClient(); const selection = getSelectionContext(); - export let id: number; + let { id }: { id: number } = $props(); + + function onclick(event: MouseEvent) { + event.preventDefault(); - function addPages() { updateComics(client, { ids: id, - input: { pages: { ids: $selection.ids, options: { mode: UpdateMode.Add } } } + input: { pages: { ids: selection.ids, options: { mode: UpdateMode.Add } } } }) - .then(() => ($selection = $selection.none())) + .then(() => selection.none()) .catch(toastFinally); } </script> -{#if $selection.size > 0} +{#if selection.size > 0} <div class="absolute left-1 top-1" transition:fade={fadeDefault}> <button type="button" - class="btn-blue rounded-full shadow-sm shadow-black" + class="btn-blue rounded-full shadow-xs shadow-black" title="Add to this comic" - on:click|preventDefault={addPages} + aria-label="Add to this comic" + {onclick} > - <span class="icon-base icon-[material-symbols--note-add]" /> + <span class="icon-base icon-[material-symbols--note-add]"></span> </button> </div> {/if} diff --git a/frontend/src/lib/tabs/ArchiveDelete.svelte b/frontend/src/lib/tabs/ArchiveDelete.svelte index b0e3c58..d2b2465 100644 --- a/frontend/src/lib/tabs/ArchiveDelete.svelte +++ b/frontend/src/lib/tabs/ArchiveDelete.svelte @@ -9,7 +9,7 @@ const client = getContextClient(); - export let archive: FullArchiveFragment; + let { archive }: { archive: FullArchiveFragment } = $props(); function deleteArchive() { confirmDeletion('Archive', archive.name, () => { @@ -21,22 +21,13 @@ </script> <div class="flex flex-col gap-2"> - <div> - <p> - Deleting this archive will remove the - <span class="cursor-help font-medium underline" title={archive.path}>archive file</span> on disk. - </p> - {#if archive.comics.length > 0} - <p>The following comics will also be deleted:</p> - <ul class="ml-8 list-disc"> - {#each archive.comics as comic} - <li><a href="/comics/{comic.id}" class="underline">{comic.title}</a></li> - {/each} - </ul> - {/if} - <p class="mt-2 font-medium">This action is irrevocable.</p> - </div> + <p> + Deleting this archive will remove all of its comics as well as the + <span class="cursor-help font-medium underline" title={archive.path}>archive file</span> on + disk. + <span class="font-medium">This action is irrevocable.</span> + </p> <div class="flex"> - <DeleteButton prominent on:click={deleteArchive} /> + <DeleteButton prominent onclick={deleteArchive} /> </div> </div> diff --git a/frontend/src/lib/tabs/ArchiveDetails.svelte b/frontend/src/lib/tabs/ArchiveDetails.svelte index 9554557..c1ad68e 100644 --- a/frontend/src/lib/tabs/ArchiveDetails.svelte +++ b/frontend/src/lib/tabs/ArchiveDetails.svelte @@ -1,14 +1,13 @@ <script lang="ts"> import type { FullArchiveFragment } from '$gql/graphql'; import { formatListSize, joinText } from '$lib/Utils'; - import Card, { comicCard } from '$lib/components/Card.svelte'; - import ComicPills from '$lib/pills/ComicPills.svelte'; + import ComicCard from '$lib/components/ComicCard.svelte'; import { formatDistance, formatISO9075 } from 'date-fns'; import { filesize } from 'filesize'; import Header from './DetailsHeader.svelte'; import Section from './DetailsSection.svelte'; - export let archive: FullArchiveFragment; + let { archive }: { archive: FullArchiveFragment } = $props(); const now = Date.now(); const modifiedDate = new Date(archive.mtime); @@ -39,10 +38,8 @@ <div class="flex flex-col gap-1"> <h2 class="text-base font-medium">Comics</h2> <div class="flex shrink-0 flex-col gap-4"> - {#each archive.comics as comic} - <Card compact {...comicCard(comic)}> - <ComicPills {comic} /> - </Card> + {#each archive.comics as comic (comic.id)} + <ComicCard compact {comic} /> {/each} </div> </div> diff --git a/frontend/src/lib/tabs/ArchiveEdit.svelte b/frontend/src/lib/tabs/ArchiveEdit.svelte index 80efaed..c6ea684 100644 --- a/frontend/src/lib/tabs/ArchiveEdit.svelte +++ b/frontend/src/lib/tabs/ArchiveEdit.svelte @@ -1,12 +1,11 @@ <script lang="ts"> import { addComic, updateArchives } from '$gql/Mutations'; import { type FullArchiveFragment } from '$gql/graphql'; - import { getSelectionContext } from '$lib/Selection'; import { toastFinally } from '$lib/Toasts'; import AddButton from '$lib/components/AddButton.svelte'; - import Card, { comicCard } from '$lib/components/Card.svelte'; + import ComicCard from '$lib/components/ComicCard.svelte'; import OrganizedButton from '$lib/components/OrganizedButton.svelte'; - import ComicPills from '$lib/pills/ComicPills.svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionControls from '$lib/toolbar/SelectionControls.svelte'; import { getContextClient } from '@urql/svelte'; import AddOverlay from './AddOverlay.svelte'; @@ -14,23 +13,23 @@ const client = getContextClient(); const selection = getSelectionContext(); - export let archive: FullArchiveFragment; + let { archive }: { archive: FullArchiveFragment } = $props(); function addNew() { addComic(client, { input: { archive: { id: archive.id }, title: archive.name, - pages: { ids: $selection.ids }, - cover: { id: archive.pages[$selection.indices.toSorted((a, b) => a - b)[0]].id } + pages: { ids: selection.ids }, + cover: { id: archive.pages[selection.indices.toSorted((a, b) => a - b)[0]].id } } }) .then((mutatation) => { const data = mutatation.addComic; if (data.__typename === 'AddComicSuccess' && !data.archivePagesRemaining) { - $selection = $selection.clear(); + selection.clear(); } else { - $selection = $selection.none(); + selection.none(); } }) .catch(toastFinally); @@ -46,21 +45,22 @@ <div class="flex flex-col gap-4"> <div class="flex gap-2 text-sm"> <SelectionControls page> - <AddButton title="Add Comic from selected" on:click={addNew} /> + <AddButton title="Add Comic from selected" onclick={addNew} /> </SelectionControls> - <div class="grow" /> - <OrganizedButton organized={archive.organized} on:click={toggleOrganized} /> + <div class="grow"></div> + <OrganizedButton organized={archive.organized} onclick={toggleOrganized} /> </div> {#if archive.comics.length > 0} <div class="flex flex-col gap-1"> <h2 class="text-base font-medium">Comics</h2> <div class="flex shrink-0 flex-col gap-4"> - {#each archive.comics as comic} - <Card compact {...comicCard(comic)}> - <AddOverlay slot="overlay" id={comic.id} /> - <ComicPills {comic} /> - </Card> + {#each archive.comics as comic (comic.id)} + <ComicCard compact {comic}> + {#snippet overlay()} + <AddOverlay id={comic.id} /> + {/snippet} + </ComicCard> {/each} </div> </div> diff --git a/frontend/src/lib/tabs/ComicDelete.svelte b/frontend/src/lib/tabs/ComicDelete.svelte index a10f6b2..93fa106 100644 --- a/frontend/src/lib/tabs/ComicDelete.svelte +++ b/frontend/src/lib/tabs/ComicDelete.svelte @@ -9,9 +9,9 @@ const client = getContextClient(); - export let comic: FullComicFragment; + let { comic }: { comic: FullComicFragment } = $props(); - function deleteComic() { + function onclick() { confirmDeletion('Comic', comic.title, () => { deleteComics(client, { ids: comic.id }) .then(() => goto('/comics/')) @@ -21,14 +21,13 @@ </script> <div class="flex flex-col gap-2"> - <div> - <p> - Deleting this comic will make all of its pages available again for allocation. All of its - metadata will be lost. - </p> - <p class="mt-2 font-medium">This action is irrevocable.</p> - </div> + <p> + Deleting this comic will make all of its pages available again for allocation. All of its + metadata will be lost. + <span class="font-medium">This action is irrevocable.</span> + </p> + <div class="flex"> - <DeleteButton prominent on:click={deleteComic} /> + <DeleteButton prominent {onclick} /> </div> </div> diff --git a/frontend/src/lib/tabs/ComicDetails.svelte b/frontend/src/lib/tabs/ComicDetails.svelte index 0a131af..3f9090e 100644 --- a/frontend/src/lib/tabs/ComicDetails.svelte +++ b/frontend/src/lib/tabs/ComicDetails.svelte @@ -1,15 +1,15 @@ <script lang="ts"> - import type { ComicFilter, FullComicFragment } from '$gql/graphql'; + import { type ComicFilter, type FullComicFragment } from '$gql/graphql'; import { CategoryLabel, CensorshipLabel, LanguageLabel, RatingLabel } from '$lib/Enums'; import { href } from '$lib/Navigation'; import { formatListSize, joinText } from '$lib/Utils'; - import AssociationPill from '$lib/pills/AssociationPill.svelte'; + import Pill from '$lib/pills/Pill.svelte'; import TagPill from '$lib/pills/TagPill.svelte'; import { formatDistance, formatISO9075 } from 'date-fns'; import Header from './DetailsHeader.svelte'; import Section from './DetailsSection.svelte'; - export let comic: FullComicFragment; + let { comic }: { comic: FullComicFragment } = $props(); const now = Date.now(); const updatedDate = new Date(comic.updatedAt); @@ -27,13 +27,13 @@ <div class="flex flex-col gap-4 text-sm"> <Header {title}> - {#if comic.url} - <a href={comic.url} target="_blank" rel="noreferrer" class="btn-slate" title="Open URL"> - <span class="icon-base icon-[material-symbols--link]" /> - </a> - {/if} - <a href={`/archives/${comic.archive.id}`} class="btn-slate" title="Go to Archive"> - <span class="icon-base icon-[material-symbols--folder-zip]" /> + <a + href={`/archives/${comic.archive.id}`} + class="btn-slate" + title="Go to Archive" + aria-label="Go to Archive" + > + <span class="icon-base icon-[material-symbols--folder-zip]"></span> </a> </Header> @@ -73,11 +73,21 @@ </Section> </div> + {#if comic.url} + <Section title="URL"> + <a + class="ellipsis-nowrap transition-colors hover:text-white hover:underline" + rel="noreferrer" + href={comic.url}>{comic.url}</a + > + </Section> + {/if} + {#if comic.artists.length} <Section title="Artists"> {#each comic.artists as { id, name } (id)} <a href={filterFor('artists', id)}> - <AssociationPill {name} type="artist" /> + <Pill highlight {name} style="artist" /> </a> {/each} </Section> @@ -86,7 +96,7 @@ <Section title="Circles"> {#each comic.circles as { id, name } (id)} <a href={filterFor('circles', id)}> - <AssociationPill {name} type="circle" /> + <Pill highlight {name} style="circle" /> </a> {/each} </Section> @@ -95,7 +105,7 @@ <Section title="Characters"> {#each comic.characters as { id, name } (id)} <a href={filterFor('characters', id)}> - <AssociationPill {name} type="character" /> + <Pill highlight {name} style="character" /> </a> {/each} </Section> @@ -104,7 +114,7 @@ <Section title="Worlds"> {#each comic.worlds as { id, name } (id)} <a href={filterFor('worlds', id)}> - <AssociationPill {name} type="world" /> + <Pill highlight {name} style="world" /> </a> {/each} </Section> @@ -113,7 +123,7 @@ <Section title="Tags"> {#each comic.tags as { id, name, description } (id)} <a href={filterFor('tags', id)}> - <TagPill {name} {description} /> + <TagPill highlight {name} {description} /> </a> {/each} </Section> diff --git a/frontend/src/lib/tabs/DetailsHeader.svelte b/frontend/src/lib/tabs/DetailsHeader.svelte index f980f75..ee5fa23 100644 --- a/frontend/src/lib/tabs/DetailsHeader.svelte +++ b/frontend/src/lib/tabs/DetailsHeader.svelte @@ -1,5 +1,7 @@ <script lang="ts"> - export let title: string; + import type { Snippet } from 'svelte'; + + let { title, children }: { title: string; children?: Snippet } = $props(); </script> <div class="flex items-center gap-2"> @@ -7,5 +9,5 @@ {title} </h2> <div class="grow"></div> - <slot /> + {@render children?.()} </div> diff --git a/frontend/src/lib/tabs/DetailsSection.svelte b/frontend/src/lib/tabs/DetailsSection.svelte index 9a6ad51..5514aa3 100644 --- a/frontend/src/lib/tabs/DetailsSection.svelte +++ b/frontend/src/lib/tabs/DetailsSection.svelte @@ -1,10 +1,12 @@ <script lang="ts"> - export let title: string; + import type { Snippet } from 'svelte'; + + let { title, children }: { title: string; children?: Snippet } = $props(); </script> <section class="flex flex-col gap-1"> <h2 class="text-base font-medium">{title}</h2> <div class="flex flex-wrap gap-1 text-gray-300"> - <slot /> + {@render children?.()} </div> </section> diff --git a/frontend/src/lib/tabs/Tab.svelte b/frontend/src/lib/tabs/Tab.svelte index 0a6be57..f8dc67c 100644 --- a/frontend/src/lib/tabs/Tab.svelte +++ b/frontend/src/lib/tabs/Tab.svelte @@ -1,14 +1,28 @@ <script lang="ts"> - import { getTabContext } from '$lib/Tabs'; import { fadeDefault } from '$lib/Transitions'; + import type { Snippet } from 'svelte'; import { fade } from 'svelte/transition'; + import { getTabContext } from './Tabs.svelte'; + + interface Props { + id: string; + title: string; + initial?: boolean; + children: Snippet; + } + + let { id, title, initial = false, children }: Props = $props(); const context = getTabContext(); - export let id: string; + + context.tabs = { ...context.tabs, [id]: { title } }; + if (initial) { + context.current = id; + } </script> -{#if $context.current === id} - <div class="h-full overflow-auto py-2 pe-3" in:fade={fadeDefault}> - <slot /> +{#if context.current === id} + <div class="h-full overflow-auto py-2 pe-3 ps-1" in:fade={fadeDefault}> + {@render children?.()} </div> {/if} diff --git a/frontend/src/lib/tabs/Tabs.svelte b/frontend/src/lib/tabs/Tabs.svelte index 09cdbdd..59b3220 100644 --- a/frontend/src/lib/tabs/Tabs.svelte +++ b/frontend/src/lib/tabs/Tabs.svelte @@ -1,28 +1,50 @@ +<script lang="ts" module> + import { getContext, setContext } from 'svelte'; + + type Tab = string; + type Tabs = Record<Tab, { title: string }>; + + class TabContext { + tabs: Tabs = $state({}); + current: Tab = $state(''); + } + + export function getTabContext() { + return getContext<TabContext>('tabs'); + } + + function initTabContext() { + return setContext('tabs', new TabContext()); + } +</script> + <script lang="ts"> - import { getTabContext } from '$lib/Tabs'; import { fadeFast } from '$lib/Transitions'; + import type { Snippet } from 'svelte'; import { fade } from 'svelte/transition'; - const context = getTabContext(); + let { badges = {}, children }: { badges?: Record<Tab, boolean>; children?: Snippet } = $props(); + + const context = initTabContext(); </script> <div class="flex h-full max-h-full flex-col"> <nav> - <ul class="me-3 flex border-b-2 border-slate-700 text-sm"> - {#each Object.entries($context.tabs) as [id, { title, badge }]} + <ul class="ms-1 me-3 flex border-b-2 border-slate-700 text-sm"> + {#each Object.entries(context.tabs) as [id, { title }] (id)} <li class="-mb-0.5"> <button type="button" - class:active={$context.current === id} - class="relative flex gap-1 p-1 px-3 hover:border-b-2 hover:border-slate-200" - on:click={() => ($context.current = id)} + class:active={context.current === id} + class="relative flex gap-1 p-1 px-3 hover:border-b-2 hover:border-slate-200 focus-visible:border-b-2 focus-visible:!border-slate-200 focus-visible:outline-hidden [&.active]:border-b-2 [&.active]:border-indigo-500" + onclick={() => (context.current = id)} > - {#if badge} + {#if badges[id]} <div - class="absolute right-0 top-1 h-2 w-2 rounded-full bg-emerald-400" + class="absolute top-1 right-0 h-2 w-2 rounded-full bg-emerald-400" title="There are pending changes" transition:fade={fadeFast} - /> + ></div> {/if} <span>{title}</span> </button> @@ -30,11 +52,5 @@ {/each} </ul> </nav> - <slot /> + {@render children?.()} </div> - -<style lang="postcss"> - button.active { - @apply border-b-2 border-indigo-500; - } -</style> diff --git a/frontend/src/lib/toolbar/DeleteSelection.svelte b/frontend/src/lib/toolbar/DeleteSelection.svelte index 7459a87..7b37313 100644 --- a/frontend/src/lib/toolbar/DeleteSelection.svelte +++ b/frontend/src/lib/toolbar/DeleteSelection.svelte @@ -1,26 +1,28 @@ <script lang="ts"> import type { DeleteMutation } from '$gql/Mutations'; - import { getSelectionContext } from '$lib/Selection'; + import DeleteButton from '$lib/components/DeleteButton.svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import { toastFinally } from '$lib/Toasts'; import { confirmDeletion } from '$lib/Utils'; - import DeleteButton from '$lib/components/DeleteButton.svelte'; import { getContextClient } from '@urql/svelte'; const client = getContextClient(); - const selection = getSelectionContext(); - export let mutation: DeleteMutation; - export let warning: string | undefined = undefined; + interface Props { + mutation: DeleteMutation; + warning?: string; + } + + let { mutation, warning = undefined }: Props = $props(); + let selection = getSelectionContext(); - function remove() { + function onclick() { const mutate = () => { - mutation(client, { ids: $selection.ids }) - .then(() => ($selection = $selection.clear())) - .catch(toastFinally); + mutation(client, { ids: selection.ids }).then(selection.clear).catch(toastFinally); }; - confirmDeletion($selection.typename, $selection.names, mutate, warning); + confirmDeletion(selection.typename, selection.names, mutate, warning); } </script> -<DeleteButton on:click={remove} /> +<DeleteButton {onclick} /> diff --git a/frontend/src/lib/toolbar/EditSelection.svelte b/frontend/src/lib/toolbar/EditSelection.svelte index 50e6656..1803ed4 100644 --- a/frontend/src/lib/toolbar/EditSelection.svelte +++ b/frontend/src/lib/toolbar/EditSelection.svelte @@ -1,20 +1,19 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import { accelerator } from '$lib/Shortcuts'; - import type { SvelteComponent } from 'svelte'; - import { openModal } from 'svelte-modals'; + import { toastFinally } from '$lib/Toasts'; + import { modals, type ModalComponent, type ModalProps } from 'svelte-modals'; const selection = getSelectionContext(); - export let dialog: typeof SvelteComponent<{ - isOpen: boolean; + interface DialogProps extends ModalProps { ids: number[]; - }>; + } + + let { dialog }: { dialog: ModalComponent<DialogProps> } = $props(); function edit() { - openModal(dialog, { - ids: $selection.ids - }); + modals.open(dialog, { ids: selection.ids }).catch(toastFinally); } </script> @@ -22,8 +21,9 @@ type="button" class="btn-slate hover:bg-blue-700" title="Edit selection" - on:click={edit} + aria-label="Edit selection" + onclick={edit} use:accelerator={'e'} > - <span class="icon-base icon-[material-symbols--edit]" /> + <span class="icon-base icon-[material-symbols--edit]"></span> </button> diff --git a/frontend/src/lib/toolbar/FilterBookmarked.svelte b/frontend/src/lib/toolbar/FilterBookmarked.svelte index bcbe295..44895c6 100644 --- a/frontend/src/lib/toolbar/FilterBookmarked.svelte +++ b/frontend/src/lib/toolbar/FilterBookmarked.svelte @@ -1,23 +1,24 @@ <script lang="ts"> - import { page } from '$app/stores'; - import { ComicFilterContext, cycleBooleanFilter, getFilterContext } from '$lib/Filter'; + import { page } from '$app/state'; + import { cycleBooleanFilter, type ComicFilterContext } from '$lib/Filter.svelte'; + import { accelerator } from '$lib/Shortcuts'; import Bookmark from '$lib/icons/Bookmark.svelte'; - const filter = getFilterContext<ComicFilterContext>(); - $: bookmarked = $filter.include.controls.bookmarked.value; + let { filter }: { filter: ComicFilterContext } = $props(); + let bookmarked = $derived(filter.include.bookmarked.value); const toggle = () => { - $filter.include.controls.bookmarked.value = cycleBooleanFilter(bookmarked, false); - $filter.apply($page.url.searchParams); + filter.include.bookmarked.value = cycleBooleanFilter(bookmarked, false); + filter.apply(page.url.searchParams); }; </script> <button class:toggled={bookmarked} class="btn-slate" - title="Filter bookmarked" - on:click={toggle} + title="Toggle bookmarks" + onclick={toggle} use:accelerator={'b'} > <Bookmark {bookmarked} /> diff --git a/frontend/src/lib/toolbar/FilterFavourites.svelte b/frontend/src/lib/toolbar/FilterFavourites.svelte index 6591cef..cdb497c 100644 --- a/frontend/src/lib/toolbar/FilterFavourites.svelte +++ b/frontend/src/lib/toolbar/FilterFavourites.svelte @@ -1,23 +1,23 @@ <script lang="ts"> - import { page } from '$app/stores'; - import { ComicFilterContext, cycleBooleanFilter, getFilterContext } from '$lib/Filter'; + import { page } from '$app/state'; + import { ComicFilterContext, cycleBooleanFilter } from '$lib/Filter.svelte'; import { accelerator } from '$lib/Shortcuts'; import Star from '$lib/icons/Star.svelte'; - const filter = getFilterContext<ComicFilterContext>(); - $: favourite = $filter.include.controls.favourite.value; + let { filter }: { filter: ComicFilterContext } = $props(); + let favourite = $derived(filter.include.favourite.value); const toggle = () => { - $filter.include.controls.favourite.value = cycleBooleanFilter(favourite, false); - $filter.apply($page.url.searchParams); + filter.include.favourite.value = cycleBooleanFilter(favourite, false); + filter.apply(page.url.searchParams); }; </script> <button class:toggled={favourite} class="btn-slate" - title="Filter favourites" - on:click={toggle} + title="Toggle favourites" + onclick={toggle} use:accelerator={'f'} > <Star {favourite} /> diff --git a/frontend/src/lib/toolbar/FilterOrganized.svelte b/frontend/src/lib/toolbar/FilterOrganized.svelte index 754e663..d01a4f0 100644 --- a/frontend/src/lib/toolbar/FilterOrganized.svelte +++ b/frontend/src/lib/toolbar/FilterOrganized.svelte @@ -1,20 +1,20 @@ <script lang="ts"> - import { page } from '$app/stores'; + import { page } from '$app/state'; import { ArchiveFilterContext, - ComicFilterContext, cycleBooleanFilter, - getFilterContext - } from '$lib/Filter'; + type ComicFilterContext + } from '$lib/Filter.svelte'; + import { accelerator } from '$lib/Shortcuts'; import Organized from '$lib/icons/Organized.svelte'; - const filter = getFilterContext<ArchiveFilterContext | ComicFilterContext>(); - $: organized = $filter.include.controls.organized.value; + let { filter }: { filter: ComicFilterContext | ArchiveFilterContext } = $props(); + let organized = $derived(filter.include.organized.value); const toggle = () => { - $filter.include.controls.organized.value = cycleBooleanFilter(organized); - $filter.apply($page.url.searchParams); + filter.include.organized.value = cycleBooleanFilter(organized); + filter.apply(page.url.searchParams); }; </script> @@ -22,9 +22,9 @@ type="button" class:toggled={organized !== undefined} class="btn-slate" - title="Filter organized" - on:click={toggle} - use:accelerator={'o'} + title="Toggle organized" + onclick={toggle} + use:accelerator={'z'} > <Organized tristate {organized} /> </button> diff --git a/frontend/src/lib/toolbar/FilterOrphaned.svelte b/frontend/src/lib/toolbar/FilterOrphaned.svelte new file mode 100644 index 0000000..7e79be1 --- /dev/null +++ b/frontend/src/lib/toolbar/FilterOrphaned.svelte @@ -0,0 +1,24 @@ +<script lang="ts"> + import { page } from '$app/state'; + import { BasicFilterContext, NamespaceFilterContext } from '$lib/Filter.svelte'; + import { accelerator } from '$lib/Shortcuts'; + import Orphan from '$lib/icons/Orphan.svelte'; + + let { filter }: { filter: BasicFilterContext | NamespaceFilterContext } = $props(); + let orphaned = $derived(filter.include.orphan.value); + + const toggle = () => { + filter.include.orphan.value = !orphaned; + filter.apply(page.url.searchParams); + }; +</script> + +<button + class:toggled={orphaned} + class="btn-slate" + title="Filter orphaned" + onclick={toggle} + use:accelerator={'r'} +> + <Orphan {orphaned} /> +</button> diff --git a/frontend/src/lib/toolbar/MarkBookmark.svelte b/frontend/src/lib/toolbar/MarkBookmark.svelte index 792b84f..e9693fc 100644 --- a/frontend/src/lib/toolbar/MarkBookmark.svelte +++ b/frontend/src/lib/toolbar/MarkBookmark.svelte @@ -1,27 +1,25 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; - import { toastFinally } from '$lib/Toasts'; + import type { MutationWith } from '$gql/Utils'; import Bookmark from '$lib/icons/Bookmark.svelte'; - import { Client, getContextClient } from '@urql/svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; const client = getContextClient(); const selection = getSelectionContext(); - export let mutation: ( - client: Client, - args: { ids: number[]; input: { bookmarked: boolean } } - ) => Promise<unknown>; + let { mutation }: { mutation: MutationWith<{ bookmarked: boolean }> } = $props(); function mutate(bookmarked: boolean) { - mutation(client, { ids: $selection.ids, input: { bookmarked } }).catch(toastFinally); + mutation(client, { ids: selection.ids, input: { bookmarked } }).catch(toastFinally); } </script> -<button type="button" class="btn-slate flex justify-start gap-1" on:click={() => mutate(true)}> +<button type="button" class="btn-slate justify-start gap-1" onclick={() => mutate(true)}> <Bookmark bookmarked={true} /> <span>Bookmark</span> </button> -<button type="button" class="btn-slate flex justify-start gap-1" on:click={() => mutate(false)}> +<button type="button" class="btn-slate justify-start gap-1" onclick={() => mutate(false)}> <Bookmark bookmarked={false} /> <span>Unbookmark</span> </button> diff --git a/frontend/src/lib/toolbar/MarkFavourite.svelte b/frontend/src/lib/toolbar/MarkFavourite.svelte index 42eaa39..1af5d60 100644 --- a/frontend/src/lib/toolbar/MarkFavourite.svelte +++ b/frontend/src/lib/toolbar/MarkFavourite.svelte @@ -1,27 +1,25 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; - import { toastFinally } from '$lib/Toasts'; + import type { MutationWith } from '$gql/Utils'; import Star from '$lib/icons/Star.svelte'; - import { Client, getContextClient } from '@urql/svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; const client = getContextClient(); const selection = getSelectionContext(); - export let mutation: ( - client: Client, - args: { ids: number[]; input: { favourite: boolean } } - ) => Promise<unknown>; + let { mutation }: { mutation: MutationWith<{ favourite: boolean }> } = $props(); function mutate(favourite: boolean) { - mutation(client, { ids: $selection.ids, input: { favourite } }).catch(toastFinally); + mutation(client, { ids: selection.ids, input: { favourite } }).catch(toastFinally); } </script> -<button type="button" class="btn-slate justify-start gap-1" on:click={() => mutate(true)}> +<button type="button" class="btn-slate justify-start gap-1" onclick={() => mutate(true)}> <Star favourite={true} /> <span>Favourite</span> </button> -<button type="button" class="btn-slate justify-start gap-1" on:click={() => mutate(false)}> +<button type="button" class="btn-slate justify-start gap-1" onclick={() => mutate(false)}> <Star favourite={false} /> <span>Unfavourite</span> </button> diff --git a/frontend/src/lib/toolbar/MarkOrganized.svelte b/frontend/src/lib/toolbar/MarkOrganized.svelte index 4dc3a83..c526393 100644 --- a/frontend/src/lib/toolbar/MarkOrganized.svelte +++ b/frontend/src/lib/toolbar/MarkOrganized.svelte @@ -1,27 +1,25 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; - import { toastFinally } from '$lib/Toasts'; + import type { MutationWith } from '$gql/Utils'; import Organized from '$lib/icons/Organized.svelte'; - import { Client, getContextClient } from '@urql/svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; + import { toastFinally } from '$lib/Toasts'; + import { getContextClient } from '@urql/svelte'; const client = getContextClient(); const selection = getSelectionContext(); - export let mutation: ( - client: Client, - args: { ids: number[]; input: { organized: boolean } } - ) => Promise<unknown>; + let { mutation }: { mutation: MutationWith<{ organized: boolean }> } = $props(); function mutate(organized: boolean) { - mutation(client, { ids: $selection.ids, input: { organized } }).catch(toastFinally); + mutation(client, { ids: selection.ids, input: { organized } }).catch(toastFinally); } </script> -<button type="button" class="btn-slate flex justify-start gap-1" on:click={() => mutate(true)}> +<button type="button" class="btn-slate justify-start gap-1" onclick={() => mutate(true)}> <Organized tristate organized={true} /> <span>Organized</span> </button> -<button type="button" class="btn-slate flex justify-start gap-1" on:click={() => mutate(false)}> +<button type="button" class="btn-slate justify-start gap-1" onclick={() => mutate(false)}> <Organized dim tristate organized={false} /> <span>Unorganized</span> </button> diff --git a/frontend/src/lib/toolbar/MarkSelection.svelte b/frontend/src/lib/toolbar/MarkSelection.svelte index 27eb2c7..8985369 100644 --- a/frontend/src/lib/toolbar/MarkSelection.svelte +++ b/frontend/src/lib/toolbar/MarkSelection.svelte @@ -1,24 +1,23 @@ <script lang="ts"> import Dropdown from '$lib/components/Dropdown.svelte'; + import type { Snippet } from 'svelte'; - let visible = false; - let button: HTMLElement; + let { children }: { children: Snippet } = $props(); </script> -<div class="relative"> - <button - type="button" - class="btn-slate rounded-inherit relative hover:bg-blue-700 [&:not(:only-child)]:bg-blue-700" - title="Set flag..." - bind:this={button} - on:click={() => (visible = !visible)} - > - <span class="icon-base icon-[material-symbols--flag] pointer-events-none" /> - </button> - - <Dropdown parent={button} bind:visible> - <div class="grid grid-cols-[min-content_min-content] gap-1"> - <slot /> - </div> - </Dropdown> -</div> +<Dropdown> + {#snippet button(onclick)} + <button + type="button" + class="btn-slate rounded-inherit relative not-only:bg-blue-700 hover:bg-blue-700" + title="Set flag..." + aria-label="Set flag..." + {onclick} + > + <span class="icon-base icon-[material-symbols--flag] pointer-events-none"></span> + </button> + {/snippet} + <div class="grid grid-cols-[min-content_min-content] gap-1"> + {@render children?.()} + </div> +</Dropdown> diff --git a/frontend/src/lib/toolbar/Search.svelte b/frontend/src/lib/toolbar/Search.svelte index f033258..d5971bc 100644 --- a/frontend/src/lib/toolbar/Search.svelte +++ b/frontend/src/lib/toolbar/Search.svelte @@ -1,13 +1,15 @@ <script lang="ts"> - import { page } from '$app/stores'; + import { page } from '$app/state'; import { debounce } from '$lib/Actions'; - import { BasicFilterContext, getFilterContext } from '$lib/Filter'; import { accelerator } from '$lib/Shortcuts'; - const filter = getFilterContext<BasicFilterContext>(); + interface Props { + name: string; + field: string; + filter: { apply: (params: URLSearchParams) => void }; + } - export let name: string; - export let field: string; + let { name, field = $bindable(), filter }: Props = $props(); </script> <input @@ -16,6 +18,6 @@ class="btn-slate w-min" placeholder="Search {name}..." bind:value={field} - use:debounce={{ callback: () => $filter.apply($page.url.searchParams) }} - use:accelerator={'F'} + use:debounce={{ callback: () => filter.apply(page.url.searchParams) }} + use:accelerator={'q'} /> diff --git a/frontend/src/lib/toolbar/SelectItems.svelte b/frontend/src/lib/toolbar/SelectItems.svelte index 7ff339e..ce8045e 100644 --- a/frontend/src/lib/toolbar/SelectItems.svelte +++ b/frontend/src/lib/toolbar/SelectItems.svelte @@ -1,19 +1,20 @@ <script lang="ts"> - import { page } from '$app/stores'; - import { getPaginationContext } from '$lib/Pagination'; + import { page } from '$app/state'; + import { navigate, type PaginationData } from '$lib/Navigation'; - const pagination = getPaginationContext(); + let { pagination }: { pagination: PaginationData } = $props(); - $: values = new Set([24, 48, 72, 90, 120, 150, 180, $pagination.items].sort((a, b) => a - b)); + let values = $derived( + new Set([24, 48, 72, 90, 120, 150, 180, pagination.items].sort((a, b) => a - b)) + ); + + function onchange(e: Event & { currentTarget: EventTarget & HTMLSelectElement }) { + navigate({ pagination: { items: +e.currentTarget.value } }, page.url.searchParams); + } </script> -<select - class="btn-slate" - bind:value={$pagination.items} - on:change={() => $pagination.apply($page.url.searchParams)} - title="Limit displayed items to..." -> - {#each values as value} +<select class="btn-slate" value={pagination.items} {onchange} title="Limit displayed items to..."> + {#each values as value (value)} <option {value}>{value}</option> {/each} </select> diff --git a/frontend/src/lib/toolbar/SelectSort.svelte b/frontend/src/lib/toolbar/SelectSort.svelte index fdcb057..cbcbd0e 100644 --- a/frontend/src/lib/toolbar/SelectSort.svelte +++ b/frontend/src/lib/toolbar/SelectSort.svelte @@ -1,60 +1,68 @@ <script lang="ts"> - import { page } from '$app/stores'; + import { page } from '$app/state'; import { SortDirection } from '$gql/graphql'; - - import { getSortContext } from '$lib/Sort'; + import { navigate, type SortData } from '$lib/Navigation'; import { slideXFast } from '$lib/Transitions'; import { getRandomInt } from '$lib/Utils'; import { slide } from 'svelte/transition'; - const sort = getSortContext(); + let { sort, labels }: { sort: SortData<string>; labels: Record<string, string> } = $props(); + + function apply(sort: SortData<string>) { + navigate({ sort }, page.url.searchParams); + } function toggle() { - if ($sort.direction === SortDirection.Ascending) { - $sort.direction = SortDirection.Descending; + if (sort.direction === SortDirection.Ascending) { + apply({ ...sort, direction: SortDirection.Descending }); } else { - $sort.direction = SortDirection.Ascending; + apply({ ...sort, direction: SortDirection.Ascending }); } + } - apply(); + function newSeed() { + return getRandomInt(0, 1000000000); } - function apply() { - if ($sort.on === 'RANDOM' && $sort.seed === undefined) { - $sort.seed = getRandomInt(0, 1000000000); - } - $sort.apply($page.url.searchParams); + function shuffle() { + apply({ ...sort, seed: newSeed() }); } - function reshuffle() { - $sort.seed = undefined; - apply(); + function onchange(e: Event & { currentTarget: EventTarget & HTMLSelectElement }) { + let seed: number | undefined = undefined; + + if (e.currentTarget.value === 'RANDOM') { + seed = newSeed(); + } + + apply({ ...sort, on: e.currentTarget.value, seed }); } </script> <div class="rounded-group flex flex-row"> - <select class="btn-slate" bind:value={$sort.on} on:change={apply} title="Sort on..."> - {#each Object.entries($sort.labels) as [value, label]} + <select class="btn-slate appearance-none" value={sort.on} {onchange} title="Sort by..."> + {#each Object.entries(labels) as [value, label] (value)} <option {value}>{label}</option> {/each} </select> - <button type="button" class="btn-slate" title="Toggle sort direction" on:click={toggle}> - {#if $sort.direction === SortDirection.Ascending} - <span class="icon-base icon-[material-symbols--sort] -scale-y-100" /> + <button type="button" class="btn-slate" title="Toggle sort direction" onclick={toggle}> + {#if sort.direction === SortDirection.Ascending} + <span class="icon-base icon-[material-symbols--sort] -scale-y-100"></span> {:else} - <span class="icon-base icon-[material-symbols--sort]" /> + <span class="icon-base icon-[material-symbols--sort]"></span> {/if} </button> - {#if $sort.on === 'RANDOM'} + {#if sort.on === 'RANDOM'} <button type="button" class="btn-slate" title="Reshuffle" - on:click={reshuffle} + aria-label="Reshuffle" + onclick={shuffle} transition:slide={slideXFast} > <div class="flex"> - <span class="icon-base icon-[material-symbols--shuffle]" /> + <span class="icon-base icon-[material-symbols--shuffle]"></span> </div> </button> {/if} diff --git a/frontend/src/lib/toolbar/SelectionControls.svelte b/frontend/src/lib/toolbar/SelectionControls.svelte index 4d309df..f0026c8 100644 --- a/frontend/src/lib/toolbar/SelectionControls.svelte +++ b/frontend/src/lib/toolbar/SelectionControls.svelte @@ -1,57 +1,64 @@ <script lang="ts"> - import { getSelectionContext } from '$lib/Selection'; + import Badge from '$lib/components/Badge.svelte'; + import { getSelectionContext } from '$lib/selection/Selection.svelte'; import { accelerator } from '$lib/Shortcuts'; import { fadeDefault, slideXFast } from '$lib/Transitions'; - import Badge from '$lib/components/Badge.svelte'; - import { onDestroy } from 'svelte'; + import { onDestroy, type Snippet } from 'svelte'; import { fade, slide } from 'svelte/transition'; - const selection = getSelectionContext(); - - export let page = false; - - const toggle = () => ($selection = $selection.toggle()); - const all = () => ($selection = $selection.all()); - const none = () => ($selection = $selection.none()); + let { page = false, children }: { page?: boolean; children?: Snippet } = $props(); + let selection = getSelectionContext(); - onDestroy(() => ($selection = $selection.clear())); + onDestroy(selection.clear); </script> <div class="rounded-group flex"> <button type="button" class="btn-slate relative" - class:toggled={$selection.active} - title={`${$selection.active ? 'Exit' : 'Enter'} ${page ? 'page ' : ' '}selection mode`} - on:click={toggle} + class:toggled={selection.active} + title={`${selection.active ? 'Exit' : 'Enter'} ${page ? 'page ' : ' '}selection mode`} + onclick={selection.toggle} use:accelerator={'s'} > - {#if $selection.active} + {#if selection.active} {#if page} - <span class="icon-base icon-[material-symbols--edit-document]" /> + <span class="icon-base icon-[material-symbols--edit-document]"></span> {:else} - <span class="icon-base icon-[material-symbols--remove-selection]" /> + <span class="icon-base icon-[material-symbols--remove-selection]"></span> {/if} {:else if page} - <span class="icon-base icon-[material-symbols--edit-document-outline]" /> + <span class="icon-base icon-[material-symbols--edit-document-outline]"></span> {:else} - <span class="icon-base icon-[material-symbols--select]" /> + <span class="icon-base icon-[material-symbols--select]"></span> {/if} - <Badge number={$selection.size} /> + <Badge number={selection.size} /> </button> - {#if $selection.active} + {#if selection.active} <div class="rounded-group-end flex" transition:slide={slideXFast}> - <button type="button" class="btn-slate" title="Select all" on:click={all}> - <span class="icon-base icon-[material-symbols--select-all]" /> + <button + type="button" + class="btn-slate" + title="Select all" + aria-label="Select all" + onclick={selection.all} + > + <span class="icon-base icon-[material-symbols--select-all]"></span> </button> - <button type="button" class="btn-slate" title="Select none" on:click={none}> - <span class="icon-base icon-[material-symbols--deselect]" /> + <button + type="button" + class="btn-slate" + title="Select none" + aria-label="Select all" + onclick={selection.none} + > + <span class="icon-base icon-[material-symbols--deselect]"></span> </button> </div> {/if} </div> -{#if $selection.size > 0} +{#if selection.size > 0} <div class="rounded-group flex" transition:fade={fadeDefault}> - <slot /> + {@render children?.()} </div> {/if} diff --git a/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte b/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte index 2e7869f..2ef63f4 100644 --- a/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte +++ b/frontend/src/lib/toolbar/ToggleAdvancedFilters.svelte @@ -1,39 +1,45 @@ <script lang="ts"> - import { page } from '$app/stores'; - import { getFilterContext } from '$lib/Filter'; + import { page } from '$app/state'; import { navigate } from '$lib/Navigation'; + import { accelerator } from '$lib/Shortcuts'; import { slideXFast } from '$lib/Transitions'; import Badge from '$lib/components/Badge.svelte'; import { slide } from 'svelte/transition'; - import { getToolbarContext } from './Toolbar.svelte'; + import type { ToolbarState } from './Toolbar.svelte'; - const toolbar = getToolbarContext(); - const filter = getFilterContext(); + interface Props extends ToolbarState { + filterSize: number; + } + + let { expanded, toggle, filterSize }: Props = $props(); </script> <div class="rounded-group flex"> <button - class:toggled={$toolbar.expand} + class:toggled={expanded} class="btn-slate relative" - title={`${$toolbar.expand ? 'Hide' : 'Show'} filters`} - on:click={() => ($toolbar.expand = !$toolbar.expand)} + title={`${expanded ? 'Hide' : 'Show'} filters`} + onclick={toggle} + use:accelerator={'F'} > - {#if $toolbar.expand} - <span class="icon-base icon-[material-symbols--filter-alt]" /> + {#if expanded} + <span class="icon-base icon-[material-symbols--filter-alt]"></span> {:else} - <span class="icon-base icon-[material-symbols--filter-alt-outline]" /> + <span class="icon-base icon-[material-symbols--filter-alt-outline]"></span> {/if} - <Badge number={$filter.include.size + $filter.exclude.size} /> + <Badge number={filterSize} /> </button> - {#if $filter.include.size + $filter.exclude.size > 0} + {#if filterSize > 0} <button class="btn-slate relative hover:bg-rose-700" - on:click={() => navigate({ filter: {} }, $page.url.searchParams)} + onclick={() => navigate({ filter: {} }, page.url.searchParams)} transition:slide={slideXFast} title="Reset filters" + aria-label="Reset filters" + use:accelerator={'X'} > <div class="flex"> - <span class="icon-base icon-[material-symbols--filter-alt-off]" /> + <span class="icon-base icon-[material-symbols--filter-alt-off]"></span> </div> </button> {/if} diff --git a/frontend/src/lib/toolbar/Toolbar.svelte b/frontend/src/lib/toolbar/Toolbar.svelte index e87d731..fefc151 100644 --- a/frontend/src/lib/toolbar/Toolbar.svelte +++ b/frontend/src/lib/toolbar/Toolbar.svelte @@ -1,23 +1,28 @@ -<script lang="ts" context="module"> - import { writable, type Writable } from 'svelte/store'; +<script lang="ts"> + import { slideYDefault } from '$lib/Transitions'; + import { type Snippet } from 'svelte'; + import { slide } from 'svelte/transition'; - interface ToolbarContext { - expand: boolean; + export interface ToolbarState { + expanded: boolean; + toggle: () => void; } - function initToolbarContext() { - return setContext<Writable<ToolbarContext>>('toolbar', writable({ expand: false })); + interface Props { + start?: Snippet<[ToolbarState]>; + center?: Snippet<[ToolbarState]>; + end?: Snippet<[ToolbarState]>; + expansion?: Snippet; + expand?: boolean; } - export function getToolbarContext() { - return getContext<Writable<ToolbarContext>>('toolbar'); - } -</script> + let { start, center, end, expansion, expand = false }: Props = $props(); -<script lang="ts"> - import { getContext, setContext } from 'svelte'; + let expanded = $state(expand); - const toolbar = initToolbarContext(); + function toggle() { + expanded = !expanded; + } </script> <div class="flex flex-col"> @@ -25,18 +30,18 @@ class="flex flex-row flex-wrap gap-4 text-sm xl:grid xl:grid-flow-col xl:grid-cols-[1fr_2fr_1fr]" > <div class="flex flex-row justify-start gap-2"> - <slot name="start" /> + {@render start?.({ expanded, toggle })} </div> <div class="flex flex-row flex-wrap justify-start gap-2 xl:flex-nowrap xl:justify-center"> - <slot name="center" /> + {@render center?.({ expanded, toggle })} </div> <div class="flex flex-row justify-end gap-2"> - <slot name="end" /> + {@render end?.({ expanded, toggle })} </div> </div> - {#if $toolbar.expand} - <div class="mt-4"> - <slot /> + {#if expanded} + <div class="mt-4" transition:slide={slideYDefault}> + {@render expansion?.()} </div> {/if} </div> diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 0eefed1..29a1c16 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -1,5 +1,6 @@ <script lang="ts"> import { addShortcut, handleShortcuts } from '$lib/Shortcuts'; + import { toastFinally } from '$lib/Toasts'; import { fadeDefault } from '$lib/Transitions'; import AddArtist from '$lib/dialogs/AddArtist.svelte'; import AddCharacter from '$lib/dialogs/AddCharacter.svelte'; @@ -11,7 +12,7 @@ import Navigation from '$lib/navigation/Navigation.svelte'; import { cacheExchange, fetchExchange, initContextClient } from '@urql/svelte'; import { SvelteToast } from '@zerodevx/svelte-toast'; - import { Modals, closeModal, openModal } from 'svelte-modals'; + import { Modals, modals, type ModalComponent } from 'svelte-modals'; import { fade } from 'svelte/transition'; import '../app.css'; @@ -20,12 +21,16 @@ exchanges: [cacheExchange, fetchExchange] }); - addShortcut('na', () => openModal(AddArtist)); - addShortcut('nh', () => openModal(AddCharacter)); - addShortcut('ni', () => openModal(AddCircle)); - addShortcut('nn', () => openModal(AddNamespace)); - addShortcut('nt', () => openModal(AddTag)); - addShortcut('nw', () => openModal(AddWorld)); + function open(modal: ModalComponent) { + modals.open(modal).catch(toastFinally); + } + + addShortcut('na', () => open(AddArtist)); + addShortcut('nh', () => open(AddCharacter)); + addShortcut('ni', () => open(AddCircle)); + addShortcut('nn', () => open(AddNamespace)); + addShortcut('nt', () => open(AddTag)); + addShortcut('nw', () => open(AddWorld)); function keydown(event: KeyboardEvent) { handleShortcuts(event); @@ -36,51 +41,55 @@ <Navigation> <Link matchExact href="/" title="Home" accel="go"> - <span class="icon-base icon-[material-symbols--home]" /> + <span class="icon-base icon-[material-symbols--home]"></span> </Link> <Link href="/comics/" title="Comics" accel="gc"> - <span class="icon-base icon-[material-symbols--menu-book]" /> + <span class="icon-base icon-[material-symbols--menu-book]"></span> </Link> <Link href="/namespaces/" title="Namespaces" accel="gn"> - <span class="icon-base icon-[material-symbols--inbox]" /> + <span class="icon-base icon-[material-symbols--inbox]"></span> </Link> <Link href="/tags/" title="Tags" accel="gt"> - <span class="icon-base icon-[material-symbols--label]" /> + <span class="icon-base icon-[material-symbols--label]"></span> </Link> <Link href="/artists/" title="Artists" accel="ga"> - <span class="icon-base icon-[material-symbols--person]" /> + <span class="icon-base icon-[material-symbols--person]"></span> </Link> <Link href="/circles/" title="Circles" accel="gi"> - <span class="icon-base icon-[material-symbols--group]" /> + <span class="icon-base icon-[material-symbols--group]"></span> </Link> <Link href="/characters/" title="Characters" accel="gh"> - <span class="icon-base icon-[material-symbols--face]" /> + <span class="icon-base icon-[material-symbols--face]"></span> </Link> <Link href="/worlds/" title="Worlds" accel="gw"> - <span class="icon-base icon-[material-symbols--public]" /> + <span class="icon-base icon-[material-symbols--public]"></span> </Link> <Link href="/archives/" title="Archives" accel="gz"> - <span class="icon-base icon-[material-symbols--folder-zip]" /> + <span class="icon-base icon-[material-symbols--folder-zip]"></span> + </Link> + <div class="mb-auto"></div> + <Link href="/statistics/" title="Statistics" accel="gs"> + <span class="icon-base icon-[material-symbols--bar-chart]"></span> </Link> - <div class="mb-auto" /> <Link href="/help/" title="Help" accel="?" target="_blank"> - <span class="icon-base icon-[material-symbols--help]" /> + <span class="icon-base icon-[material-symbols--help]"></span> </Link> </Navigation> -<div class="min-w-[360px] overflow-auto p-4"> +<div class="min-w-[360px] overflow-auto p-4" tabindex="-1"> <slot /> </div> <Modals> - <!-- svelte-ignore a11y-no-static-element-interactions --> - <!-- svelte-ignore a11y-click-events-have-key-events --> - <div - slot="backdrop" - on:click={closeModal} - transition:fade={fadeDefault} - class="fixed bottom-0 left-0 right-0 top-0 z-20 bg-stone-800/80" - /> + {#snippet backdrop({ close })} + <!-- svelte-ignore a11y-no-static-element-interactions --> + <!-- svelte-ignore a11y-click-events-have-key-events --> + <div + onclick={() => close()} + transition:fade={fadeDefault} + class="fixed bottom-0 left-0 right-0 top-0 z-20 bg-stone-800/80" + ></div> + {/snippet} </Modals> <SvelteToast options={{ reversed: true, intro: { y: 192 } }} /> diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 97a7a60..4b921bb 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -6,29 +6,40 @@ import { href } from '$lib/Navigation'; import { fadeDefault } from '$lib/Transitions'; import logo from '$lib/assets/logo.webp'; - import Card, { comicCard } from '$lib/components/Card.svelte'; + import ComicCard from '$lib/components/ComicCard.svelte'; import Guard from '$lib/components/Guard.svelte'; import Head from '$lib/components/Head.svelte'; import Carousel from '$lib/containers/Carousel.svelte'; import { getContextClient } from '@urql/svelte'; import { fade } from 'svelte/transition'; - const bookmarkLink = href('comics', { filter: { include: { bookmarked: true } } }); + const bookmarkLink = href('comics', { + filter: { include: { bookmarked: true } }, + sort: { on: ComicSort.Random, seed: dailySeed() } + }); const recentLink = href('comics', { sort: { on: ComicSort.CreatedAt, direction: SortDirection.Descending } }); - const favouriteLink = href('comics', { filter: { include: { favourite: true } } }); + const favouriteLink = href('comics', { + filter: { include: { favourite: true } }, + sort: { on: ComicSort.Random, seed: dailySeed() } + }); + + function dailySeed() { + const date = new Date(); + return +`${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}`; + } - $: query = frontpageQuery(getContextClient()); - $: recent = $query.data?.recent; - $: favourites = $query.data?.favourites; - $: bookmarked = $query.data?.bookmarked; + let query = $derived(frontpageQuery(getContextClient(), { seed: dailySeed() })); + let recent = $derived($query.data?.recent); + let favourites = $derived($query.data?.favourites); + let bookmarked = $derived($query.data?.bookmarked); </script> <Head section="Home" /> -<div class="flex flex-col justify-center gap-16 xl:flex-row"> - {#if $query.data} +{#if $query.data} + <div class="flex flex-col justify-center gap-16 xl:flex-row"> <div class="flex flex-col items-center gap-1"> <img src={logo} width="512" height="512" class="min-w-[400px]" alt="" /> <h1 class="text-4xl font-medium"> @@ -40,27 +51,27 @@ <div class="flex flex-col gap-8" in:fade={fadeDefault}> {#if recent && recent.count > 0} <Carousel title="Recently added" href={recentLink}> - {#each recent.edges as comic} - <Card coverOnly {...comicCard(comic)} /> + {#each recent.edges as comic (comic.id)} + <ComicCard coverOnly {comic} /> {/each} </Carousel> {/if} {#if favourites && favourites.count > 0} <Carousel title="Favourites" href={favouriteLink}> - {#each favourites.edges as comic} - <Card coverOnly {...comicCard(comic)} /> + {#each favourites.edges as comic (comic.id)} + <ComicCard coverOnly {comic} /> {/each} </Carousel> {/if} {#if bookmarked && bookmarked.count > 0} <Carousel title="Bookmarks" href={bookmarkLink}> - {#each bookmarked.edges as comic} - <Card coverOnly {...comicCard(comic)} /> + {#each bookmarked.edges as comic (comic.id)} + <ComicCard coverOnly {comic} /> {/each} </Carousel> {/if} </div> - {:else} - <Guard result={query} /> - {/if} -</div> + </div> +{:else} + <Guard result={query} /> +{/if} diff --git a/frontend/src/routes/archives/+page.svelte b/frontend/src/routes/archives/+page.svelte index 545058a..2bc6703 100644 --- a/frontend/src/routes/archives/+page.svelte +++ b/frontend/src/routes/archives/+page.svelte @@ -3,11 +3,8 @@ import { archivesQuery } from '$gql/Queries'; import type { ArchiveFragment } from '$gql/graphql'; import { ArchiveSortLabel } from '$lib/Enums'; - import { ArchiveFilterContext, initFilterContext } from '$lib/Filter'; - import { initPaginationContext } from '$lib/Pagination'; - import { initSelectionContext } from '$lib/Selection'; - import { initSortContext } from '$lib/Sort'; - import Card from '$lib/components/Card.svelte'; + import { ArchiveFilterContext } from '$lib/Filter.svelte'; + import ArchiveCard from '$lib/components/ArchiveCard.svelte'; import Empty from '$lib/components/Empty.svelte'; import Guard from '$lib/components/Guard.svelte'; import Head from '$lib/components/Head.svelte'; @@ -15,8 +12,8 @@ import Cards from '$lib/containers/Cards.svelte'; import Column from '$lib/containers/Column.svelte'; import Pagination from '$lib/pagination/Pagination.svelte'; - import Pill from '$lib/pills/Pill.svelte'; import Selectable from '$lib/selection/Selectable.svelte'; + import { initSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionOverlay from '$lib/selection/SelectionOverlay.svelte'; import DeleteSelection from '$lib/toolbar/DeleteSelection.svelte'; import FilterOrganized from '$lib/toolbar/FilterOrganized.svelte'; @@ -28,91 +25,74 @@ import SelectionControls from '$lib/toolbar/SelectionControls.svelte'; import Toolbar from '$lib/toolbar/Toolbar.svelte'; import { getContextClient } from '@urql/svelte'; - import { filesize } from 'filesize'; - import type { PageData } from './$types'; + import type { PageProps } from './$types'; - let client = getContextClient(); + let { data }: PageProps = $props(); + let pagination = $derived(data.pagination); + let sort = $derived(data.sort); - export let data: PageData; + const client = getContextClient(); + let result = $derived(archivesQuery(client, { ...data })); + let archives = $derived($result.data?.archives); - $: result = archivesQuery(client, { - pagination: data.pagination, - filter: data.filter, - sort: data.sort + let selection = initSelectionContext<ArchiveFragment>('Archive', (a) => a.name); + $effect(() => { + if (archives) { + selection.view = archives.edges; + } }); - $: archives = $result.data?.archives; - - const selection = initSelectionContext<ArchiveFragment>('Archive', (a) => a.name); - $: if (archives) { - $selection.view = archives.edges; - $pagination.total = archives.count; - } - - const pagination = initPaginationContext(); - $: $pagination.update = data.pagination; - - const filter = initFilterContext<ArchiveFilterContext>(); - $: $filter = new ArchiveFilterContext(data.filter); - - const sort = initSortContext(data.sort, ArchiveSortLabel); - $: $sort.update = data.sort; - - function refresh() { - result.reexecute({ requestPolicy: 'network-only' }); - } + let filter = $state(new ArchiveFilterContext(data.filter)); + $effect(() => { + filter = new ArchiveFilterContext(data.filter); + }); </script> <Head section="Archives" /> <Column> <Toolbar> - <SelectionControls slot="start"> - <MarkSelection> - <MarkOrganized mutation={updateArchives} /> - </MarkSelection> - <DeleteSelection - mutation={deleteArchives} - warning="Deleting an archive will also delete its archive file on disk as well as all comics that belong to it." - /> - </SelectionControls> - <svelte:fragment slot="center"> - <Search name="Archives" bind:field={$filter.include.controls.path.contains} /> - <FilterOrganized /> - <SelectSort /> - <SelectItems /> - </svelte:fragment> - <RefreshButton slot="end" on:click={refresh} /> + {#snippet start()} + <SelectionControls> + <MarkSelection> + <MarkOrganized mutation={updateArchives} /> + </MarkSelection> + <DeleteSelection + mutation={deleteArchives} + warning="Deleting an archive will also delete its archive file on disk as well as all comics that belong to it." + /> + </SelectionControls> + {/snippet} + {#snippet center()} + <Search name="Archives" {filter} bind:field={filter.include.path.contains} /> + <FilterOrganized {filter} /> + <SelectSort {sort} labels={ArchiveSortLabel} /> + <SelectItems {pagination} /> + {/snippet} + {#snippet end()} + <RefreshButton onclick={() => result.reexecute({ requestPolicy: 'network-only' })} /> + {/snippet} </Toolbar> {#if archives} - <Pagination /> + <Pagination {pagination} total={archives.count} /> <main> <Cards> - {#each archives.edges as { id, name, cover, size, pageCount }, index (id)} - <Selectable {index} {id} let:handle let:selected> - <Card - ellipsis={false} - href={id.toString()} - details={{ title: name, cover: cover }} - on:click={handle} - > - <SelectionOverlay position="left" {selected} slot="overlay" /> - <div class="flex gap-1 text-xs"> - <Pill name={`${pageCount} pages`}> - <span class="icon-[material-symbols--note] mr-0.5" slot="icon" /> - </Pill> - <Pill name={filesize(size, { base: 2 })}> - <span class="icon-[material-symbols--hard-drive] mr-0.5" slot="icon" /> - </Pill> - </div> - </Card> + {#each archives.edges as archive, index (archive.id)} + <Selectable {index} id={archive.id}> + {#snippet children({ onclick, selected })} + <ArchiveCard {archive} {onclick}> + {#snippet overlay()} + <SelectionOverlay position="left" {selected} /> + {/snippet} + </ArchiveCard> + {/snippet} </Selectable> {:else} <Empty /> {/each} </Cards> </main> - <Pagination /> + <Pagination {pagination} total={archives.count} /> {:else} <Guard {result} /> {/if} diff --git a/frontend/src/routes/archives/[id]/+page.svelte b/frontend/src/routes/archives/[id]/+page.svelte index 50a2940..9eedcb2 100644 --- a/frontend/src/routes/archives/[id]/+page.svelte +++ b/frontend/src/routes/archives/[id]/+page.svelte @@ -2,9 +2,7 @@ import { updateArchives } from '$gql/Mutations'; import { archiveQuery } from '$gql/Queries'; import { Direction, Layout, type FullArchiveFragment, type PageFragment } from '$gql/graphql'; - import { initReaderContext } from '$lib/Reader'; - import { initSelectionContext } from '$lib/Selection'; - import { setTabContext } from '$lib/Tabs'; + import { initReaderContext } from '$lib/Reader.svelte'; import { toastFinally } from '$lib/Toasts'; import Guard from '$lib/components/Guard.svelte'; import Head from '$lib/components/Head.svelte'; @@ -13,51 +11,39 @@ import Gallery from '$lib/gallery/Gallery.svelte'; import PageView from '$lib/reader/PageView.svelte'; import Reader from '$lib/reader/Reader.svelte'; + import { initSelectionContext } from '$lib/selection/Selection.svelte'; import ArchiveDelete from '$lib/tabs/ArchiveDelete.svelte'; import ArchiveDetails from '$lib/tabs/ArchiveDetails.svelte'; import ArchiveEdit from '$lib/tabs/ArchiveEdit.svelte'; import Tab from '$lib/tabs/Tab.svelte'; import Tabs from '$lib/tabs/Tabs.svelte'; import { getContextClient } from '@urql/svelte'; - import type { PageData } from './$types'; + import type { PageProps } from './$types'; - export let data: PageData; + let { data }: PageProps = $props(); const client = getContextClient(); const reader = initReaderContext(); - setTabContext({ - tabs: { - details: { title: 'Details' }, - edit: { title: 'Edit' }, - deletion: { title: 'Delete' } - }, - current: 'details' - }); - - $: result = archiveQuery(client, { id: data.id }); - function updateCover(event: CustomEvent<number>) { - updateArchives(client, { ids: archive.id, input: { cover: { id: event.detail } } }).catch( - toastFinally - ); + function updateCover(id: number) { + updateArchives(client, { ids: data.id, input: { cover: { id } } }).catch(toastFinally); } - let archive: FullArchiveFragment; + let selection = initSelectionContext<PageFragment>( + 'Page', + (p) => p.path, + (p) => p.comicId === null + ); - $: $result, update(); - function update() { - if (!$result.stale && $result.data?.archive.__typename === 'FullArchive') { - archive = structuredClone($result.data.archive); + let result = $derived(archiveQuery(client, { id: data.id })); + let archive: FullArchiveFragment | undefined = $state(); - $reader.pages = archive.pages; + $effect(() => { + if (!$result.stale && $result.data?.archive.__typename === 'FullArchive') { + archive = $result.data.archive; + reader.pages = $result.data.archive.pages; + selection.view = $result.data.archive.pages; } - } - - const selection = initSelectionContext<PageFragment>('Page', (p) => p.path); - $selection.selectable = (p) => p.comicId === null; - - $: if (archive) { - $selection.view = archive.pages; - } + }); </script> <Head section="Archive" title={archive?.name} /> @@ -70,24 +56,20 @@ <aside> <Tabs> - <Tab id="details"> + <Tab initial id="details" title="Details"> <ArchiveDetails {archive} /> </Tab> - <Tab id="edit"> + <Tab id="edit" title="Edit"> <ArchiveEdit {archive} /> </Tab> - <Tab id="deletion"> + <Tab id="deletion" title="Delete"> <ArchiveDelete {archive} /> </Tab> </Tabs> </aside> <main class="overflow-auto"> - <Gallery - pages={archive.pages} - on:open={(e) => ($reader = $reader.open(e.detail))} - on:cover={updateCover} - /> + <Gallery pages={archive.pages} open={reader.open} {updateCover} /> </main> </Grid> {:else} diff --git a/frontend/src/routes/artists/+page.svelte b/frontend/src/routes/artists/+page.svelte index e07338c..9fff9d2 100644 --- a/frontend/src/routes/artists/+page.svelte +++ b/frontend/src/routes/artists/+page.svelte @@ -3,10 +3,8 @@ import { artistsQuery, fetchArtist } from '$gql/Queries'; import type { Artist } from '$gql/graphql'; import { ArtistSortLabel } from '$lib/Enums'; - import { BasicFilterContext, initFilterContext } from '$lib/Filter'; - import { initPaginationContext } from '$lib/Pagination'; - import { initSelectionContext } from '$lib/Selection'; - import { initSortContext } from '$lib/Sort'; + import { BasicFilterContext } from '$lib/Filter.svelte'; + import { quickComicFilter } from '$lib/Navigation'; import { toastFinally } from '$lib/Toasts'; import AddButton from '$lib/components/AddButton.svelte'; import Cardlet from '$lib/components/Cardlet.svelte'; @@ -19,82 +17,87 @@ import EditArtist from '$lib/dialogs/EditArtist.svelte'; import Pagination from '$lib/pagination/Pagination.svelte'; import Selectable from '$lib/selection/Selectable.svelte'; + import { initSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionOverlay from '$lib/selection/SelectionOverlay.svelte'; import DeleteSelection from '$lib/toolbar/DeleteSelection.svelte'; + import FilterOrphaned from '$lib/toolbar/FilterOrphaned.svelte'; import Search from '$lib/toolbar/Search.svelte'; import SelectItems from '$lib/toolbar/SelectItems.svelte'; import SelectSort from '$lib/toolbar/SelectSort.svelte'; import SelectionControls from '$lib/toolbar/SelectionControls.svelte'; import Toolbar from '$lib/toolbar/Toolbar.svelte'; import { getContextClient } from '@urql/svelte'; - import { openModal } from 'svelte-modals'; - import type { PageData } from './$types'; + import { modals } from 'svelte-modals'; + import type { PageProps } from './$types'; + + let { data }: PageProps = $props(); + let pagination = $derived(data.pagination); + let sort = $derived(data.sort); const client = getContextClient(); - export let data: PageData; + let result = $derived(artistsQuery(client, { ...data })); + let artists = $derived($result.data?.artists); - $: result = artistsQuery(client, { - pagination: data.pagination, - filter: data.filter, - sort: data.sort + let selection = initSelectionContext<Artist>('Artist', (a) => a.name); + $effect(() => { + if (artists) { + selection.view = artists.edges; + } }); - $: artists = $result.data?.artists; - - const selection = initSelectionContext<Artist>('Artist', (a) => a.name); - $: if (artists) { - $selection.view = artists.edges; - $pagination.total = artists.count; - } - - const filter = initFilterContext<BasicFilterContext>(); - $: $filter = new BasicFilterContext(data.filter); - - const sort = initSortContext(data.sort, ArtistSortLabel); - $: $sort.update = data.sort; - - const pagination = initPaginationContext(); - $: $pagination.update = data.pagination; + let filter = $state(new BasicFilterContext(data.filter)); + $effect(() => { + filter = new BasicFilterContext(data.filter); + }); const edit = (id: number) => { fetchArtist(client, id) - .then((artist) => openModal(EditArtist, { artist })) + .then((artist) => modals.open(EditArtist, { artist })) .catch(toastFinally); }; + + const quickFilter = (id: number) => quickComicFilter(id, 'artists'); </script> <Head section="artists" /> <Column> <Toolbar> - <SelectionControls slot="start"> - <DeleteSelection mutation={deleteArtists} /> - </SelectionControls> - <svelte:fragment slot="center"> - <Search name="Artists" bind:field={$filter.include.controls.name.contains} /> - <SelectSort /> - <SelectItems /> - </svelte:fragment> - <svelte:fragment slot="end"> - <AddButton title="Add Artist" on:click={() => openModal(AddArtist)} /> - </svelte:fragment> + {#snippet start()} + <SelectionControls> + <DeleteSelection mutation={deleteArtists} /> + </SelectionControls> + {/snippet} + {#snippet center()} + <Search name="Artists" {filter} bind:field={filter.include.name.contains} /> + <FilterOrphaned {filter} /> + <SelectSort {sort} labels={ArtistSortLabel} /> + <SelectItems {pagination} /> + {/snippet} + {#snippet end()} + <AddButton title="Add Artist" onclick={() => modals.open(AddArtist)} /> + {/snippet} </Toolbar> {#if artists} - <Pagination /> + <Pagination {pagination} total={artists.count} /> <main> <Cardlets> {#each artists.edges as { id, name }, index (id)} - <Selectable {index} {id} {edit} let:handle let:selected> - <Cardlet {name} on:click={handle} filter="artists" {id}> - <SelectionOverlay slot="overlay" position="right" centered {selected} /> - </Cardlet> + <Selectable {index} {id} onclick={edit} onauxclick={quickFilter}> + {#snippet children({ onclick, onauxclick, selected })} + <Cardlet {name} {onclick} {onauxclick}> + {#snippet overlay()} + <SelectionOverlay position="right" centered {selected} /> + {/snippet} + </Cardlet> + {/snippet} </Selectable> {:else} <Empty /> {/each} </Cardlets> </main> - <Pagination /> + <Pagination {pagination} total={artists.count} /> {:else} <Guard {result} /> {/if} diff --git a/frontend/src/routes/characters/+page.svelte b/frontend/src/routes/characters/+page.svelte index 0934bab..970a8fa 100644 --- a/frontend/src/routes/characters/+page.svelte +++ b/frontend/src/routes/characters/+page.svelte @@ -3,10 +3,8 @@ import { charactersQuery, fetchCharacter } from '$gql/Queries'; import type { Character } from '$gql/graphql'; import { CharacterSortLabel } from '$lib/Enums'; - import { BasicFilterContext, initFilterContext } from '$lib/Filter'; - import { initPaginationContext } from '$lib/Pagination'; - import { initSelectionContext } from '$lib/Selection'; - import { initSortContext } from '$lib/Sort'; + import { BasicFilterContext } from '$lib/Filter.svelte'; + import { quickComicFilter } from '$lib/Navigation'; import { toastFinally } from '$lib/Toasts'; import AddButton from '$lib/components/AddButton.svelte'; import Cardlet from '$lib/components/Cardlet.svelte'; @@ -19,82 +17,87 @@ import EditCharacter from '$lib/dialogs/EditCharacter.svelte'; import Pagination from '$lib/pagination/Pagination.svelte'; import Selectable from '$lib/selection/Selectable.svelte'; + import { initSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionOverlay from '$lib/selection/SelectionOverlay.svelte'; import DeleteSelection from '$lib/toolbar/DeleteSelection.svelte'; + import FilterOrphaned from '$lib/toolbar/FilterOrphaned.svelte'; import Search from '$lib/toolbar/Search.svelte'; import SelectItems from '$lib/toolbar/SelectItems.svelte'; import SelectSort from '$lib/toolbar/SelectSort.svelte'; import SelectionControls from '$lib/toolbar/SelectionControls.svelte'; import Toolbar from '$lib/toolbar/Toolbar.svelte'; import { getContextClient } from '@urql/svelte'; - import { openModal } from 'svelte-modals'; - import type { PageData } from './$types'; + import { modals } from 'svelte-modals'; + import type { PageProps } from './$types'; + + let { data }: PageProps = $props(); + let pagination = $derived(data.pagination); + let sort = $derived(data.sort); const client = getContextClient(); - export let data: PageData; + let result = $derived(charactersQuery(client, { ...data })); + let characters = $derived($result.data?.characters); - $: result = charactersQuery(client, { - pagination: data.pagination, - filter: data.filter, - sort: data.sort + let selection = initSelectionContext<Character>('Character', (a) => a.name); + $effect(() => { + if (characters) { + selection.view = characters.edges; + } }); - $: characters = $result.data?.characters; - - const selection = initSelectionContext<Character>('Character', (c) => c.name); - $: if (characters) { - $selection.view = characters.edges; - $pagination.total = characters.count; - } - - const filter = initFilterContext<BasicFilterContext>(); - $: $filter = new BasicFilterContext(data.filter); - - const sort = initSortContext(data.sort, CharacterSortLabel); - $: $sort.update = data.sort; - - const pagination = initPaginationContext(); - $: $pagination.update = data.pagination; + let filter = $state(new BasicFilterContext(data.filter)); + $effect(() => { + filter = new BasicFilterContext(data.filter); + }); const edit = (id: number) => { fetchCharacter(client, id) - .then((character) => openModal(EditCharacter, { character })) + .then((character) => modals.open(EditCharacter, { character })) .catch(toastFinally); }; + + const quickFilter = (id: number) => quickComicFilter(id, 'characters'); </script> <Head section="characters" /> <Column> <Toolbar> - <SelectionControls slot="start"> - <DeleteSelection mutation={deleteCharacters} /> - </SelectionControls> - <svelte:fragment slot="center"> - <Search name="Characters" bind:field={$filter.include.controls.name.contains} /> - <SelectSort /> - <SelectItems /> - </svelte:fragment> - <svelte:fragment slot="end"> - <AddButton title="Add Character" on:click={() => openModal(AddCharacter)} /> - </svelte:fragment> + {#snippet start()} + <SelectionControls> + <DeleteSelection mutation={deleteCharacters} /> + </SelectionControls> + {/snippet} + {#snippet center()} + <Search name="Characters" {filter} bind:field={filter.include.name.contains} /> + <FilterOrphaned {filter} /> + <SelectSort {sort} labels={CharacterSortLabel} /> + <SelectItems {pagination} /> + {/snippet} + {#snippet end()} + <AddButton title="Add Character" onclick={() => modals.open(AddCharacter)} /> + {/snippet} </Toolbar> {#if characters} - <Pagination /> + <Pagination {pagination} total={characters.count} /> <main> <Cardlets> {#each characters.edges as { id, name }, index (id)} - <Selectable {index} {id} {edit} let:handle let:selected> - <Cardlet {name} on:click={handle} filter="characters" {id}> - <SelectionOverlay slot="overlay" position="right" centered {selected} /> - </Cardlet> + <Selectable {index} {id} onclick={edit} onauxclick={quickFilter}> + {#snippet children({ onclick, onauxclick, selected })} + <Cardlet {name} {onclick} {onauxclick}> + {#snippet overlay()} + <SelectionOverlay position="right" centered {selected} /> + {/snippet} + </Cardlet> + {/snippet} </Selectable> {:else} <Empty /> {/each} </Cardlets> </main> - <Pagination /> + <Pagination {pagination} total={characters.count} /> {:else} <Guard {result} /> {/if} diff --git a/frontend/src/routes/circles/+page.svelte b/frontend/src/routes/circles/+page.svelte index 14b0866..bad1e1d 100644 --- a/frontend/src/routes/circles/+page.svelte +++ b/frontend/src/routes/circles/+page.svelte @@ -3,10 +3,8 @@ import { circlesQuery, fetchCircle } from '$gql/Queries'; import type { Circle } from '$gql/graphql'; import { CircleSortLabel } from '$lib/Enums'; - import { BasicFilterContext, initFilterContext } from '$lib/Filter'; - import { initPaginationContext } from '$lib/Pagination'; - import { initSelectionContext } from '$lib/Selection'; - import { initSortContext } from '$lib/Sort'; + import { BasicFilterContext } from '$lib/Filter.svelte'; + import { quickComicFilter } from '$lib/Navigation'; import { toastFinally } from '$lib/Toasts'; import AddButton from '$lib/components/AddButton.svelte'; import Cardlet from '$lib/components/Cardlet.svelte'; @@ -19,82 +17,87 @@ import EditCircle from '$lib/dialogs/EditCircle.svelte'; import Pagination from '$lib/pagination/Pagination.svelte'; import Selectable from '$lib/selection/Selectable.svelte'; + import { initSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionOverlay from '$lib/selection/SelectionOverlay.svelte'; import DeleteSelection from '$lib/toolbar/DeleteSelection.svelte'; + import FilterOrphaned from '$lib/toolbar/FilterOrphaned.svelte'; import Search from '$lib/toolbar/Search.svelte'; import SelectItems from '$lib/toolbar/SelectItems.svelte'; import SelectSort from '$lib/toolbar/SelectSort.svelte'; import SelectionControls from '$lib/toolbar/SelectionControls.svelte'; import Toolbar from '$lib/toolbar/Toolbar.svelte'; import { getContextClient } from '@urql/svelte'; - import { openModal } from 'svelte-modals'; - import type { PageData } from './$types'; + import { modals } from 'svelte-modals'; + import type { PageProps } from './$types'; + + let { data }: PageProps = $props(); + let pagination = $derived(data.pagination); + let sort = $derived(data.sort); const client = getContextClient(); - export let data: PageData; + let result = $derived(circlesQuery(client, { ...data })); + let circles = $derived($result.data?.circles); - $: result = circlesQuery(client, { - pagination: data.pagination, - filter: data.filter, - sort: data.sort + let selection = initSelectionContext<Circle>('Circle', (a) => a.name); + $effect(() => { + if (circles) { + selection.view = circles.edges; + } }); - $: circles = $result.data?.circles; - - const selection = initSelectionContext<Circle>('Circle', (c) => c.name); - $: if (circles) { - $selection.view = circles.edges; - $pagination.total = circles.count; - } - - const filter = initFilterContext<BasicFilterContext>(); - $: $filter = new BasicFilterContext(data.filter); - - const sort = initSortContext(data.sort, CircleSortLabel); - $: $sort.update = data.sort; - - const pagination = initPaginationContext(); - $: $pagination.update = data.pagination; + let filter = $state(new BasicFilterContext(data.filter)); + $effect(() => { + filter = new BasicFilterContext(data.filter); + }); const edit = (id: number) => { fetchCircle(client, id) - .then((circle) => openModal(EditCircle, { circle })) + .then((circle) => modals.open(EditCircle, { circle })) .catch(toastFinally); }; + + const quickFilter = (id: number) => quickComicFilter(id, 'circles'); </script> <Head section="circles" /> <Column> <Toolbar> - <SelectionControls slot="start"> - <DeleteSelection mutation={deleteCircles} /> - </SelectionControls> - <svelte:fragment slot="center"> - <Search name="Circles" bind:field={$filter.include.controls.name.contains} /> - <SelectSort /> - <SelectItems /> - </svelte:fragment> - <svelte:fragment slot="end"> - <AddButton title="Add Circle" on:click={() => openModal(AddCircle)} /> - </svelte:fragment> + {#snippet start()} + <SelectionControls> + <DeleteSelection mutation={deleteCircles} /> + </SelectionControls> + {/snippet} + {#snippet center()} + <Search name="Circles" {filter} bind:field={filter.include.name.contains} /> + <FilterOrphaned {filter} /> + <SelectSort {sort} labels={CircleSortLabel} /> + <SelectItems {pagination} /> + {/snippet} + {#snippet end()} + <AddButton title="Add Circle" onclick={() => modals.open(AddCircle)} /> + {/snippet} </Toolbar> {#if circles} - <Pagination /> + <Pagination {pagination} total={circles.count} /> <main> <Cardlets> {#each circles.edges as { id, name }, index (id)} - <Selectable {index} {id} {edit} let:handle let:selected> - <Cardlet {name} on:click={handle} filter="circles" {id}> - <SelectionOverlay slot="overlay" position="right" centered {selected} /> - </Cardlet> + <Selectable {index} {id} onclick={edit} onauxclick={quickFilter}> + {#snippet children({ onclick, onauxclick, selected })} + <Cardlet {name} {onclick} {onauxclick}> + {#snippet overlay()} + <SelectionOverlay position="right" centered {selected} /> + {/snippet} + </Cardlet> + {/snippet} </Selectable> {:else} <Empty /> {/each} </Cardlets> </main> - <Pagination /> + <Pagination {pagination} total={circles.count} /> {:else} <Guard {result} /> {/if} diff --git a/frontend/src/routes/comics/+page.svelte b/frontend/src/routes/comics/+page.svelte index 353d69c..11289e7 100644 --- a/frontend/src/routes/comics/+page.svelte +++ b/frontend/src/routes/comics/+page.svelte @@ -3,11 +3,8 @@ import { comicsQuery } from '$gql/Queries'; import { type ComicFragment } from '$gql/graphql'; import { ComicSortLabel } from '$lib/Enums'; - import { ComicFilterContext, initFilterContext } from '$lib/Filter'; - import { initPaginationContext } from '$lib/Pagination'; - import { initSelectionContext } from '$lib/Selection'; - import { initSortContext } from '$lib/Sort'; - import Card, { comicCard } from '$lib/components/Card.svelte'; + import { ComicFilterContext } from '$lib/Filter.svelte'; + import ComicCard from '$lib/components/ComicCard.svelte'; import Empty from '$lib/components/Empty.svelte'; import Guard from '$lib/components/Guard.svelte'; import Head from '$lib/components/Head.svelte'; @@ -16,8 +13,8 @@ import UpdateComicsDialog from '$lib/dialogs/UpdateComics.svelte'; import ComicFilterForm from '$lib/filter/ComicFilterForm.svelte'; import Pagination from '$lib/pagination/Pagination.svelte'; - import ComicPills from '$lib/pills/ComicPills.svelte'; import Selectable from '$lib/selection/Selectable.svelte'; + import { initSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionOverlay from '$lib/selection/SelectionOverlay.svelte'; import DeleteSelection from '$lib/toolbar/DeleteSelection.svelte'; import EditSelection from '$lib/toolbar/EditSelection.svelte'; @@ -35,81 +32,82 @@ import ToggleAdvancedFilters from '$lib/toolbar/ToggleAdvancedFilters.svelte'; import Toolbar from '$lib/toolbar/Toolbar.svelte'; import { getContextClient } from '@urql/svelte'; - import type { PageData } from './$types'; + import type { PageProps } from './$types'; - export let data: PageData; + let { data }: PageProps = $props(); + let pagination = $derived(data.pagination); + let sort = $derived(data.sort); const client = getContextClient(); - - $: result = comicsQuery(client, { - pagination: data.pagination, - filter: data.filter, - sort: data.sort - }); - - $: comics = $result.data?.comics; + let result = $derived(comicsQuery(client, { ...data })); + let comics = $derived($result.data?.comics); const selection = initSelectionContext<ComicFragment>('Comic', (c) => c.title); - $: if (comics) { - $selection.view = comics.edges; - $pagination.total = comics.count; - } - - const filter = initFilterContext<ComicFilterContext>(); - $: $filter = new ComicFilterContext(data.filter); - - const sort = initSortContext(data.sort, ComicSortLabel); - $: $sort.update = data.sort; + $effect(() => { + if (comics) { + selection.view = comics.edges; + } + }); - const pagination = initPaginationContext(); - $: $pagination.update = data.pagination; + let filter = $state(new ComicFilterContext(data.filter)); + let filterSize = $derived(filter.includes + filter.excludes); + $effect(() => { + filter = new ComicFilterContext(data.filter); + }); </script> <Head section="Comics" /> <Column> - <Toolbar> - <SelectionControls slot="start"> - <MarkSelection> - <MarkFavourite mutation={updateComics} /> - <hr class="col-span-2 border-slate-600" /> - <MarkBookmark mutation={updateComics} /> - <hr class="col-span-2 border-slate-600" /> - <MarkOrganized mutation={updateComics} /> - </MarkSelection> - <EditSelection dialog={UpdateComicsDialog} /> - <DeleteSelection mutation={deleteComics} /> - </SelectionControls> - <svelte:fragment slot="center"> - <Search name="Comics" bind:field={$filter.include.controls.title.contains} /> - <ToggleAdvancedFilters /> + <Toolbar expand={filterSize > 0}> + {#snippet start()} + <SelectionControls> + <MarkSelection> + <MarkFavourite mutation={updateComics} /> + <hr class="col-span-2 border-slate-600" /> + <MarkBookmark mutation={updateComics} /> + <hr class="col-span-2 border-slate-600" /> + <MarkOrganized mutation={updateComics} /> + </MarkSelection> + <EditSelection dialog={UpdateComicsDialog} /> + <DeleteSelection mutation={deleteComics} /> + </SelectionControls> + {/snippet} + {#snippet center({ expanded, toggle })} + <Search name="Comics" {filter} bind:field={filter.include.title.contains} /> + <ToggleAdvancedFilters {expanded} {toggle} filterSize={filter.includes + filter.excludes} /> <div class="rounded-group flex"> - <FilterFavourites /> - <FilterBookmarked /> - <FilterOrganized /> + <FilterFavourites {filter} /> + <FilterBookmarked {filter} /> + <FilterOrganized {filter} /> </div> - <SelectSort /> - <SelectItems /> - </svelte:fragment> - <ComicFilterForm /> + <SelectSort {sort} labels={ComicSortLabel} /> + <SelectItems {pagination} /> + {/snippet} + {#snippet expansion()} + <ComicFilterForm {filter} /> + {/snippet} </Toolbar> {#if comics} - <Pagination /> + <Pagination {pagination} total={comics.count} /> <main> <Cards> {#each comics.edges as comic, index (comic.id)} - <Selectable {index} id={comic.id} let:handle let:selected> - <Card {...comicCard(comic)} on:click={handle}> - <SelectionOverlay position="left" {selected} slot="overlay" /> - <ComicPills {comic} /> - </Card> + <Selectable {index} id={comic.id}> + {#snippet children({ onclick, selected })} + <ComicCard {comic} {onclick}> + {#snippet overlay()} + <SelectionOverlay position="left" {selected} /> + {/snippet} + </ComicCard> + {/snippet} </Selectable> {:else} <Empty /> {/each} </Cards> </main> - <Pagination /> + <Pagination {pagination} total={comics.count} /> {:else} <Guard {result} /> {/if} diff --git a/frontend/src/routes/comics/[id]/+page.svelte b/frontend/src/routes/comics/[id]/+page.svelte index cfc5840..28a8dde 100644 --- a/frontend/src/routes/comics/[id]/+page.svelte +++ b/frontend/src/routes/comics/[id]/+page.svelte @@ -2,12 +2,15 @@ import { beforeNavigate } from '$app/navigation'; import { updateComics } from '$gql/Mutations'; import { comicQuery } from '$gql/Queries'; - import { comicEquals } from '$gql/Utils'; - import { UpdateMode, type FullComicFragment, type UpdateComicInput } from '$gql/graphql'; - import { initReaderContext } from '$lib/Reader'; - import { initScraperContext } from '$lib/Scraper'; - import { initSelectionContext } from '$lib/Selection'; - import { setTabContext } from '$lib/Tabs'; + import { omitIdentifiers, type OmitIdentifiers } from '$gql/Utils'; + import { + UpdateMode, + type FullComicFragment, + type PageFragment, + type UpdateComicInput + } from '$gql/graphql'; + import { comicPending } from '$lib/Form'; + import { initReaderContext } from '$lib/Reader.svelte'; import { toastFinally } from '$lib/Toasts'; import { preventOnPending } from '$lib/Utils'; import BookmarkButton from '$lib/components/BookmarkButton.svelte'; @@ -23,153 +26,138 @@ import PageView from '$lib/reader/PageView.svelte'; import Reader from '$lib/reader/Reader.svelte'; import ComicScrapeForm from '$lib/scraper/ComicScrapeForm.svelte'; + import { initScraperContext } from '$lib/scraper/Scraper.svelte'; + import { initSelectionContext } from '$lib/selection/Selection.svelte'; import ComicDelete from '$lib/tabs/ComicDelete.svelte'; import ComicDetails from '$lib/tabs/ComicDetails.svelte'; import Tab from '$lib/tabs/Tab.svelte'; import Tabs from '$lib/tabs/Tabs.svelte'; import SelectionControls from '$lib/toolbar/SelectionControls.svelte'; import { getContextClient } from '@urql/svelte'; - import type { PageData } from './$types'; + import { untrack } from 'svelte'; + import type { PageProps } from './$types'; + let { data }: PageProps = $props(); const client = getContextClient(); const reader = initReaderContext(); - const selection = initSelectionContext(); const scraper = initScraperContext(); - const tabContext = setTabContext({ - tabs: { - details: { title: 'Details' }, - edit: { title: 'Edit' }, - scrape: { title: 'Scrape' }, - deletion: { title: 'Delete' } - }, - current: 'details' - }); + const selection = initSelectionContext<PageFragment>('Page', (p) => p.path); - export let data: PageData; - $: result = comicQuery(client, { id: data.id }); - - let comic: FullComicFragment; - let original: Readonly<FullComicFragment>; - let updatePartial = false; - - $: $result, update(); - $: pending = !comicEquals(comic, original); - $: $tabContext.tabs.edit.badge = pending; - - function update() { - if (!$result.stale && $result.data?.comic.__typename === 'FullComic') { - original = $result.data.comic; - if (updatePartial) { - comic.pages = structuredClone(original.pages); - comic.favourite = original.favourite; - comic.bookmarked = original.bookmarked; - comic.organized = original.organized; - comic.updatedAt = original.updatedAt; - updatePartial = false; - } else { - comic = structuredClone(original); - } - - $reader.pages = original.pages; - $selection.view = comic.pages; - $scraper.selector = undefined; - } + let comic: FullComicFragment | undefined = $state(); + let input: OmitIdentifiers<FullComicFragment> | undefined = $state(); + let updateInput = $state(true); + + function invalidateInput() { + updateInput = true; } - function toggle(field: keyof Omit<UpdateComicInput, 'cover'>) { - updateComics(client, { ids: comic.id, input: { [field]: !comic[field] } }) - .then(() => (updatePartial = true)) - .catch(toastFinally); + function submit(input: UpdateComicInput) { + updateComics(client, { ids: data.id, input }).then(invalidateInput).catch(toastFinally); } - function updateComic(event: CustomEvent<UpdateComicInput>) { - updateComics(client, { ids: comic.id, input: event.detail }).catch(toastFinally); + function toggle(field: keyof Pick<UpdateComicInput, 'bookmarked' | 'favourite' | 'organized'>) { + if (!comic) return; + updateComics(client, { ids: data.id, input: { [field]: !comic[field] } }).catch(toastFinally); } - function updateCover(event: CustomEvent<number>) { - updateComics(client, { ids: comic.id, input: { cover: { id: event.detail } } }) - .then(() => (updatePartial = true)) - .catch(toastFinally); + function updateCover(id: number) { + updateComics(client, { ids: data.id, input: { cover: { id } } }).catch(toastFinally); } function removePages() { updateComics(client, { - ids: comic.id, - input: { pages: { ids: $selection.ids, options: { mode: UpdateMode.Remove } } } + ids: data.id, + input: { pages: { ids: selection.ids, options: { mode: UpdateMode.Remove } } } }) - .then(() => { - updatePartial = true; - $selection = $selection.clear(); - }) + .then(selection.clear) .catch(toastFinally); } beforeNavigate((navigation) => preventOnPending(navigation, pending)); + let result = $derived(comicQuery(client, { id: data.id })); + let pending = $derived(comicPending(comic, input)); + + $effect(() => { + if (!$result.stale) { + untrack(() => { + if ($result.data?.comic.__typename === 'FullComic') { + comic = $result.data.comic; + + if (updateInput) { + input = omitIdentifiers($result.data.comic); + updateInput = false; + } + + reader.pages = comic.pages; + selection.view = comic.pages; + scraper.reset(); + } + }); + } + }); </script> -<Head section="Comic" title={original?.title} /> +<Head section="Comic" title={comic?.title} /> -{#if comic} +{#if comic && input} <Grid> <header> <Titlebar - title={original.title} - subtitle={original.originalTitle} - bind:favourite={comic.favourite} - on:favourite={() => toggle('favourite')} + title={comic.title} + subtitle={comic.originalTitle} + favourite={comic.favourite} + onfavourite={() => toggle('favourite')} /> </header> <aside> - <Tabs> - <Tab id="details"> - <ComicDetails comic={original} /> + <Tabs badges={{ edit: pending }}> + <Tab initial id="details" title="Details"> + <ComicDetails {comic} /> </Tab> - <Tab id="edit"> + <Tab id="edit" title="Edit"> <div class="flex flex-col gap-4"> <div class="flex gap-2 text-sm"> <SelectionControls page> - <RemovePageButton on:click={removePages} /> + <RemovePageButton onclick={removePages} /> </SelectionControls> - <div class="grow" /> - <BookmarkButton bookmarked={comic.bookmarked} on:click={() => toggle('bookmarked')} /> - <OrganizedButton organized={comic.organized} on:click={() => toggle('organized')} /> + <div class="grow"></div> + <BookmarkButton bookmarked={comic.bookmarked} onclick={() => toggle('bookmarked')} /> + <OrganizedButton organized={comic.organized} onclick={() => toggle('organized')} /> </div> - <ComicForm bind:comic on:submit={updateComic}> + <ComicForm bind:input {submit}> <div class="flex gap-2"> - <div class="grow" /> - <SubmitButton active={pending} /> + <div class="grow"></div> + <SubmitButton {pending} /> </div> </ComicForm> </div> </Tab> - <Tab id="scrape"> - <ComicScrapeForm {comic} /> + <Tab id="scrape" title="Scrape"> + <ComicScrapeForm {comic} onupsert={invalidateInput} /> </Tab> - <Tab id="deletion"> + <Tab id="deletion" title="Delete"> <ComicDelete {comic} /> </Tab> </Tabs> </aside> <main class="overflow-auto"> - <Gallery - pages={comic.pages} - on:open={(e) => ($reader = $reader.open(e.detail))} - on:cover={updateCover} - /> + <Gallery pages={comic.pages} open={reader.open} {updateCover} /> </main> </Grid> <Reader> - <PageView layout={comic.layout} direction={comic.direction} /> - <svelte:fragment slot="sidebar"> - <ComicForm bind:comic on:submit={updateComic}> - <div class="flex justify-end gap-2"> - <SubmitButton active={pending} /> - </div> - </ComicForm> - </svelte:fragment> + <PageView layout={input.layout} direction={input.direction} /> + {#snippet sidebar()} + {#if input} + <ComicForm bind:input {submit}> + <div class="flex justify-end gap-2"> + <SubmitButton {pending} /> + </div> + </ComicForm> + {/if} + {/snippet} </Reader> {:else} <Guard {result} /> diff --git a/frontend/src/routes/namespaces/+page.svelte b/frontend/src/routes/namespaces/+page.svelte index f6568f9..429432f 100644 --- a/frontend/src/routes/namespaces/+page.svelte +++ b/frontend/src/routes/namespaces/+page.svelte @@ -3,10 +3,8 @@ import { fetchNamespace, namespacesQuery } from '$gql/Queries'; import type { Namespace } from '$gql/graphql'; import { NamespaceSortLabel } from '$lib/Enums'; - import { BasicFilterContext, initFilterContext } from '$lib/Filter'; - import { initPaginationContext } from '$lib/Pagination'; - import { initSelectionContext } from '$lib/Selection'; - import { initSortContext } from '$lib/Sort'; + import { NamespaceFilterContext } from '$lib/Filter.svelte'; + import { quickComicFilter } from '$lib/Navigation'; import { toastFinally } from '$lib/Toasts'; import AddButton from '$lib/components/AddButton.svelte'; import Cardlet from '$lib/components/Cardlet.svelte'; @@ -19,82 +17,87 @@ import EditNamespace from '$lib/dialogs/EditNamespace.svelte'; import Pagination from '$lib/pagination/Pagination.svelte'; import Selectable from '$lib/selection/Selectable.svelte'; + import { initSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionOverlay from '$lib/selection/SelectionOverlay.svelte'; import DeleteSelection from '$lib/toolbar/DeleteSelection.svelte'; + import FilterOrphaned from '$lib/toolbar/FilterOrphaned.svelte'; import Search from '$lib/toolbar/Search.svelte'; import SelectItems from '$lib/toolbar/SelectItems.svelte'; import SelectSort from '$lib/toolbar/SelectSort.svelte'; import SelectionControls from '$lib/toolbar/SelectionControls.svelte'; import Toolbar from '$lib/toolbar/Toolbar.svelte'; import { getContextClient } from '@urql/svelte'; - import { openModal } from 'svelte-modals'; - import type { PageData } from './$types'; + import { modals } from 'svelte-modals'; + import type { PageProps } from './$types'; + + let { data }: PageProps = $props(); + let pagination = $derived(data.pagination); + let sort = $derived(data.sort); const client = getContextClient(); - export let data: PageData; + let result = $derived(namespacesQuery(client, { ...data })); + let namespaces = $derived($result.data?.namespaces); - $: result = namespacesQuery(client, { - pagination: data.pagination, - filter: data.filter, - sort: data.sort + let selection = initSelectionContext<Namespace>('Namespace', (n) => n.name); + $effect(() => { + if (namespaces) { + selection.view = namespaces.edges; + } }); - $: namespaces = $result.data?.namespaces; - - const selection = initSelectionContext<Namespace>('Namespace', (n) => n.name); - $: if (namespaces) { - $selection.view = namespaces.edges; - $pagination.total = namespaces.count; - } - - const filter = initFilterContext<BasicFilterContext>(); - $: $filter = new BasicFilterContext(data.filter); - - const sort = initSortContext(data.sort, NamespaceSortLabel); - $: $sort.update = data.sort; - - const pagination = initPaginationContext(); - $: $pagination.update = data.pagination; + let filter = $state(new NamespaceFilterContext(data.filter)); + $effect(() => { + filter = new NamespaceFilterContext(data.filter); + }); const edit = (id: number) => { fetchNamespace(client, id) - .then((namespace) => openModal(EditNamespace, { namespace })) + .then((namespace) => modals.open(EditNamespace, { namespace })) .catch(toastFinally); }; + + const quickFilter = (id: number) => quickComicFilter(`${id}:`, 'tags'); </script> <Head section="Namespaces" /> <Column> <Toolbar> - <SelectionControls slot="start"> - <DeleteSelection mutation={deleteNamespaces} /> - </SelectionControls> - <svelte:fragment slot="center"> - <Search name="Namespaces" bind:field={$filter.include.controls.name.contains} /> - <SelectSort /> - <SelectItems /> - </svelte:fragment> - <svelte:fragment slot="end"> - <AddButton title="Add Namespace" on:click={() => openModal(AddNamespace)} /> - </svelte:fragment> + {#snippet start()} + <SelectionControls> + <DeleteSelection mutation={deleteNamespaces} /> + </SelectionControls> + {/snippet} + {#snippet center()} + <Search name="Namespaces" {filter} bind:field={filter.include.name.contains} /> + <FilterOrphaned {filter} /> + <SelectSort {sort} labels={NamespaceSortLabel} /> + <SelectItems {pagination} /> + {/snippet} + {#snippet end()} + <AddButton title="Add Namespace" onclick={() => modals.open(AddNamespace)} /> + {/snippet} </Toolbar> {#if namespaces} - <Pagination /> + <Pagination {pagination} total={namespaces.count} /> <main> <Cardlets> {#each namespaces.edges as { id, name }, index (id)} - <Selectable {index} {id} {edit} let:handle let:selected> - <Cardlet {name} on:click={handle} filter="tags" id={`${id}:`}> - <SelectionOverlay slot="overlay" position="right" centered {selected} /> - </Cardlet> + <Selectable {index} {id} onclick={edit} onauxclick={quickFilter}> + {#snippet children({ onclick, onauxclick, selected })} + <Cardlet {name} {onclick} {onauxclick}> + {#snippet overlay()} + <SelectionOverlay position="right" centered {selected} /> + {/snippet} + </Cardlet> + {/snippet} </Selectable> {:else} <Empty /> {/each} </Cardlets> </main> - <Pagination /> + <Pagination {pagination} total={namespaces.count} /> {:else} <Guard {result} /> {/if} diff --git a/frontend/src/routes/statistics/+page.svelte b/frontend/src/routes/statistics/+page.svelte new file mode 100644 index 0000000..53c56a4 --- /dev/null +++ b/frontend/src/routes/statistics/+page.svelte @@ -0,0 +1,46 @@ +<script lang="ts"> + import { statisticsQuery } from '$gql/Queries'; + import Guard from '$lib/components/Guard.svelte'; + import Head from '$lib/components/Head.svelte'; + import Stat from '$lib/statistics/Stat.svelte'; + import StatGroup from '$lib/statistics/StatGroup.svelte'; + import { getContextClient } from '@urql/svelte'; + + let query = $derived(statisticsQuery(getContextClient())); + let totals = $derived($query.data?.statistics.total); +</script> + +<Head section="Statistics" /> + +{#if $query.data && totals} + <div class="flex flex-row flex-wrap gap-8"> + <StatGroup title="Content"> + <Stat title="Archives" value={totals.archives} /> + <Stat title="Comics" value={totals.comics} /> + <Stat title="Images" value={totals.images} /> + <Stat title="Pages" value={totals.pages} /> + <Stat title="Dedup" value={(1 - totals.images / totals.pages) * 100} precision={2} unit="%" /> + </StatGroup> + <StatGroup title="Metadata"> + <Stat title="Namespaces" value={totals.namespaces} /> + <Stat title="Tags" value={totals.tags} /> + <Stat title="Artists" value={totals.artists} /> + <Stat title="Circles" value={totals.circles} /> + <Stat title="Characters" value={totals.characters} /> + <Stat title="Worlds" value={totals.worlds} /> + </StatGroup> + <StatGroup title="Average per Comic"> + <Stat title="Tags" value={totals.comic.tags / totals.comics} precision={2} /> + <Stat title="Artists" value={totals.comic.artists / totals.comics} precision={2} /> + <Stat title="Circles" value={totals.comic.circles / totals.comics} precision={2} /> + <Stat title="Characters" value={totals.comic.characters / totals.comics} precision={2} /> + <Stat title="Worlds" value={totals.comic.worlds / totals.comics} precision={2} /> + </StatGroup> + <StatGroup title="Average per Archive"> + <Stat title="Comics" value={totals.comics / totals.archives} precision={2} /> + <Stat title="Images" value={totals.images / totals.archives} precision={2} /> + </StatGroup> + </div> +{:else} + <Guard result={query} /> +{/if} diff --git a/frontend/src/routes/statistics/+page.ts b/frontend/src/routes/statistics/+page.ts new file mode 100644 index 0000000..d3c3250 --- /dev/null +++ b/frontend/src/routes/statistics/+page.ts @@ -0,0 +1 @@ +export const trailingSlash = 'always'; diff --git a/frontend/src/routes/tags/+page.svelte b/frontend/src/routes/tags/+page.svelte index e0909ad..2cb6f87 100644 --- a/frontend/src/routes/tags/+page.svelte +++ b/frontend/src/routes/tags/+page.svelte @@ -3,10 +3,8 @@ import { fetchTag, tagsQuery } from '$gql/Queries'; import { type Tag } from '$gql/graphql'; import { TagSortLabel } from '$lib/Enums'; - import { TagFilterContext, initFilterContext } from '$lib/Filter'; - import { initPaginationContext } from '$lib/Pagination'; - import { initSelectionContext } from '$lib/Selection'; - import { initSortContext } from '$lib/Sort'; + import { TagFilterContext } from '$lib/Filter.svelte'; + import { quickComicFilter } from '$lib/Navigation'; import { toastFinally } from '$lib/Toasts'; import AddButton from '$lib/components/AddButton.svelte'; import Cardlet from '$lib/components/Cardlet.svelte'; @@ -17,13 +15,15 @@ import Column from '$lib/containers/Column.svelte'; import AddTag from '$lib/dialogs/AddTag.svelte'; import EditTag from '$lib/dialogs/EditTag.svelte'; - import UpdateTagsDialog from '$lib/dialogs/UpdateTags.svelte'; + import UpdateTags from '$lib/dialogs/UpdateTags.svelte'; import TagFilterForm from '$lib/filter/TagFilterForm.svelte'; import Pagination from '$lib/pagination/Pagination.svelte'; import Selectable from '$lib/selection/Selectable.svelte'; + import { initSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionOverlay from '$lib/selection/SelectionOverlay.svelte'; import DeleteSelection from '$lib/toolbar/DeleteSelection.svelte'; import EditSelection from '$lib/toolbar/EditSelection.svelte'; + import FilterOrphaned from '$lib/toolbar/FilterOrphaned.svelte'; import Search from '$lib/toolbar/Search.svelte'; import SelectItems from '$lib/toolbar/SelectItems.svelte'; import SelectSort from '$lib/toolbar/SelectSort.svelte'; @@ -31,78 +31,83 @@ import ToggleAdvancedFilters from '$lib/toolbar/ToggleAdvancedFilters.svelte'; import Toolbar from '$lib/toolbar/Toolbar.svelte'; import { getContextClient } from '@urql/svelte'; - import { openModal } from 'svelte-modals'; - import type { PageData } from './$types'; + import { modals } from 'svelte-modals'; + import type { PageProps } from './$types'; - const client = getContextClient(); - - export let data: PageData; + let { data }: PageProps = $props(); + let pagination = $derived(data.pagination); + let sort = $derived(data.sort); - $: result = tagsQuery(client, { - pagination: data.pagination, - filter: data.filter, - sort: data.sort - }); - - $: tags = $result.data?.tags; + const client = getContextClient(); + let result = $derived(tagsQuery(client, { ...data })); + let tags = $derived($result.data?.tags); const selection = initSelectionContext<Tag>('Tag', (t) => t.name); - $: if (tags) { - $selection.view = tags.edges; - $pagination.total = tags.count; - } - - const filter = initFilterContext<TagFilterContext>(); - $: $filter = new TagFilterContext(data.filter); - - const sort = initSortContext(data.sort, TagSortLabel); - $: $sort.update = data.sort; + $effect(() => { + if (tags) { + selection.view = tags.edges; + } + }); - const pagination = initPaginationContext(); - $: $pagination.update = data.pagination; + let filter = $state(new TagFilterContext(data.filter)); + let filterSize = $derived(filter.includes + filter.excludes); + $effect(() => { + filter = new TagFilterContext(data.filter); + }); const edit = (id: number) => { fetchTag(client, id) - .then((tag) => openModal(EditTag, { tag })) + .then((tag) => modals.open(EditTag, { tag })) .catch(toastFinally); }; + + const quickFilter = (id: number) => quickComicFilter(`:${id}`, 'tags'); </script> <Head section="Tags" /> <Column> <Toolbar> - <SelectionControls slot="start"> - <EditSelection dialog={UpdateTagsDialog} /> - <DeleteSelection mutation={deleteTags} /> - </SelectionControls> - <svelte:fragment slot="center"> - <Search name="Tags" bind:field={$filter.include.controls.name.contains} /> - <ToggleAdvancedFilters /> - <SelectSort /> - <SelectItems /> - </svelte:fragment> - <svelte:fragment slot="end"> - <AddButton title="Add Tag" on:click={() => openModal(AddTag)} /> - </svelte:fragment> - <TagFilterForm /> + {#snippet start()} + <SelectionControls> + <EditSelection dialog={UpdateTags} /> + <DeleteSelection mutation={deleteTags} /> + </SelectionControls> + {/snippet} + {#snippet center({ expanded, toggle })} + <Search name="Tags" {filter} bind:field={filter.include.name.contains} /> + <ToggleAdvancedFilters {expanded} {toggle} {filterSize} /> + <FilterOrphaned {filter} /> + <SelectSort {sort} labels={TagSortLabel} /> + <SelectItems {pagination} /> + {/snippet} + {#snippet end()} + <AddButton title="Add Tag" onclick={() => modals.open(AddTag)} /> + {/snippet} + {#snippet expansion()} + <TagFilterForm {filter} /> + {/snippet} </Toolbar> {#if tags} - <Pagination /> + <Pagination {pagination} total={tags.count} /> <main> <Cardlets> {#each tags.edges as { id, name, description }, index (id)} - <Selectable {index} {id} {edit} let:handle let:selected> - <Cardlet {name} title={description} on:click={handle} filter="tags" id={`:${id}`}> - <SelectionOverlay slot="overlay" position="right" centered {selected} /> - </Cardlet> + <Selectable {index} {id} onclick={edit} onauxclick={quickFilter}> + {#snippet children({ onclick, onauxclick, selected })} + <Cardlet {name} title={description} {onclick} {onauxclick}> + {#snippet overlay()} + <SelectionOverlay position="right" centered {selected} /> + {/snippet} + </Cardlet> + {/snippet} </Selectable> {:else} <Empty /> {/each} </Cardlets> </main> - <Pagination /> + <Pagination {pagination} total={tags.count} /> {:else} <Guard {result} /> {/if} diff --git a/frontend/src/routes/worlds/+page.svelte b/frontend/src/routes/worlds/+page.svelte index e0366e9..133dc27 100644 --- a/frontend/src/routes/worlds/+page.svelte +++ b/frontend/src/routes/worlds/+page.svelte @@ -3,10 +3,8 @@ import { fetchWorld, worldsQuery } from '$gql/Queries'; import type { World } from '$gql/graphql'; import { WorldSortLabel } from '$lib/Enums'; - import { BasicFilterContext, initFilterContext } from '$lib/Filter'; - import { initPaginationContext } from '$lib/Pagination'; - import { initSelectionContext } from '$lib/Selection'; - import { initSortContext } from '$lib/Sort'; + import { BasicFilterContext } from '$lib/Filter.svelte'; + import { quickComicFilter } from '$lib/Navigation'; import { toastFinally } from '$lib/Toasts'; import AddButton from '$lib/components/AddButton.svelte'; import Cardlet from '$lib/components/Cardlet.svelte'; @@ -19,83 +17,87 @@ import EditWorld from '$lib/dialogs/EditWorld.svelte'; import Pagination from '$lib/pagination/Pagination.svelte'; import Selectable from '$lib/selection/Selectable.svelte'; + import { initSelectionContext } from '$lib/selection/Selection.svelte'; import SelectionOverlay from '$lib/selection/SelectionOverlay.svelte'; import DeleteSelection from '$lib/toolbar/DeleteSelection.svelte'; + import FilterOrphaned from '$lib/toolbar/FilterOrphaned.svelte'; import Search from '$lib/toolbar/Search.svelte'; import SelectItems from '$lib/toolbar/SelectItems.svelte'; import SelectSort from '$lib/toolbar/SelectSort.svelte'; import SelectionControls from '$lib/toolbar/SelectionControls.svelte'; import Toolbar from '$lib/toolbar/Toolbar.svelte'; import { getContextClient } from '@urql/svelte'; - import { openModal } from 'svelte-modals'; - import type { PageData } from './$types'; + import { modals } from 'svelte-modals'; + import type { PageProps } from './$types'; - const client = getContextClient(); + let { data }: PageProps = $props(); + let pagination = $derived(data.pagination); + let sort = $derived(data.sort); - export let data: PageData; + const client = getContextClient(); + let result = $derived(worldsQuery(client, { ...data })); + let worlds = $derived($result.data?.worlds); - $: result = worldsQuery(client, { - pagination: data.pagination, - filter: data.filter, - sort: data.sort + let selection = initSelectionContext<World>('World', (a) => a.name); + $effect(() => { + if (worlds) { + selection.view = worlds.edges; + } }); - $: worlds = $result.data?.worlds; - - const selection = initSelectionContext<World>('World', (w) => w.name); - $: if (worlds) { - $selection.view = worlds.edges; - $pagination.total = worlds.count; - } - - const filter = initFilterContext<BasicFilterContext>(); - $: $filter = new BasicFilterContext(data.filter); - - const sort = initSortContext(data.sort, WorldSortLabel); - $: $sort.update = data.sort; - - const pagination = initPaginationContext(); - $: $pagination.update = data.pagination; + let filter = $state(new BasicFilterContext(data.filter)); + $effect(() => { + filter = new BasicFilterContext(data.filter); + }); const edit = (id: number) => { fetchWorld(client, id) - .then((world) => openModal(EditWorld, { world })) + .then((world) => modals.open(EditWorld, { world })) .catch(toastFinally); }; + + const quickFilter = (id: number) => quickComicFilter(id, 'worlds'); </script> -<Head section="Worlds" /> +<Head section="worlds" /> <Column> <Toolbar> - <SelectionControls slot="start"> - <DeleteSelection mutation={deleteWorlds} /> - </SelectionControls> - <svelte:fragment slot="center"> - <Search name="Worlds" bind:field={$filter.include.controls.name.contains} /> - <SelectSort /> - <SelectItems /> - </svelte:fragment> - <svelte:fragment slot="end"> - <AddButton title="Add World" on:click={() => openModal(AddWorld)} /> - </svelte:fragment> + {#snippet start()} + <SelectionControls> + <DeleteSelection mutation={deleteWorlds} /> + </SelectionControls> + {/snippet} + {#snippet center()} + <Search name="Worlds" {filter} bind:field={filter.include.name.contains} /> + <FilterOrphaned {filter} /> + <SelectSort {sort} labels={WorldSortLabel} /> + <SelectItems {pagination} /> + {/snippet} + {#snippet end()} + <AddButton title="Add World" onclick={() => modals.open(AddWorld)} /> + {/snippet} </Toolbar> {#if worlds} - <Pagination /> + <Pagination {pagination} total={worlds.count} /> <main> <Cardlets> {#each worlds.edges as { id, name }, index (id)} - <Selectable {index} {id} {edit} let:handle let:selected> - <Cardlet {name} on:click={handle} filter="worlds" {id}> - <SelectionOverlay slot="overlay" position="right" centered {selected} /> - </Cardlet> + <Selectable {index} {id} onclick={edit} onauxclick={quickFilter}> + {#snippet children({ onclick, onauxclick, selected })} + <Cardlet {name} {onclick} {onauxclick}> + {#snippet overlay()} + <SelectionOverlay position="right" centered {selected} /> + {/snippet} + </Cardlet> + {/snippet} </Selectable> {:else} <Empty /> {/each} </Cardlets> </main> - <Pagination /> + <Pagination {pagination} total={worlds.count} /> {:else} <Guard {result} /> {/if} diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js index dbfdf53..6b76a53 100644 --- a/frontend/svelte.config.js +++ b/frontend/svelte.config.js @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ import adapter from '@sveltejs/adapter-static'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; import { readFileSync } from 'fs'; diff --git a/frontend/tailwind.config.cjs b/frontend/tailwind.config.cjs index 99307cd..d8b3776 100644 --- a/frontend/tailwind.config.cjs +++ b/frontend/tailwind.config.cjs @@ -1,7 +1,4 @@ -const { addDynamicIconSelectors } = require('@iconify/tailwind'); - /** @type {import('tailwindcss').Config} */ module.exports = { - content: ['./src/**/*.{html,js,svelte,ts}'], - plugins: [addDynamicIconSelectors()] + content: ['./src/**/*.{html,js,svelte,ts}'] }; diff --git a/frontend/tests/Reader.test.ts b/frontend/tests/Reader.test.ts index e12d69b..5113e65 100644 --- a/frontend/tests/Reader.test.ts +++ b/frontend/tests/Reader.test.ts @@ -1,5 +1,6 @@ import { Layout, type PageFragment } from '$gql/graphql'; -import { partition, type Chunk } from '$lib/Reader'; +import { partition, type Chunk } from '$lib/Reader.svelte'; + import { expect, test } from 'vitest'; const normalAttrs = { aspectRatio: 0.7, width: 140, height: 200 }; diff --git a/frontend/tests/Selection.test.ts b/frontend/tests/Selection.test.ts index 67e8c4c..1bdbb55 100644 --- a/frontend/tests/Selection.test.ts +++ b/frontend/tests/Selection.test.ts @@ -1,4 +1,4 @@ -import { ItemSelection } from '$lib/Selection'; +import { ItemSelection } from '$lib/selection/Selection.svelte'; import { expect, test } from 'vitest'; interface TestItem { @@ -17,100 +17,104 @@ const all = items.map((i) => i.id); const selectable = items.filter((i) => i.selectable).map((i) => i.id); const setup = () => { - const selection = new ItemSelection<TestItem>(); + const selection = new ItemSelection<TestItem>('test', (t) => t.id.toString()); selection.view = items; return selection; }; test('selects a single item', () => { - let selection = setup(); + const selection = setup(); - selection = selection.update(0, false); + selection.update(0, false); expect(selection.ids).toStrictEqual([items[0].id]); }); test('selects a single item (with empty shift select)', () => { - let selection = setup(); + const selection = setup(); - selection = selection.update(0, true); + selection.update(0, true); expect(selection.ids).toStrictEqual([items[0].id]); }); test('selects multiple items (forwards)', () => { - let selection = setup(); + const selection = setup(); - selection = selection.update(0, false); - selection = selection.update(2, true); + selection.update(0, false); + selection.update(2, true); expect(selection.ids.toSorted((a, b) => a - b)).toStrictEqual(all.slice(0, 3)); }); test('selects multiple items (backwards)', () => { - let selection = setup(); + const selection = setup(); - selection = selection.update(2, false); - selection = selection.update(0, true); + selection.update(2, false); + selection.update(0, true); expect(selection.ids.toSorted((a, b) => a - b)).toStrictEqual(all.slice(0, 3)); }); test('selects multiple items (only selectables)', () => { - let selection = setup(); + const selection = setup(); selection.selectable = (i) => i.selectable; - selection = selection.update(0, false); - selection = selection.update(3, true); + selection.update(0, false); + selection.update(3, true); expect(selection.ids).toStrictEqual(selectable); }); test('selects all', () => { - const selection = setup().all(); + const selection = setup(); + + selection.all(); expect(selection.ids).toStrictEqual(all); }); test('selects all selectables', () => { - let selection = setup(); + const selection = setup(); selection.selectable = (i) => i.selectable; - selection = selection.all(); + selection.all(); expect(selection.ids).toStrictEqual(selectable); }); test('deselects all', () => { - let selection = setup().all(); + const selection = setup(); - selection = selection.none(); + selection.all(); + selection.none(); - expect(selection.ids).empty; + expect(selection.ids).toHaveLength(0); }); test('deselects a single item', () => { - let selection = setup().all(); + const selection = setup(); - selection = selection.update(0, false); + selection.all(); + selection.update(0, false); expect(selection.ids).toStrictEqual(all.slice(1)); }); test('deselects multiple items', () => { - let selection = setup(); + const selection = setup(); - selection = selection.update(0, false); - selection = selection.update(2, true); - selection = selection.update(2, true); + selection.update(0, false); + selection.update(2, true); + selection.update(2, true); - expect(selection.ids).empty; + expect(selection.ids).toHaveLength(0); }); test('retains selection', () => { - let selection = setup(); + const selection = setup(); - selection = selection.all(); + selection.all(); selection.view = items.slice(0, 2); expect(selection.ids).toStrictEqual(all.slice(0, 2)); @@ -125,40 +129,40 @@ test('is inactive by default', () => { }); test('is inactive after clearing', () => { - let selection = setup(); + const selection = setup(); selection.active = true; - selection = selection.clear(); - expect(selection.active).false; + selection.clear(); + expect(selection.active).toBeFalsy(); }); test('can be toggled', () => { - let selection = setup(); + const selection = setup(); - selection = selection.toggle(); + selection.toggle(); expect(selection.active).toBe(true); - selection = selection.all(); - selection = selection.toggle(); + selection.all(); + selection.toggle(); expect(selection.active).toBe(false); - expect(selection.ids).empty; + expect(selection.ids).toHaveLength(0); }); test('can be cleared', () => { - let selection = setup(); + const selection = setup(); - selection = selection.all(); + selection.all(); - selection = selection.clear(); - expect(selection.ids).empty; + selection.clear(); + expect(selection.ids).toHaveLength(0); }); test('reports selected items', () => { - let selection = setup(); + const selection = setup(); - selection = selection.update(0, false); - selection = selection.update(2, false); + selection.update(0, false); + selection.update(2, false); expect(selection.contains(all[0])).toBeTruthy(); expect(selection.contains(all[1])).toBeFalsy(); @@ -167,13 +171,17 @@ test('reports selected items', () => { }); test('reports size', () => { - const selection = setup().all(); + const selection = setup(); + + selection.all(); expect(selection.size).toBe(all.length); }); test('reports size of visible items', () => { - const selection = setup().all(); + const selection = setup(); + + selection.all(); selection.view = items.slice(0, 2); expect(selection.size).toBe(2); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 95c4753..a0d6da6 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -10,7 +10,9 @@ "sourceMap": true, "strict": true, "target": "ES2022", - "module": "ES2022" + "module": "ES2022", + "verbatimModuleSyntax": true, + "isolatedModules": true } // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias // diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 145901f..eeabcf6 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,11 +1,9 @@ import { sveltekit } from '@sveltejs/kit/vite'; +import tailwindcss from '@tailwindcss/vite'; import { defineConfig } from 'vite'; export default defineConfig({ - plugins: [sveltekit()], - server: { - fs: { - allow: ['objects'] - } - } + plugins: [tailwindcss(), sveltekit()], + server: { fs: { allow: ['objects'] } }, + test: { environment: 'jsdom' } }); |