diff --git a/frontend/.eslintignore b/frontend/.eslintignore deleted file mode 100644 index e04fc3a..0000000 --- a/frontend/.eslintignore +++ /dev/null @@ -1,8 +0,0 @@ -dist -node_modules -*.d.ts -vite.config.ts -vitest.config.ts -postcss.config.js -tailwind.config.js -components.json diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs deleted file mode 100644 index 5df62b4..0000000 --- a/frontend/.eslintrc.cjs +++ /dev/null @@ -1,62 +0,0 @@ -module.exports = { - root: true, - env: { - browser: true, - es2021: true, - node: true, - }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:vue/vue3-recommended', - ], - parser: 'vue-eslint-parser', - parserOptions: { - ecmaVersion: 'latest', - parser: '@typescript-eslint/parser', - sourceType: 'module', - }, - plugins: ['@typescript-eslint', 'vue'], - rules: { - // TypeScript 规则 - '@typescript-eslint/no-unused-vars': [ - 'error', - { - argsIgnorePattern: '^_', - varsIgnorePattern: '^_', - }, - ], - '@typescript-eslint/no-explicit-any': 'warn', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-non-null-assertion': 'warn', - - // Vue 规则 - 'vue/multi-word-component-names': 'off', - 'vue/no-v-html': 'error', // 防止 XSS 攻击 - 'vue/component-api-style': ['error', ['script-setup']], - 'vue/component-name-in-template-casing': ['error', 'PascalCase'], - 'vue/custom-event-name-casing': ['error', 'camelCase'], - 'vue/define-macros-order': [ - 'error', - { - order: ['defineProps', 'defineEmits'], - }, - ], - 'vue/html-comment-content-spacing': ['error', 'always'], - 'vue/no-unused-refs': 'error', - 'vue/no-useless-v-bind': 'error', - 'vue/padding-line-between-blocks': ['error', 'always'], - 'vue/prefer-separate-static-class': 'error', - - // 一般规则 - 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn', - 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'warn', - 'prefer-const': 'error', - 'no-var': 'error', - 'object-shorthand': ['error', 'always'], - 'prefer-template': 'error', - 'prefer-arrow-callback': 'error', - }, - ignorePatterns: ['dist', 'node_modules', '*.config.js', '*.config.ts'], -} diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..0365492 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,186 @@ +import js from '@eslint/js' +import tseslint from 'typescript-eslint' +import pluginVue from 'eslint-plugin-vue' +import vueParser from 'vue-eslint-parser' + +export default [ + // 忽略的文件和目录 + { + ignores: ['dist/**', 'node_modules/**', '*.config.js', '*.config.ts'], + }, + + // JavaScript 基础配置 + js.configs.recommended, + + // TypeScript 配置 + ...tseslint.configs.recommended, + + // Vue 配置 + ...pluginVue.configs['flat/recommended'], + + // 全局配置 + { + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + globals: { + // 浏览器全局变量 + window: 'readonly', + document: 'readonly', + navigator: 'readonly', + console: 'readonly', + localStorage: 'readonly', + sessionStorage: 'readonly', + fetch: 'readonly', + URL: 'readonly', + URLSearchParams: 'readonly', + FormData: 'readonly', + Blob: 'readonly', + File: 'readonly', + FileReader: 'readonly', + HTMLElement: 'readonly', + HTMLInputElement: 'readonly', + HTMLSelectElement: 'readonly', + MouseEvent: 'readonly', + KeyboardEvent: 'readonly', + Event: 'readonly', + EventTarget: 'readonly', + CustomEvent: 'readonly', + MutationObserver: 'readonly', + ResizeObserver: 'readonly', + IntersectionObserver: 'readonly', + requestAnimationFrame: 'readonly', + cancelAnimationFrame: 'readonly', + setTimeout: 'readonly', + clearTimeout: 'readonly', + setInterval: 'readonly', + clearInterval: 'readonly', + queueMicrotask: 'readonly', + // Node.js 全局变量 + process: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + module: 'readonly', + require: 'readonly', + exports: 'readonly', + Buffer: 'readonly', + // DOM/SVG 全局类型 + alert: 'readonly', + confirm: 'readonly', + prompt: 'readonly', + Node: 'readonly', + NodeList: 'readonly', + Element: 'readonly', + SVGPathElement: 'readonly', + SVGElement: 'readonly', + DOMParser: 'readonly', + XMLSerializer: 'readonly', + getComputedStyle: 'readonly', + performance: 'readonly', + PerformanceObserver: 'readonly', + Image: 'readonly', + Audio: 'readonly', + WebSocket: 'readonly', + Worker: 'readonly', + SharedWorker: 'readonly', + ServiceWorker: 'readonly', + crypto: 'readonly', + atob: 'readonly', + btoa: 'readonly', + TextEncoder: 'readonly', + TextDecoder: 'readonly', + AbortController: 'readonly', + AbortSignal: 'readonly', + Headers: 'readonly', + Request: 'readonly', + Response: 'readonly', + ClipboardItem: 'readonly', + Selection: 'readonly', + Range: 'readonly', + matchMedia: 'readonly', + history: 'readonly', + location: 'readonly', + open: 'readonly', + close: 'readonly', + print: 'readonly', + scrollTo: 'readonly', + scrollBy: 'readonly', + getSelection: 'readonly', + Intl: 'readonly', + globalThis: 'readonly', + // Canvas/WebGL + HTMLCanvasElement: 'readonly', + CanvasRenderingContext2D: 'readonly', + WebGLRenderingContext: 'readonly', + WebGL2RenderingContext: 'readonly', + ImageData: 'readonly', + Path2D: 'readonly', + OffscreenCanvas: 'readonly', + }, + }, + }, + + // Vue 文件配置 + { + files: ['**/*.vue'], + languageOptions: { + parser: vueParser, + parserOptions: { + parser: tseslint.parser, + ecmaVersion: 'latest', + sourceType: 'module', + }, + }, + rules: { + // Vue 规则 + 'vue/multi-word-component-names': 'off', + 'vue/no-v-html': 'error', + 'vue/component-api-style': ['error', ['script-setup']], + 'vue/component-name-in-template-casing': ['error', 'PascalCase'], + 'vue/custom-event-name-casing': ['error', 'camelCase'], + 'vue/define-macros-order': [ + 'error', + { + order: ['defineProps', 'defineEmits'], + }, + ], + 'vue/html-comment-content-spacing': ['error', 'always'], + 'vue/no-unused-refs': 'error', + 'vue/no-useless-v-bind': 'error', + 'vue/padding-line-between-blocks': ['error', 'always'], + 'vue/prefer-separate-static-class': 'error', + }, + }, + + // TypeScript 文件配置 + { + files: ['**/*.ts', '**/*.tsx', '**/*.vue'], + rules: { + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-non-null-assertion': 'warn', + }, + }, + + // 通用规则 + { + files: ['**/*.js', '**/*.ts', '**/*.tsx', '**/*.vue'], + rules: { + 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'warn', + 'prefer-const': 'error', + 'no-var': 'error', + 'object-shorthand': ['error', 'always'], + 'prefer-template': 'error', + 'prefer-arrow-callback': 'error', + }, + }, +] diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 633fa18..2962f47 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -31,6 +31,7 @@ "vue-router": "^4.5.1" }, "devDependencies": { + "@eslint/js": "^9.39.1", "@types/node": "^24.3.3", "@typescript-eslint/eslint-plugin": "^8.47.0", "@typescript-eslint/parser": "^8.47.0", @@ -46,6 +47,7 @@ "tailwindcss": "^3.4.17", "tailwindcss-animate": "^1.0.7", "typescript": "~5.8.3", + "typescript-eslint": "^8.49.0", "vite": "^7.1.2", "vitest": "^4.0.10", "vue-tsc": "^3.0.5" @@ -1640,18 +1642,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz", - "integrity": "sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz", + "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/type-utils": "8.47.0", - "@typescript-eslint/utils": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", - "graphemer": "^1.4.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/type-utils": "8.49.0", + "@typescript-eslint/utils": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" @@ -1664,22 +1665,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.47.0", + "@typescript-eslint/parser": "^8.49.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz", - "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz", + "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4" }, "engines": { @@ -1695,14 +1696,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz", - "integrity": "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz", + "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.47.0", - "@typescript-eslint/types": "^8.47.0", + "@typescript-eslint/tsconfig-utils": "^8.49.0", + "@typescript-eslint/types": "^8.49.0", "debug": "^4.3.4" }, "engines": { @@ -1717,14 +1718,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz", - "integrity": "sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz", + "integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0" + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1735,9 +1736,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz", - "integrity": "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz", + "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==", "dev": true, "license": "MIT", "engines": { @@ -1752,15 +1753,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz", - "integrity": "sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz", + "integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0", - "@typescript-eslint/utils": "8.47.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1777,9 +1778,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz", - "integrity": "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz", + "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==", "dev": true, "license": "MIT", "engines": { @@ -1791,21 +1792,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz", - "integrity": "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz", + "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.47.0", - "@typescript-eslint/tsconfig-utils": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", + "@typescript-eslint/project-service": "8.49.0", + "@typescript-eslint/tsconfig-utils": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", + "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "engines": { @@ -1820,16 +1820,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz", - "integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz", + "integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0" + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1844,13 +1844,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz", - "integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz", + "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/types": "8.49.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3805,13 +3805,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -6042,6 +6035,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.49.0.tgz", + "integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.49.0", + "@typescript-eslint/parser": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0" + }, + "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 <6.0.0" + } + }, "node_modules/undici-types": { "version": "7.10.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5bbd977..bea325e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "test": "vitest", "test:ui": "vitest --ui", "test:run": "vitest run", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .eslintignore", + "lint": "eslint . --fix", "type-check": "vue-tsc --noEmit" }, "dependencies": { @@ -38,6 +38,7 @@ "vue-router": "^4.5.1" }, "devDependencies": { + "@eslint/js": "^9.39.1", "@types/node": "^24.3.3", "@typescript-eslint/eslint-plugin": "^8.47.0", "@typescript-eslint/parser": "^8.47.0", @@ -53,6 +54,7 @@ "tailwindcss": "^3.4.17", "tailwindcss-animate": "^1.0.7", "typescript": "~5.8.3", + "typescript-eslint": "^8.49.0", "vite": "^7.1.2", "vitest": "^4.0.10", "vue-tsc": "^3.0.5"