From 1adacea3eb4da4746fcc87db7f746e1fc532869c Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 15:33:03 +0800 Subject: [PATCH 01/24] build(frontend): scaffold vite react typescript workspace The MS2 frontend needs a reproducible workspace before any module code can land. Add the Vite entry, the pinned dependency manifest and lockfile, the three referenced TypeScript configs, and the ignore rules. The workspace installs and builds from a clean checkout. --- .gitignore | 4 + frontend/.gitignore | 28 + frontend/index.html | 12 + frontend/package-lock.json | 3630 +++++++++++++++++++++++++++++++++++ frontend/package.json | 36 + frontend/tsconfig.app.json | 31 + frontend/tsconfig.json | 4 + frontend/tsconfig.node.json | 23 + frontend/vite.config.ts | 16 + 9 files changed, 3784 insertions(+) create mode 100644 .gitignore create mode 100644 frontend/.gitignore create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2603d1a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# macOS +.DS_Store + +# 依赖与构建产物由各子项目的 .gitignore 负责 diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..00baad0 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,28 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Vercel 本地项目链接元数据,不提交账号和项目 ID +.vercel + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.env* diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..cd1df0c --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + Windup · 2D 角色资产生成 + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..d513d30 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,3630 @@ +{ + "name": "windup-frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "windup-frontend", + "version": "0.0.0", + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router": "^8.3.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.3.3", + "@testing-library/react": "^16.3.2", + "@types/node": "^24.13.2", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "jsdom": "^29.1.1", + "oxfmt": "^0.61.0", + "oxlint": "^1.71.0", + "tailwindcss": "^4.3.3", + "typescript": "~6.0.2", + "vite": "^8.1.1", + "vitest": "^4.1.10" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz", + "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.10.tgz", + "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.1.0", + "@csstools/css-calc": "^3.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz", + "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@oxfmt/binding-android-arm-eabi": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.61.0.tgz", + "integrity": "sha512-BaS+1OVvg9sr+Xav0+KdWedQRcAzrdoEcwMZeqoc2F6ieC1s/t5eM35YQoRPQ7vAqkZ+p3tbQb1r9I9mrV5oGA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-android-arm64": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.61.0.tgz", + "integrity": "sha512-of8atAV0M1egGcVOMbgZCvc10sFOP3ayQBNQV5h5G3fNq8gACdEswfFk9bzGrdbM23rtg0Coxi7np7oPLcueNw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-darwin-arm64": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.61.0.tgz", + "integrity": "sha512-7l8+5ov4BGwtAcmpzvEik/TG3bciwyw/S3e6j5GKH7pcQqcgCVxD3AuJeP6upto+SOTBKQ4wrrdbMt0gq8fHSQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-darwin-x64": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.61.0.tgz", + "integrity": "sha512-Fnz4dDDXBb7udk+DmwelNjxbD6yptyxwCqwCH2ebo4RVLxVsRfFsn/AHJC49KIltPrVokamGv4SSOsiV50DTxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-freebsd-x64": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.61.0.tgz", + "integrity": "sha512-mddOebKNCP+AucmzfNsk3jgbr681qAUvgMqi865GW5gWLJ/AnzXbvjQRrny0e++NAN8aphav/aRSrfFxNsNjpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm-gnueabihf": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.61.0.tgz", + "integrity": "sha512-svx59iYL+DbaZGZUIoice4W0CjRXGExnbz7Re+awIb60rVxBS2KrU7Hnlx+nZYanLGLpjneUEgo/VFEKkSZAyQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm-musleabihf": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.61.0.tgz", + "integrity": "sha512-BYK9MPJPCf6d+fLKMTruThmEyCtHzQ1zLcsrTlUVkmnoXIaHAbfpeLYQwX1tkjs7W11dyzoi6HFvKcdnvX1zNg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm64-gnu": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.61.0.tgz", + "integrity": "sha512-QUaCNLq2/EC6G5ljOuFanl9Lgw6ZWp4co7rs4+KOMUzbGfA4Lq58FHRjjF9sVIG+93XSbo343MxFATrOU1qctA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm64-musl": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.61.0.tgz", + "integrity": "sha512-S6uvJ6MXnRXl+zTs0CARNDvkE+cymj0EVWEKKsyKnlLlqTyQJBjw5s4D2pSIOZc+S46cy4STefzcr/sm0VzVPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-ppc64-gnu": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.61.0.tgz", + "integrity": "sha512-6VDlRcytvZG6UlSIdAFKDLbppo9tvPxrWzle6vHldYFMeuDPQEfMKrkwezp7FaBq1wik9ra554ZZeRPsyIkFpg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-riscv64-gnu": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.61.0.tgz", + "integrity": "sha512-KkBTYbzExpbmn15XjKPLu2fRV2PVlq+KWt+brad5rwIa03vdYoaDRWiS7raHII/dCTR6Ro4UpYUCH4t6lif4WQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-riscv64-musl": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.61.0.tgz", + "integrity": "sha512-69tzIq7sJLVB9dxYYtvMzcSSsnZHSO+U2U19O2RqDqgj6+Q4O7HjSXdaszbcgqzhsUwzSH7z5kWvk8nmf6BHTg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-s390x-gnu": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.61.0.tgz", + "integrity": "sha512-Oqi/N0OvtOVXsPKAOOhKgGH3msRYF8BLJaNBbWiupRiKoKVyc8JRKPCfarkQJC+RgP9U8raUKLe+bNwd0HUMiA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-x64-gnu": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.61.0.tgz", + "integrity": "sha512-3TKwv/ed4uwJSemAA8P9XcoqETpjQI4waquF9UilhA9Mn/dhr1PdUEXWlL74mtc6ZNfmKPA9+NEJm01nRF8CVA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-x64-musl": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.61.0.tgz", + "integrity": "sha512-uFso4u4nLkVSlMCpgjyvWV60Gt7GvDQHnk1mmRxHIkZTMB0ljpUKwCD9FYGgN9H97x2wYl0UwEjgRZaPIuhEhw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-openharmony-arm64": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.61.0.tgz", + "integrity": "sha512-keGLkzeOvkMpNmPp4hffXWpfoSsY6e1K8++KXD4mSSfxdvM8q9QUDsYY689TB1k6Co832DZn1MnaaVx6cIBMWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-arm64-msvc": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.61.0.tgz", + "integrity": "sha512-VzsAISkFxmNhJ5LBDEL9VuH6tJsVJMtqYit2LyIUf/HLnsCe4Pg9SMOjjVQzGWt0bnpyfJ94CrqTqcpNZzK+ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-ia32-msvc": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.61.0.tgz", + "integrity": "sha512-xv4t7yzwJoYaLB6Zv28B3W+j7brEjsyv50rLTAQgmxJzddce9fAMCxed8dSAkbWES0zz2J29nYK5FaTuD2YBHg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-x64-msvc": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.61.0.tgz", + "integrity": "sha512-6EZXFkqOwxdDYjIn3TSNnPk3ST5E5GiYd4FiM6UF/mCL/LZSfr6D6UygTfW3R1PCQP2quCKpCEGRlij8E3VYbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.75.0.tgz", + "integrity": "sha512-lutovtFzJqlRaqpZrCqSSGaHZzl9nIxxpjLzhSRLunN6dCLylj0uzlCyQGaQDIys7rrv8kVXiFO+R4Zpn0bX7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.75.0.tgz", + "integrity": "sha512-hXI0hDgHkw4w5nfru72aG7y+2iQJmC4waH/KV6H/hbgA6yAP5jYNx0P9yug15Hs0tWl/+mda3Jjn/2gmDT48tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.75.0.tgz", + "integrity": "sha512-D91BWbK/dMYfCcrghspPIuKs2D9LF4Z/OabVSQjw1AO6PWxArD7teDA48bm0ySFqWDaPVqmQRl5GMWNglTXyrQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.75.0.tgz", + "integrity": "sha512-02mpwzf12BonZ6PT0TuQoomvEh2kVl2WGBIKWezCyToIS+rYkQZ6GXnARBAl9A4Ovm2V+Xe7M4KretyqmmcnJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.75.0.tgz", + "integrity": "sha512-qZJgLnDaBsiL5YESx2t/TZ8eXkL9fEkKoXEdzegROhlz9A0lgyGnZ0dAzJrh7LJAHQl2K9RdRueN2s/9N7+odg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.75.0.tgz", + "integrity": "sha512-7XlaWA5BJD3XpCfrEqjEe6Zseeb14S7QGa304XfwKignRaKQ+eIj775BQ7nIslggWickl4IsPUFqJ+/gAyNHVg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.75.0.tgz", + "integrity": "sha512-av6Tpv8yrcMMMOadOqENBhlsLRcGFXXwoQ0hzHhsmS9FJ4Wioy8we427GbcMe2XTxmL2e60T67H1Dyr3up+tAA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.75.0.tgz", + "integrity": "sha512-WcUhd8fHT5plrA14lANevl+hOl815mVI5t2hU21oFWrZKFXIVV/Sr4rWQV0NzSvzBupbMLNc5ErEA6Ehxh5jMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.75.0.tgz", + "integrity": "sha512-UWzp5wRHFe/ESO3+eEaxXsTkYTGLYjnTsi/I5neEacXSItQ6WNleapfOAeA4x2b8nyhJ4uQxqvtv9pHv8kWJtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.75.0.tgz", + "integrity": "sha512-XEVRwGMLKCUKrvhLAz4F6AIh8MJrQVdSZtAmPpRZt9tGPsUnamPOcl3dS/ZQzJnar/Ymgc//+xho0L60Emzuxg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.75.0.tgz", + "integrity": "sha512-mAG4DUXqfLC8cTjMD2kt3jDmVzFREYtDyeLNdLdsCcBc4Zbl2EMuiFektGBilQwkNjYnMvCqJs55U+Hyb+b+jw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.75.0.tgz", + "integrity": "sha512-95hrAvriAlI+pekSomTFIn0+bawMDlDwTNVmdjsFusTHyL2JWh7TWvRNG/Lkim72uN8OiCcO9wcaC6omLP5E3w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.75.0.tgz", + "integrity": "sha512-4b6f2+FrtruAESrCqIKcrarzfrSx+wk2QNcp+RT91/Prc+pMQMAfyZ1rG1c3tFQNl8Bc616tx40uNXyxNBRPbQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.75.0.tgz", + "integrity": "sha512-nshAhrUvXFUWOvqQ2soIw7HFNWvpvEV4o0cYSqPtzLiPF5gKyYTDOOTJ6Rn8g8K/iGvPIrbDA4v8+5MvnjJrrg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.75.0.tgz", + "integrity": "sha512-e4jNxLKnxLC6sYBQRxrI2pgIIxnmMtF8U/VwNYcjTT/CLS+spH624cYVnj07bTKwaEWT37/e025isOs6j/0xqA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.75.0.tgz", + "integrity": "sha512-hZ2lH+1qLf/DiEP9UWuQTK2JWj/BgvMB4jhIV4SmNU1wfEiYYX4TynQyAZXx0j9X4qRYizAL042SKaV+8ynh4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.75.0.tgz", + "integrity": "sha512-Ilj6PNzGDS3bCU0MSJH7Msh0NhH+T/mRp2shwg+q+GHeVlPwP5LEboW96aW+3kVKFk6zYZy1Xi5pZkqZh6X8KQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.75.0.tgz", + "integrity": "sha512-QVit2nOEOiPhkmsrksPSkoGCdnZRNkspt8fwoYyP09te1VEbnSj4LAxua4rc8FKTmWkySVe05j8iz9GXYfF1AQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.75.0.tgz", + "integrity": "sha512-DSxnNkBUAYARPwJtR12Ig3deWr8w0H997xP6jy33i+e0SyYJw8FKuz4+cZtpmPEhQmvlPJE3X/2vNxDmLkd/rA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "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-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "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/@tailwindcss/node/node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "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/@tailwindcss/node/node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "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/@tailwindcss/node/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "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/@tailwindcss/node/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "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/@tailwindcss/node/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "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/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "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.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.4.tgz", + "integrity": "sha512-XcCQz0TBpBgljhj0gMuuDj49i6Ytqh5q1osT/Gp5uAVJUCTWxyskk/l1jwYYiu2xcNHHipdMz40EGfM1VdamVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.10", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-3.1.1.tgz", + "integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==", + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/enhanced-resolve": { + "version": "5.24.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.3.tgz", + "integrity": "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "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" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.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/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "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-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "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/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/oxfmt": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.61.0.tgz", + "integrity": "sha512-DxdHBEMYpcEnHoUHjjOigUqV2TYKsvxLwUPXnVYBjgFdqrcQ/91OtwubtZ2PUodCs3sStI8R5Qw3fKNGK4e8wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinypool": "2.1.0" + }, + "bin": { + "oxfmt": "bin/oxfmt" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxfmt/binding-android-arm-eabi": "0.61.0", + "@oxfmt/binding-android-arm64": "0.61.0", + "@oxfmt/binding-darwin-arm64": "0.61.0", + "@oxfmt/binding-darwin-x64": "0.61.0", + "@oxfmt/binding-freebsd-x64": "0.61.0", + "@oxfmt/binding-linux-arm-gnueabihf": "0.61.0", + "@oxfmt/binding-linux-arm-musleabihf": "0.61.0", + "@oxfmt/binding-linux-arm64-gnu": "0.61.0", + "@oxfmt/binding-linux-arm64-musl": "0.61.0", + "@oxfmt/binding-linux-ppc64-gnu": "0.61.0", + "@oxfmt/binding-linux-riscv64-gnu": "0.61.0", + "@oxfmt/binding-linux-riscv64-musl": "0.61.0", + "@oxfmt/binding-linux-s390x-gnu": "0.61.0", + "@oxfmt/binding-linux-x64-gnu": "0.61.0", + "@oxfmt/binding-linux-x64-musl": "0.61.0", + "@oxfmt/binding-openharmony-arm64": "0.61.0", + "@oxfmt/binding-win32-arm64-msvc": "0.61.0", + "@oxfmt/binding-win32-ia32-msvc": "0.61.0", + "@oxfmt/binding-win32-x64-msvc": "0.61.0" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/oxlint": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.75.0.tgz", + "integrity": "sha512-m9WzjRcRYA/uqIZDa9tclrieoPJ/ln1QYTKdFx6NUOs8uY5DiHlIwRQoCrHT6OM6O3ww3l2skY5gO7G7ZphE7g==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.75.0", + "@oxlint/binding-android-arm64": "1.75.0", + "@oxlint/binding-darwin-arm64": "1.75.0", + "@oxlint/binding-darwin-x64": "1.75.0", + "@oxlint/binding-freebsd-x64": "1.75.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.75.0", + "@oxlint/binding-linux-arm-musleabihf": "1.75.0", + "@oxlint/binding-linux-arm64-gnu": "1.75.0", + "@oxlint/binding-linux-arm64-musl": "1.75.0", + "@oxlint/binding-linux-ppc64-gnu": "1.75.0", + "@oxlint/binding-linux-riscv64-gnu": "1.75.0", + "@oxlint/binding-linux-riscv64-musl": "1.75.0", + "@oxlint/binding-linux-s390x-gnu": "1.75.0", + "@oxlint/binding-linux-x64-gnu": "1.75.0", + "@oxlint/binding-linux-x64-musl": "1.75.0", + "@oxlint/binding-openharmony-arm64": "1.75.0", + "@oxlint/binding-win32-arm64-msvc": "1.75.0", + "@oxlint/binding-win32-ia32-msvc": "1.75.0", + "@oxlint/binding-win32-x64-msvc": "1.75.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=7.0.2001", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/pathe": { + "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" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/react-router": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-8.3.0.tgz", + "integrity": "sha512-qyPMvW83jGIct3yiieisxdk9M745anqhpIMKN5m1t6yBMfgVPpt77aHOqs5fUlEJRMCGffg9BaQLH9oPVOL7xQ==", + "license": "MIT", + "dependencies": { + "cookie-es": "^3.1.1" + }, + "engines": { + "node": ">=22.22.0" + }, + "peerDependencies": { + "react": ">=19.2.7", + "react-dom": ">=19.2.7" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "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" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, + "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" + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-2.1.0.tgz", + "integrity": "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.4.9", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.9.tgz", + "integrity": "sha512-3kZ8wQQ/k5DrChD4X4FVvr2D7E5uoRgAqkPyLpSCGUvqOvqu+JEdr3mwMUaVWb+vMHZaKhF5fp2PBigKsui7hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.9" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.9", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.9.tgz", + "integrity": "sha512-DxKfPBI52p2msTEu7MPhdpdDTBhhVQg1a/8PjQckeyAvO13eMYElX545grIp6nnTGIMZlRvFZPvFhvI/WIz2Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "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/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "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", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "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" + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..0a845f7 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,36 @@ +{ + "name": "windup-frontend", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "oxlint", + "format": "oxfmt", + "format:check": "oxfmt --check", + "preview": "vite preview", + "test": "vitest run --passWithNoTests", + "typecheck": "tsc -b" + }, + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router": "^8.3.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.3.3", + "@testing-library/react": "^16.3.2", + "@types/node": "^24.13.2", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "jsdom": "^29.1.1", + "oxfmt": "^0.61.0", + "oxlint": "^1.71.0", + "tailwindcss": "^4.3.3", + "typescript": "~6.0.2", + "vite": "^8.1.1", + "vitest": "^4.1.10" + } +} diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000..474b9e6 --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023", "DOM"], + "module": "esnext", + "types": ["vite/client"], + "allowArbitraryExtensions": true, + "skipLibCheck": true, + + /* 路径别名:@/xxx -> src/xxx,需与 vite.config.ts 的 resolve.alias 保持一致 */ + "paths": { + "@/*": ["./src/*"] + }, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..d32ff68 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,4 @@ +{ + "files": [], + "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..8455dcb --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "module": "nodenext", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..b928223 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,16 @@ +import { fileURLToPath, URL } from 'node:url' + +import tailwindcss from '@tailwindcss/vite' +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss()], + resolve: { + alias: { + // 全项目统一用 @/xxx 引用 src 下的模块,避免 ../../.. 相对路径 + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, +}) From 059d3759313875c725418bc6733d26bc5198092f Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 15:33:03 +0800 Subject: [PATCH 02/24] build(frontend): add oxlint and oxfmt configuration Style and lint rules should be fixed before contributors start writing modules. Add the oxlint rule set and the oxfmt formatting profile for the frontend workspace. Formatting and lint results are identical on every machine and in CI. --- frontend/.oxfmtrc.json | 9 +++++++++ frontend/.oxlintrc.json | 8 ++++++++ 2 files changed, 17 insertions(+) create mode 100644 frontend/.oxfmtrc.json create mode 100644 frontend/.oxlintrc.json diff --git a/frontend/.oxfmtrc.json b/frontend/.oxfmtrc.json new file mode 100644 index 0000000..293ef6a --- /dev/null +++ b/frontend/.oxfmtrc.json @@ -0,0 +1,9 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "endOfLine": "lf", + "printWidth": 100, + "semi": false, + "singleQuote": true, + "trailingComma": "all", + "ignorePatterns": ["**/*.md"] +} diff --git a/frontend/.oxlintrc.json b/frontend/.oxlintrc.json new file mode 100644 index 0000000..6fa991d --- /dev/null +++ b/frontend/.oxlintrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["react", "typescript", "oxc"], + "rules": { + "react/rules-of-hooks": "error", + "react/only-export-components": ["warn", { "allowConstantExport": true }] + } +} From 980d5112407a036ff860f00b000970877c50fdd2 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 15:33:03 +0800 Subject: [PATCH 03/24] build(frontend): add vitest configuration The skeleton carries no test suite yet, but the test runner has to be wired for later work. Add the vitest config so it resolves the same path aliases as the application build. Tests can be added to any module without further setup. --- frontend/vitest.config.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 frontend/vitest.config.ts diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts new file mode 100644 index 0000000..3a40db6 --- /dev/null +++ b/frontend/vitest.config.ts @@ -0,0 +1,21 @@ +import { fileURLToPath, URL } from 'node:url' + +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + test: { + // 纯逻辑测试跑 node;需要 DOM 的用文件顶部 @vitest-environment jsdom 单独声明 + environment: 'node', + include: [ + 'src/**/*.test.ts', + 'src/**/*.test.tsx', + '../tests/**/*.test.ts', + '../tests/**/*.test.tsx', + ], + }, +}) From fd4424a5bcfbdd727f9d620b6dde5c9c9966f858 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 15:33:24 +0800 Subject: [PATCH 04/24] feat(shared): add shared layer with pagination contract Every upper layer needs a place for cross-module primitives that belong to no single domain. Add the shared layer with its pagination contract and a README stating what may live here. Modules have one agreed home for shared primitives instead of duplicating them. --- frontend/src/shared/README.md | 28 +++++++++++++++++++++++++ frontend/src/shared/pagination/index.ts | 13 ++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 frontend/src/shared/README.md create mode 100644 frontend/src/shared/pagination/index.ts diff --git a/frontend/src/shared/README.md b/frontend/src/shared/README.md new file mode 100644 index 0000000..b35fb60 --- /dev/null +++ b/frontend/src/shared/README.md @@ -0,0 +1,28 @@ +# shared + +与 Windup 业务无关、可被任意上层模块复用的基础代码。它是依赖方向的最底层,**不能反向依赖 `entities`、`features`、`pages` 或 `app`**。 + +## 判断标准 + +**如果一段代码需要理解 Windup 的业务词汇,它就不属于 shared。** + +## 现有内容 + +- `pagination/` —— 与传输协议无关的分页请求与结果形状。 + +## 后续允许放入 + +- `ui/` —— 按钮、弹窗、加载状态等不含业务含义的展示组件 +- `hooks/` —— 通用浏览器或 React 行为,例如媒体查询、键盘快捷键 +- `utils/` —— 纯函数工具,例如日期格式化、文件大小显示 +- `config/` —— 前端通用常量与运行时配置读取 + +**这些目录只在出现真实代码时创建,不为占位提前建空文件。** + +## 不允许放入 + +- Project、Character、Generation、Task、WorkflowRun 等业务数据 +- `ProjectApis`、`CharacterApis` 这类业务接口集合 +- 流程的推进、重启、中断和 Revision 规则 +- 为开发与生产各维护一套实现的切换机制 +- 只被单个页面或模块使用、却以「复用」名义提前抽出的代码 diff --git a/frontend/src/shared/pagination/index.ts b/frontend/src/shared/pagination/index.ts new file mode 100644 index 0000000..d5e2c27 --- /dev/null +++ b/frontend/src/shared/pagination/index.ts @@ -0,0 +1,13 @@ +/** 与传输协议无关的分页请求形状。 */ +export interface PageQuery { + page?: number + pageSize?: number +} + +/** 与传输协议无关的分页结果形状。 */ +export interface Paged { + items: T[] + total: number + page: number + pageSize: number +} From 7e22b51e16ad9744ccbf6072907170147de5c24c Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 15:33:24 +0800 Subject: [PATCH 05/24] feat(entities): add data layer public interfaces The frontend has to state which business objects it owns and how each one is reached. Add one contract file per entity for character, project, task, generation, media, action template, playtest inspection and workflow run, plus the aggregate export. Upper layers depend on declared entity interfaces rather than on transport details. --- .../src/entities/action-template/index.ts | 14 ++ frontend/src/entities/character/index.ts | 138 ++++++++++++++++ frontend/src/entities/generation/index.ts | 108 ++++++++++++ frontend/src/entities/index.ts | 86 ++++++++++ frontend/src/entities/media/index.ts | 9 + .../src/entities/playtest-inspection/index.ts | 29 ++++ frontend/src/entities/project/index.ts | 78 +++++++++ frontend/src/entities/task/index.ts | 45 +++++ frontend/src/entities/workflow-run/index.ts | 155 ++++++++++++++++++ 9 files changed, 662 insertions(+) create mode 100644 frontend/src/entities/action-template/index.ts create mode 100644 frontend/src/entities/character/index.ts create mode 100644 frontend/src/entities/generation/index.ts create mode 100644 frontend/src/entities/index.ts create mode 100644 frontend/src/entities/media/index.ts create mode 100644 frontend/src/entities/playtest-inspection/index.ts create mode 100644 frontend/src/entities/project/index.ts create mode 100644 frontend/src/entities/task/index.ts create mode 100644 frontend/src/entities/workflow-run/index.ts diff --git a/frontend/src/entities/action-template/index.ts b/frontend/src/entities/action-template/index.ts new file mode 100644 index 0000000..2dafa39 --- /dev/null +++ b/frontend/src/entities/action-template/index.ts @@ -0,0 +1,14 @@ +interface ActionTemplateBase { + id: string + name: string + prompt: string +} + +/** 系统内置模板没有项目归属;项目自定义模板必须携带所属 Project ID。 */ +export type ActionTemplate = ActionTemplateBase & + ({ scope: 'system'; projectId: null } | { scope: 'project'; projectId: string }) + +/** ActionTemplate 对应的一组后端接口。 */ +export interface ActionTemplateApis { + listAvailable(projectId: string): Promise +} diff --git a/frontend/src/entities/character/index.ts b/frontend/src/entities/character/index.ts new file mode 100644 index 0000000..db8315b --- /dev/null +++ b/frontend/src/entities/character/index.ts @@ -0,0 +1,138 @@ +/** + * 动作「如何被定义」的来源维度:preset 复用预设定义,custom 由用户自定义。 + * 它与动作做什么的 ActionType 相互独立,例如 custom + walk 和 preset + custom 都是合法组合。 + */ +export type ActionKind = 'preset' | 'custom' + +/** + * 动作「做什么」的业务语义维度;custom 表示不属于当前内置语义枚举。 + * 它不表示定义来源:custom 来源仍可描述 walk,preset 来源也可承载 custom 业务语义。 + */ +export type ActionType = 'walk' | 'idle' | 'attack' | 'jump' | 'custom' + +/** 动作在生成流水线上的位置。 */ +export type ActionStatus = + /** 已加入工作流但还没开始生成 */ + | 'planned' + /** 后端任务进行中 */ + | 'generating' + /** 生成完成,是候选,还没被用户确认 */ + | 'candidate' + /** 用户确认后成为正式资产 */ + | 'confirmed' + /** 生成失败 */ + | 'failed' + +/** 系统质检结论。系统通过不等于人工通过,分开记。 */ +export type FrameQcResult = 'pending' | 'passed' | 'failed' + +/** 单帧相对动作首帧的根位移,单位为像素。 */ +export interface FrameRootMotion { + dx: number + /** 正值表示向上。 */ + dy: number +} + +/** 动作序列中的一张有序画面;帧序号由其在 Action.frames 中的位置决定。 */ +export interface Frame { + imageUrl: string + /** + * 此帧的显示时长,单位毫秒。 + * null 表示该帧没有独立时长,读取方才使用所属 Action.fps 计算等时长回退值。 + */ + durationMs: number | null + /** null 表示不提供根位移,Playtest 与 Export 不应据此施加任何位移。 */ + rootMotion: FrameRootMotion | null + /** 后端自动质检结论,与人工 rejected 状态相互独立。 */ + qc: FrameQcResult + /** 人工退回。不设「通过此帧」,故是布尔量而非三态。 */ + rejected: boolean +} + +/** 一张母版候选;attemptId 用于区分候选所属的生成尝试。 */ +export interface CharacterTemplateCandidate { + id: string + imageUrl: string + attemptId: string +} + +/** 确认母版候选时同时命名造型与候选,避免两个字符串 ID 在调用处颠倒。 */ +export interface ConfirmCharacterTemplateInput { + outfitId: string + candidateId: string +} + +/** 造型的基础参考帧;方向与质检结构等待真实资产契约后再扩展。 */ +export interface BaseFrame { + readonly imageUrl: string +} + +/** 某个角色造型下的一段动画动作。 */ +export interface Action { + id: string + outfitId: Outfit['id'] + name: string + /** 定义来源方式;与 type 正交,不用于推断动作业务语义。 */ + kind: ActionKind + /** 动作业务语义;与 kind 的 preset/custom 来源维度相互独立。 */ + type: ActionType + status: ActionStatus + /** + * 每秒播放帧数。仅当某帧 durationMs 为 null 时用于等时长回退; + * Playtest 与 Export 不得用前端全局常量替代,也不得覆盖帧自己的 durationMs。 + */ + fps: number + /** + * 攻击触点、跳跃顶点等关键时刻在 frames 中的零基下标;null 表示没有明确关键帧。 + * 非 null 值必须指向当前 frames 数组内的成员。 + */ + keyFrameIndex: number | null + /** 按播放顺序排列的帧;数组下标就是零基帧序号。 */ + frames: Frame[] +} + +/** 同一角色的一套独立造型;MVP UI 只展示第一套,但数据结构不折叠该层。 */ +export interface Outfit { + id: string + characterId: string + name: string + /** 母版生成阶段返回的候选;生成完成前可以为空数组。 */ + candidateCharacterTemplates: CharacterTemplateCandidate[] + /** 用户从候选图中选定的角色母版 URL;尚未选定时为 null。 */ + characterTemplateUrl: string | null + /** 供后续动作生成使用的只读基础帧入口。 */ + readonly baseFrames: readonly BaseFrame[] + /** 每个 Action.outfitId 必须等于本造型 ID。 */ + actions: Action[] +} + +/** 项目下的角色资产;造型拥有各自的母版和动作帧。 */ +export interface Character { + id: string + projectId: string + name: string + /** 角色的全部独立造型;MVP 页面至少保留这一层,即使当前只有一个成员。 */ + outfits: Outfit[] + createdAt: string + updatedAt: string +} + +/** 创建角色并发起母版生成所需的入参。 */ +export interface CreateCharacterInput { + projectId: string + name: string + /** 交给模型生成母版。 */ + description: string + referenceImageUrl?: string | null +} + +/** + * Character 对应的一组后端接口。 + * 造型、动作和帧是 Character 内的完整树,不通过独立粒度方法写入;每次确认后整棵更新。 + */ +export interface CharacterApis { + get(id: Character['id']): Promise + listByProject(projectId: string): Promise + create(input: CreateCharacterInput): Promise + update(character: Character): Promise +} diff --git a/frontend/src/entities/generation/index.ts b/frontend/src/entities/generation/index.ts new file mode 100644 index 0000000..7c932fc --- /dev/null +++ b/frontend/src/entities/generation/index.ts @@ -0,0 +1,108 @@ +import type { ActionType } from '../character' +import type { MediaReference } from '../media' +import type { TaskStatus } from '../task' + +/** + * Generation 是业务数据,不是「调用图片生成能力」。 + * 前端只创建 generation 并订阅它的状态;真正调用模型的是后端,前端不接触那一层。 + */ + +/** 生成对应的三个前端可见异步步骤。 */ +export type GenerationType = 'character_template' | 'first_frame' | 'complete_animation' + +interface GenerationInputBase { + projectId: string + /** 可选参考媒体;没有参考图时传空数组。 */ + referenceMedia: readonly MediaReference[] +} + +/** 角色母版候选生成。 */ +export interface CharacterTemplateGenerationInput extends GenerationInputBase { + type: 'character_template' + /** 已由手动输入或 Quick Start 整理好的角色提示词。 */ + prompt: string +} + +/** 指定角色造型下的动作首帧生成;不能只绑定 Character。 */ +export interface FirstFrameGenerationInput extends GenerationInputBase { + type: 'first_frame' + characterId: string + outfitId: string + actionType: ActionType + /** 自定义动作或额外动作要求;没有时为 null。 */ + prompt: string | null +} + +/** 以已确认首帧为起点生成完整动画。 */ +export interface CompleteAnimationGenerationInput extends GenerationInputBase { + type: 'complete_animation' + characterId: string + outfitId: string + actionType: ActionType + /** 已确认的生成首帧 URL。 */ + firstFrameUrl: string + prompt: string | null +} + +export type GenerationInput = + | CharacterTemplateGenerationInput + | FirstFrameGenerationInput + | CompleteAnimationGenerationInput + +/** 后端当前能交付给前端的最小图片结果。 */ +export interface GeneratedImage { + url: string +} + +/** 结果按 type 分别定义,不共用一个 urls 数组。 */ +export interface CharacterTemplateGenerationResult { + type: 'character_template' + images: readonly GeneratedImage[] +} + +export interface FirstFrameGenerationResult { + type: 'first_frame' + image: GeneratedImage +} + +/** 帧顺序由数组位置表达。 */ +export interface CompleteAnimationGenerationResult { + type: 'complete_animation' + frames: readonly GeneratedImage[] +} + +export type GenerationResult = + | CharacterTemplateGenerationResult + | FirstFrameGenerationResult + | CompleteAnimationGenerationResult + +export type GenerationResultFor = + T extends CharacterTemplateGenerationInput + ? CharacterTemplateGenerationResult + : T extends FirstFrameGenerationInput + ? FirstFrameGenerationResult + : CompleteAnimationGenerationResult + +/** + * 一次生成任务的完整快照。 + * 它是服务端的资源,不是一次「调用能力」——前端创建它,然后订阅或轮询它的状态。 + */ +export interface Generation { + id: string + projectId: string + /** 与创建时的输入判别字段保持同一字面量类型。 */ + type: TType + status: TaskStatus + /** 完成前为 null;完成后形状由 type 决定。 */ + result: GenerationResult | null + /** status 为 failed 时有值。 */ + error: string | null +} + +/** Generation 对应的一组后端接口。 */ +export interface GenerationApis { + /** 创建一次生成任务。 */ + create(input: T): Promise> + /** 读取生成任务的最新快照。 */ + get(id: Generation['id']): Promise +} diff --git a/frontend/src/entities/index.ts b/frontend/src/entities/index.ts new file mode 100644 index 0000000..804de8b --- /dev/null +++ b/frontend/src/entities/index.ts @@ -0,0 +1,86 @@ +/** + * entities 唯一公开入口。外部不得绕过本文件访问内部文件。 + * 本次只提交类型与接口,不提交实现。 + */ + +/* 项目 —— 全局约束:视角、朝向、精灵尺寸、画风 */ +export { CHARACTER_PERSPECTIVE, DIRECTIONAL_MOVEMENT, SPRITE_SIZES } from './project' +export type { + CharacterPerspective, + CreateProjectInput, + DirectionalMovement, + Project, + ProjectApis, + UpdateProjectInput, +} from './project' + +/* 角色 —— 资产本体;造型、动作、帧都在这棵树里 */ +export type { + Action, + ActionKind, + ActionStatus, + ActionType, + BaseFrame, + Character, + CharacterApis, + CharacterTemplateCandidate, + ConfirmCharacterTemplateInput, + CreateCharacterInput, + Frame, + FrameQcResult, + FrameRootMotion, + Outfit, +} from './character' + +/* 动作模板 —— 能跨角色复用的配方 */ +export type { ActionTemplate, ActionTemplateApis } from './action-template' + +/* 生成 —— 业务数据,不是「调用生成能力」 */ +export type { + CharacterTemplateGenerationInput, + CharacterTemplateGenerationResult, + CompleteAnimationGenerationInput, + CompleteAnimationGenerationResult, + FirstFrameGenerationInput, + FirstFrameGenerationResult, + GeneratedImage, + Generation, + GenerationApis, + GenerationInput, + GenerationResult, + GenerationResultFor, + GenerationType, +} from './generation' + +/* 媒体引用 —— 不承诺 URL 或后端 Media ID 的具体表示 */ +export type { MediaReference } from './media' + +/* 后端异步任务 —— 与工作流节点是两回事 */ +export type { Task, TaskApis, TaskEvent, TaskStatus, TaskType } from './task' + +/* 工作流 —— 节点由前端推进,运行记录由后端持久化 */ +export { WORKFLOW_STEP_ORDER } from './workflow-run' +export type { + CreateWorkflowRunInput, + ExportStatus, + GenerationStatus, + WorkflowDriver, + WorkflowStep, + WorkflowStepStatus, + WorkflowStepType, + WorkflowRevision, + WorkflowRevisionStatus, + WorkflowRun, + WorkflowRunApis, + WorkflowRunPurpose, + WorkflowRunStatus, + WorkflowTaskLink, +} from './workflow-run' + +/* Playtest 核验记录 —— 独立于 Character 与 WorkflowRun */ +export type { + PlaytestInspection, + PlaytestInspectionApis, + PlaytestInspectionStatus, + RecordPlaytestInspectionInput, +} from './playtest-inspection' diff --git a/frontend/src/entities/media/index.ts b/frontend/src/entities/media/index.ts new file mode 100644 index 0000000..347e626 --- /dev/null +++ b/frontend/src/entities/media/index.ts @@ -0,0 +1,9 @@ +declare const mediaReferenceBrand: unique symbol + +/** + * 已上传媒体的不透明引用。 + * 当前不承诺运行时字符串代表 URL、media_id 或其他后端标识。 + */ +export type MediaReference = string & { + readonly [mediaReferenceBrand]: 'MediaReference' +} diff --git a/frontend/src/entities/playtest-inspection/index.ts b/frontend/src/entities/playtest-inspection/index.ts new file mode 100644 index 0000000..e984111 --- /dev/null +++ b/frontend/src/entities/playtest-inspection/index.ts @@ -0,0 +1,29 @@ +/** 可持久化的 Playtest 核验结论;没有记录即表示尚未核验。 */ +export type PlaytestInspectionStatus = 'passed' | 'issues_found' + +/** 一次独立核验记录,不属于 Character 或 WorkflowRevision 的内部字段。 */ +export interface PlaytestInspection { + id: string + characterId: string + outfitId: string + /** 仅从 Workflow 进入时保留返回定位;独立入口为 null。 */ + source: { runId: string; revisionId: string } | null + status: PlaytestInspectionStatus + createdAt: string + updatedAt: string +} + +/** 创建或更新指定核验目标结论所需的业务输入。 */ +export interface RecordPlaytestInspectionInput { + characterId: string + outfitId: string + /** 可选的 Workflow 来源,不是播放和核验的前置条件。 */ + source?: { runId: string; revisionId: string } | null + status: PlaytestInspectionStatus +} + +/** PlaytestInspection 对应的一组后端接口。 */ +export interface PlaytestInspectionApis { + getLatest(target: { characterId: string; outfitId: string }): Promise + record(input: RecordPlaytestInspectionInput): Promise +} diff --git a/frontend/src/entities/project/index.ts b/frontend/src/entities/project/index.ts new file mode 100644 index 0000000..778acf8 --- /dev/null +++ b/frontend/src/entities/project/index.ts @@ -0,0 +1,78 @@ +import type { Paged, PageQuery } from '@/shared/pagination' + +/** Project 前端领域形状;字段只表达当前页面需要,不对应任何已确认后端 DTO。 */ +export interface Project { + id: string + /** Project 所属用户 ID;认证来源尚未冻结。 */ + ownerId: string + name: string + /** 游戏视角,见 CHARACTER_PERSPECTIVE。 */ + perspective: CharacterPerspective + /** 移动方向,见 DIRECTIONAL_MOVEMENT。 */ + directionalMovement: DirectionalMovement + /** 当前页面使用建议档位,后端范围尚未确认。 */ + spriteSize: { width: number; height: number } + /** 项目级画风描述,作为本项目所有角色和动作生成的视觉约束。 */ + gameStyle: string | null + /** + * 项目级画风参考图,本项目所有角色和动作都遵循它的视觉风格。 + * 它不决定某个角色具体长什么样;角色自身参考图由 CreateCharacterInput.referenceImageUrl 表达。 + */ + sampleImageUrl: string | null + /** ISO 8601 字符串。 */ + createdAt: string + /** ISO 8601 字符串。 */ + updatedAt: string +} + +/** 新建项目的入参。 */ +export interface CreateProjectInput { + name: string + perspective: CharacterPerspective + directionalMovement: DirectionalMovement + spriteSize: { width: number; height: number } + gameStyle?: string | null + sampleImageUrl?: string | null +} + +/** 更新项目设置的入参;未提供的字段保持不变。 */ +export interface UpdateProjectInput { + name?: string + perspective?: CharacterPerspective + directionalMovement?: DirectionalMovement + spriteSize?: { width: number; height: number } + gameStyle?: string | null + sampleImageUrl?: string | null +} + +/** 前端使用的游戏视角枚举;后端映射尚未冻结。 */ +export type CharacterPerspective = 'side' | 'top-down' | 'isometric' + +/** 前端使用的移动方向枚举;后端映射尚未冻结。 */ +export type DirectionalMovement = 'single' | 'four-way' | 'eight-way' + +/** 游戏视角的页面文案。 */ +export const CHARACTER_PERSPECTIVE: Record = { + side: '横版视角', + 'top-down': '俯视', + isometric: '2.5D', +} + +/** 移动方向,决定一个动作要生成几套朝向的帧。 */ +export const DIRECTIONAL_MOVEMENT: Record = { + single: '单向', + 'four-way': '四向', + 'eight-way': '八向', +} + +/** UI 使用的建议尺寸档位;不代表后端约束。 */ +export const SPRITE_SIZES = [32, 64, 128, 256, 512, 1024, 2048] as const + +/** Project 对应的一组后端接口。 */ +export interface ProjectApis { + list(query?: PageQuery): Promise> + get(id: Project['id']): Promise + create(input: CreateProjectInput): Promise + update(id: Project['id'], input: UpdateProjectInput): Promise + remove(id: Project['id']): Promise +} diff --git a/frontend/src/entities/task/index.ts b/frontend/src/entities/task/index.ts new file mode 100644 index 0000000..eaccba1 --- /dev/null +++ b/frontend/src/entities/task/index.ts @@ -0,0 +1,45 @@ +/** + * 异步任务实体,与 WorkflowRun 的页面节点是两回事。 + * + * 任务粒度 = 前端可见的一个异步步骤,而不是内部某次模型调用。 + * 任务生命周期不等于工作流节点状态,WorkflowRun 只通过 WorkflowTaskLink 记录两者关系。 + */ + +/** 后端异步任务状态;pending 表示已提交但尚未执行。 */ +export type TaskStatus = 'pending' | 'running' | 'completed' | 'failed' + +/** + * MS2 前端需要展示和恢复的三类异步步骤。 + * 完整动画内部可包含视频生成、截帧和多次图像处理,但对前端仍是一个 Task。 + */ +export type TaskType = 'character_template' | 'first_frame' | 'complete_animation' + +/** + * 创建、查询和断线恢复都使用的完整任务快照。 + * TType 在调用边界已知时保留精确任务类型;按 ID 恢复时使用默认值,等待运行时解析后再收窄。 + */ +export interface Task { + /** 后端生成的任务 ID,是查询状态和订阅事件的唯一标识。 */ + id: string + /** 后端 task_type 对应的领域判别字段,不接受任意字符串。 */ + type: TType + /** 后端任务当前状态;它不会直接改变工作流节点状态。 */ + status: TaskStatus + /** 失败原因,status 为 failed 时有值。completed 不表示工作流节点已通过。 */ + error: string | null + /** 任务产出;契约冻结前保持 unknown,避免调用方依赖猜测结构。 */ + result: unknown +} + +/** 每条事件携带同类型 Task 的完整状态,taskId 对应 Task.id。 */ +export interface TaskEvent extends Omit, 'id'> { + taskId: Task['id'] +} + +/** Task 对应的一组后端接口。服务端没有取消能力,因此这里不声明 cancel。 */ +export interface TaskApis { + /** 按后端 Task ID 读取最新快照;实现必须在运行时校验 task_type 后才返回。 */ + get(taskId: Task['id']): Promise + /** 订阅任务状态变化,返回取消订阅函数。 */ + subscribe(taskId: Task['id'], onEvent: (event: TaskEvent) => void): () => void +} diff --git a/frontend/src/entities/workflow-run/index.ts b/frontend/src/entities/workflow-run/index.ts new file mode 100644 index 0000000..04ec0e2 --- /dev/null +++ b/frontend/src/entities/workflow-run/index.ts @@ -0,0 +1,155 @@ +import type { Task } from '../task' + +/** Quick Start 与手动工作流只改变输入方式,共用同一种运行模型。 */ +export type WorkflowDriver = 'ai' | 'manual' + +/** 创建 WorkflowRun 时要完成的用户意图。 */ +export type WorkflowRunPurpose = 'create_character' | 'add_action' + +/** + * 流程步骤类型的唯一标准顺序;它不是后端 Workflow 或 Execution 定义。 + * 某个 Revision 已进入执行线的步骤顺序,由 WorkflowRevision.nodes 的数组位置表达。 + */ +export const WORKFLOW_STEP_ORDER = [ + 'character-setup', + 'character-template', + 'template-candidate', + 'action-setup', + 'first-frame', + 'complete-animation', + 'review', + 'export', +] as const + +/** 前端流程步骤类型,与 WORKFLOW_STEP_ORDER 的成员保持一致。 */ +export type WorkflowStepType = (typeof WORKFLOW_STEP_ORDER)[number] + +/** + * 步骤的可用性和执行结果;不直接复用后端任务状态。 + * locked/available 表示尚未执行,active 表示当前页面阶段,passed/failed 表示结果。 + */ +export type WorkflowStepStatus = 'locked' | 'available' | 'active' | 'passed' | 'failed' + +/** + * 单个版本的生命周期。 + * abandoned 表示停止沿用但仍保留为历史。 + */ +export type WorkflowRevisionStatus = 'active' | 'completed' | 'failed' | 'abandoned' + +/** + * 整次流程的汇总状态。 + * interrupted 只表示用户主动停止自动推进:历史仍保留且可只读查看,它不等于 failed 或 completed。 + * 后端 Task 是否真正停止是独立问题;从历史重启成功后可重新进入 active。 + */ +export type WorkflowRunStatus = 'active' | 'interrupted' | 'completed' | 'failed' + +/** 当前版本在生成阶段的汇总状态;素材准备期间为 not_started。 */ +export type GenerationStatus = 'not_started' | 'in_progress' | 'completed' | 'failed' + +/** 当前版本在导出阶段的汇总状态。 */ +export type ExportStatus = 'not_exported' | 'exporting' | 'exported' | 'failed' + +/** + * 一个 Revision 中已经进入执行线的流程步骤。 + * 步骤自身不重复保存顺序;其在 nodes 中的数组位置就是该版本的执行顺序。 + */ +export interface WorkflowStep { + /** 只用于编排和页面定位,不作为业务 ID 发送给后端。 */ + id: string + type: WorkflowStepType + status: WorkflowStepStatus + /** 进入步骤时保存的输入快照。 */ + input: unknown + /** 步骤完成后的结果或引用;尚无结果时为 null。 */ + output: unknown + /** 该步骤沿用或依赖的步骤 ID,用于版本来源追踪,不代表后端执行依赖。 */ + referenceStepIds: string[] +} + +/** 一次页面执行版本;当前版本会推进,从旧步骤重开则追加新版本。 */ +export interface WorkflowRevision { + id: string + /** 首次创建的版本没有来源,因此为 null。 */ + basedOnRevisionId: string | null + /** 在来源版本中选择的重启步骤 ID;非重启创建的版本为 null。 */ + restartStepId: string | null + status: WorkflowRevisionStatus + /** + * 已进入当前执行线的步骤;数组位置是该版本步骤顺序的唯一来源。 + * 尚未推进到的后续步骤可以不存在;完整步骤类型顺序以 WORKFLOW_STEP_ORDER 为准。 + */ + steps: WorkflowStep[] + generationStatus: GenerationStatus + exportStatus: ExportStatus + createdAt: string +} + +/** + * 一次由前端推进的页面流程。 + * 步骤怎么走由前端决定;WorkflowRun 本身由后端持久化,前端通过 WorkflowRunApis 读写。 + */ +export interface WorkflowRun { + id: string + projectId: string + /** 已关联的 Character ID;角色尚未创建或确认时为 null。 */ + characterId: string | null + /** 已有角色加动作时的目标造型;新建角色时为 null。 */ + outfitId: string | null + purpose: WorkflowRunPurpose + driver: WorkflowDriver + status: WorkflowRunStatus + /** 当前可编辑版本 ID;必须能在 revisions 中找到。 */ + currentRevisionId: string + /** 按创建顺序保存的全部版本;历史版本保留用于只读查看和重启。 */ + revisions: WorkflowRevision[] + /** Quick Start 的规范化提示词;空白输入或手动模式无提示词时为 null。 */ + prompt: string | null +} + +/** 两种入口共享的创建字段。 */ +interface CreateWorkflowRunInputBase { + projectId: string + driver: WorkflowDriver + /** Quick Start 的自然语言需求;提交时去除首尾空白,空字符串按 null 保存。 */ + prompt?: string +} + +/** + * 创建 WorkflowRun 的输入。 + * add_action 分支把已有角色、造型、母版和基准帧设为必填,避免创建无法恢复的半成品运行。 + */ +export type CreateWorkflowRunInput = CreateWorkflowRunInputBase & + ( + | { + purpose: 'create_character' + characterId?: never + outfitId?: never + characterTemplateUrl?: never + baseFrameUrls?: never + } + | { + purpose: 'add_action' + characterId: string + outfitId: string + characterTemplateUrl: string + baseFrameUrls: readonly string[] + } + ) + +/** 前端编排关联;后端 Task 不需要认识 WorkflowRun、Revision 或流程步骤。 */ +export interface WorkflowTaskLink { + taskId: Task['id'] + runId: WorkflowRun['id'] + /** 发起任务时所在的版本 ID,避免结果写入后来创建的新版本。 */ + revisionId: WorkflowRevision['id'] + /** 发起任务的步骤 ID,用于把结果映射回正确页面阶段。 */ + stepId: WorkflowStep['id'] +} + +/** WorkflowRun 对应的一组后端接口;只负责存取,不负责推进。 */ +export interface WorkflowRunApis { + get(runId: WorkflowRun['id']): Promise + create(input: CreateWorkflowRunInput): Promise + /** 保存已由 WorkflowController 完成合法状态转换的流程快照。 */ + save(run: WorkflowRun): Promise +} From 73d7b981c3011eafe8947e388146536ba9053686 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 15:33:24 +0800 Subject: [PATCH 06/24] feat(features): add use case layer contracts The workflow steps the mentor reviewed need an explicit home separate from both entities and pages. Add contracts for character setup, generation, review, export and the workflow controller. Each use case has a named boundary that pages call instead of reaching into entities. --- .../src/features/character-setup/index.ts | 7 ++ frontend/src/features/export/index.ts | 6 ++ frontend/src/features/generation/index.ts | 6 ++ frontend/src/features/review/index.ts | 7 ++ .../src/features/workflow-controller/index.ts | 67 +++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100644 frontend/src/features/character-setup/index.ts create mode 100644 frontend/src/features/export/index.ts create mode 100644 frontend/src/features/generation/index.ts create mode 100644 frontend/src/features/review/index.ts create mode 100644 frontend/src/features/workflow-controller/index.ts diff --git a/frontend/src/features/character-setup/index.ts b/frontend/src/features/character-setup/index.ts new file mode 100644 index 0000000..44c9a7b --- /dev/null +++ b/frontend/src/features/character-setup/index.ts @@ -0,0 +1,7 @@ +import type { CreateCharacterInput } from '@/entities' + +/** 填写角色资料并提交母版生成。 */ +export interface CharacterSetupProps { + projectId: string + onSubmit(input: CreateCharacterInput): void +} diff --git a/frontend/src/features/export/index.ts b/frontend/src/features/export/index.ts new file mode 100644 index 0000000..adfc5aa --- /dev/null +++ b/frontend/src/features/export/index.ts @@ -0,0 +1,6 @@ +/** 把确认后的造型与动作导出到资产库。 */ +export interface ExportProps { + runId: string + characterId: string + outfitId: string +} diff --git a/frontend/src/features/generation/index.ts b/frontend/src/features/generation/index.ts new file mode 100644 index 0000000..47f5997 --- /dev/null +++ b/frontend/src/features/generation/index.ts @@ -0,0 +1,6 @@ +/** 触发并展示一次生成;不感知后端调用的是哪个模型。 */ +export interface GenerationProps { + runId: string + /** 生成母版等非动作任务可以省略。 */ + actionId?: string +} diff --git a/frontend/src/features/review/index.ts b/frontend/src/features/review/index.ts new file mode 100644 index 0000000..8a897e8 --- /dev/null +++ b/frontend/src/features/review/index.ts @@ -0,0 +1,7 @@ +/** 逐帧审核:打回有问题的帧。不设「通过此帧」,默认通过。 */ +export interface ReviewProps { + actionId: string + frameIndex: number + onSelectFrame(index: number): void + onRejectFrame(index: number): void +} diff --git a/frontend/src/features/workflow-controller/index.ts b/frontend/src/features/workflow-controller/index.ts new file mode 100644 index 0000000..3ccff25 --- /dev/null +++ b/frontend/src/features/workflow-controller/index.ts @@ -0,0 +1,67 @@ +import type { + CreateWorkflowRunInput, + WorkflowRevision, + WorkflowRun, + WorkflowStep, +} from '@/entities' + +/** 更新当前 Revision 中某个步骤的业务数据。 */ +export interface UpdateWorkflowStepInput { + stepId: WorkflowStep['id'] + data: unknown +} + +/** 从指定 Revision 的指定步骤建立新的执行版本。 */ +export interface RestartWorkflowFromStepInput { + revisionId: WorkflowRevision['id'] + stepId: WorkflowStep['id'] +} + +/** 把某次服务端调用的结果写回目标步骤。 */ +export interface ApplyServerResultInput { + /** 发起请求时所属的 Revision,防止旧的异步结果污染重启后的新版本。 */ + revisionId: WorkflowRevision['id'] + stepId: WorkflowStep['id'] + result: unknown +} + +/** + * Quick Start 与手动工作流共用的流程推进边界,不含界面。 + * 两套界面共享同一套流程:手动模式一次推进一步,Quick Start 连续推进到终点。 + * + * Controller 围绕同一份 WorkflowRun 提供推进、更新、重启和中断。这些操作依赖同一份 + * 步骤数据,不拆成互不共享状态的独立模块。 + * + * 步骤怎么走由前端决定,服务端只负责持久化 WorkflowRun 和提供生成能力。 + */ +export interface WorkflowController { + /** 初始化一条创建角色或增加动作的流程。 */ + create(input: CreateWorkflowRunInput): Promise + + /** 读取当前维护的完整流程快照。 */ + getWorkflow(): WorkflowRun + + /** 按前端规则完成当前步骤并进入下一步;需要服务端时创建对应的 generation。 */ + nextStep(): Promise + + /** 连续推进到终点,Quick Start 使用。 */ + runToCompletion(): Promise + + /** 更新指定步骤的数据;页面不绕过 Controller 直接改流程状态。 */ + updateStep(input: UpdateWorkflowStepInput): Promise + + /** + * 把服务端返回的结果写回目标步骤。 + * 目标 Revision 已被重启取代时丢弃该结果,不写入新的执行线。 + */ + applyServerResult(input: ApplyServerResultInput): Promise + + /** + * 从历史步骤开出新的执行线。 + * 旧 Revision 保留为只读历史,不会被改写成失败或完成。 + */ + restartFromStep(input: RestartWorkflowFromStepInput): Promise + + /** 用户主动停止自动推进;历史保留,不等于失败或完成。 */ + interrupt(): Promise +} From 6b21545783682ec3dcbcc6274129c261a8aadb7c Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 15:33:53 +0800 Subject: [PATCH 07/24] feat(pages): add page modules Routing targets have to exist before the application shell can wire them together. Add the home, projects, project detail, quick start, workflow editor, asset library, playtest and not found page modules. Every route in the product design maps to exactly one page module. --- frontend/src/pages/asset-library/index.tsx | 9 +++++++++ frontend/src/pages/home/index.tsx | 13 +++++++++++++ frontend/src/pages/not-found/index.tsx | 9 +++++++++ frontend/src/pages/playtest/index.tsx | 9 +++++++++ frontend/src/pages/project-detail/index.tsx | 9 +++++++++ frontend/src/pages/projects/index.tsx | 9 +++++++++ frontend/src/pages/quick-start/index.tsx | 9 +++++++++ frontend/src/pages/workflow-editor/index.tsx | 9 +++++++++ 8 files changed, 76 insertions(+) create mode 100644 frontend/src/pages/asset-library/index.tsx create mode 100644 frontend/src/pages/home/index.tsx create mode 100644 frontend/src/pages/not-found/index.tsx create mode 100644 frontend/src/pages/playtest/index.tsx create mode 100644 frontend/src/pages/project-detail/index.tsx create mode 100644 frontend/src/pages/projects/index.tsx create mode 100644 frontend/src/pages/quick-start/index.tsx create mode 100644 frontend/src/pages/workflow-editor/index.tsx diff --git a/frontend/src/pages/asset-library/index.tsx b/frontend/src/pages/asset-library/index.tsx new file mode 100644 index 0000000..016328b --- /dev/null +++ b/frontend/src/pages/asset-library/index.tsx @@ -0,0 +1,9 @@ +/** 资产库。 */ +export function AssetLibraryPage() { + return ( +
+

资产库

+

本次只提交模块划分与接口,页面实现进后续 PR。

+
+ ) +} diff --git a/frontend/src/pages/home/index.tsx b/frontend/src/pages/home/index.tsx new file mode 100644 index 0000000..7a153ad --- /dev/null +++ b/frontend/src/pages/home/index.tsx @@ -0,0 +1,13 @@ +/** 首页:入口与项目概览。 */ +export function HomePage() { + return +} + +function PagePlaceholder({ title }: { title: string }) { + return ( +
+

{title}

+

本次只提交模块划分与接口,页面实现进后续 PR。

+
+ ) +} diff --git a/frontend/src/pages/not-found/index.tsx b/frontend/src/pages/not-found/index.tsx new file mode 100644 index 0000000..d35181b --- /dev/null +++ b/frontend/src/pages/not-found/index.tsx @@ -0,0 +1,9 @@ +/** 页面不存在。 */ +export function NotFoundPage() { + return ( +
+

页面不存在

+

本次只提交模块划分与接口,页面实现进后续 PR。

+
+ ) +} diff --git a/frontend/src/pages/playtest/index.tsx b/frontend/src/pages/playtest/index.tsx new file mode 100644 index 0000000..9796e86 --- /dev/null +++ b/frontend/src/pages/playtest/index.tsx @@ -0,0 +1,9 @@ +/** 核验台。 */ +export function PlaytestPage() { + return ( +
+

核验台

+

本次只提交模块划分与接口,页面实现进后续 PR。

+
+ ) +} diff --git a/frontend/src/pages/project-detail/index.tsx b/frontend/src/pages/project-detail/index.tsx new file mode 100644 index 0000000..cc6ff5c --- /dev/null +++ b/frontend/src/pages/project-detail/index.tsx @@ -0,0 +1,9 @@ +/** 项目详情。 */ +export function ProjectDetailPage() { + return ( +
+

项目详情

+

本次只提交模块划分与接口,页面实现进后续 PR。

+
+ ) +} diff --git a/frontend/src/pages/projects/index.tsx b/frontend/src/pages/projects/index.tsx new file mode 100644 index 0000000..8f807f7 --- /dev/null +++ b/frontend/src/pages/projects/index.tsx @@ -0,0 +1,9 @@ +/** 项目列表。 */ +export function ProjectsPage() { + return ( +
+

项目列表

+

本次只提交模块划分与接口,页面实现进后续 PR。

+
+ ) +} diff --git a/frontend/src/pages/quick-start/index.tsx b/frontend/src/pages/quick-start/index.tsx new file mode 100644 index 0000000..1ffe9fe --- /dev/null +++ b/frontend/src/pages/quick-start/index.tsx @@ -0,0 +1,9 @@ +/** 快速开始。 */ +export function QuickStartPage() { + return ( +
+

快速开始

+

本次只提交模块划分与接口,页面实现进后续 PR。

+
+ ) +} diff --git a/frontend/src/pages/workflow-editor/index.tsx b/frontend/src/pages/workflow-editor/index.tsx new file mode 100644 index 0000000..59df779 --- /dev/null +++ b/frontend/src/pages/workflow-editor/index.tsx @@ -0,0 +1,9 @@ +/** 工作流画布。 */ +export function WorkflowEditorPage() { + return ( +
+

工作流画布

+

本次只提交模块划分与接口,页面实现进后续 PR。

+
+ ) +} From e5de44093ada07325cc040ab5013234e61a7bb40 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 15:33:53 +0800 Subject: [PATCH 08/24] feat(app): add application shell and entry point The page modules need a composition root that owns routing and layout. Add the app shell with its layout and route table, the browser entry point, and the base stylesheet. The application runs end to end and every page is reachable. --- frontend/src/app/app.tsx | 36 +++++++++++++++++++++++++++++++ frontend/src/app/index.ts | 1 + frontend/src/app/layout/index.tsx | 32 +++++++++++++++++++++++++++ frontend/src/index.css | 8 +++++++ frontend/src/main.tsx | 11 ++++++++++ 5 files changed, 88 insertions(+) create mode 100644 frontend/src/app/app.tsx create mode 100644 frontend/src/app/index.ts create mode 100644 frontend/src/app/layout/index.tsx create mode 100644 frontend/src/index.css create mode 100644 frontend/src/main.tsx diff --git a/frontend/src/app/app.tsx b/frontend/src/app/app.tsx new file mode 100644 index 0000000..b46ae87 --- /dev/null +++ b/frontend/src/app/app.tsx @@ -0,0 +1,36 @@ +import { BrowserRouter, Route, Routes } from 'react-router' + +import { AssetLibraryPage } from '@/pages/asset-library' +import { HomePage } from '@/pages/home' +import { NotFoundPage } from '@/pages/not-found' +import { PlaytestPage } from '@/pages/playtest' +import { ProjectDetailPage } from '@/pages/project-detail' +import { ProjectsPage } from '@/pages/projects' +import { QuickStartPage } from '@/pages/quick-start' +import { WorkflowEditorPage } from '@/pages/workflow-editor' +import { AppShell } from './layout' + +/** + * 路由表与全局外壳。 + * 页面自己获取所需数据,不再由 app 层构造服务后逐层传入。 + */ +export function App() { + return ( + + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + ) +} diff --git a/frontend/src/app/index.ts b/frontend/src/app/index.ts new file mode 100644 index 0000000..f1c81c7 --- /dev/null +++ b/frontend/src/app/index.ts @@ -0,0 +1 @@ +export { App } from './app' diff --git a/frontend/src/app/layout/index.tsx b/frontend/src/app/layout/index.tsx new file mode 100644 index 0000000..c69669a --- /dev/null +++ b/frontend/src/app/layout/index.tsx @@ -0,0 +1,32 @@ +import type { ReactNode } from 'react' +import { Link } from 'react-router' + +/** + * 模块五:跨页面常驻导航。不属于任何单个页面,故上提到 app 层。 + * 页内头部 PageHeader 在 shared/ui —— pages 不能 import app 层。 + */ + +export interface AppShellProps { + /** 渲染在全局导航下方的当前路由页面。 */ + children: ReactNode +} + +/** 全站外壳,全局导航常驻。 */ +export function AppShell({ children }: AppShellProps) { + return ( +
+ +
{children}
+
+ ) +} diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..f80172a --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,8 @@ +@import 'tailwindcss'; + +/* 全局只放这一点点:其余样式一律走 Tailwind 工具类,避免样式散落各处。 */ +html, +body, +#root { + height: 100%; +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..ff3b910 --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,11 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' + +import { App } from '@/app' +import './index.css' + +createRoot(document.getElementById('root')!).render( + + + , +) From 0a13193592df74e89d4c4ffa16cae87f9c16bfb2 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 15:33:53 +0800 Subject: [PATCH 09/24] ci(frontend): add quality gate and preview deployment Reviewers need the branch checked automatically and a running preview to look at. Add the frontend workflow running lint, typecheck, test and build, and scope Vercel previews to frontend changes. Every pull request reports its own build status and preview URL. --- .github/workflows/frontend-ci.yml | 56 +++++++++++++++++++++++++++++++ frontend/vercel.json | 13 +++++++ 2 files changed, 69 insertions(+) create mode 100644 .github/workflows/frontend-ci.yml create mode 100644 frontend/vercel.json diff --git a/.github/workflows/frontend-ci.yml b/.github/workflows/frontend-ci.yml new file mode 100644 index 0000000..fa48364 --- /dev/null +++ b/.github/workflows/frontend-ci.yml @@ -0,0 +1,56 @@ +name: Frontend CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: frontend-ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + frontend-checks: + name: Frontend checks + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + CI: 'true' + + defaults: + run: + working-directory: frontend + + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Setup Node.js + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Check formatting + run: npm run format:check + + - name: Lint + run: npm run lint + + - name: Typecheck + run: npm run typecheck + + - name: Test + run: npm run test + + - name: Build + run: npm run build diff --git a/frontend/vercel.json b/frontend/vercel.json new file mode 100644 index 0000000..c99f398 --- /dev/null +++ b/frontend/vercel.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "vite", + "installCommand": "npm ci", + "buildCommand": "npm run build", + "outputDirectory": "dist", + "rewrites": [ + { + "source": "/(.*)", + "destination": "/index.html" + } + ] +} From 33b6fa32595bc096b8726a157713fe3b66694a47 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 15:33:53 +0800 Subject: [PATCH 10/24] docs(frontend): document architecture and backend contract The module boundaries and the interfaces expected from the backend must be reviewable without reading the code. Add the architecture document, the frontend README, and the API contract listing every endpoint the frontend calls. Backend and frontend can align on one written list of interfaces. --- frontend-architecture-v3.md | 101 ++++++++++++++++++++++++++++++++ frontend/API_CONTRACT.md | 114 ++++++++++++++++++++++++++++++++++++ frontend/README.md | 30 ++++++++++ 3 files changed, 245 insertions(+) create mode 100644 frontend-architecture-v3.md create mode 100644 frontend/API_CONTRACT.md create mode 100644 frontend/README.md diff --git a/frontend-architecture-v3.md b/frontend-architecture-v3.md new file mode 100644 index 0000000..01b8926 --- /dev/null +++ b/frontend-architecture-v3.md @@ -0,0 +1,101 @@ +# Windup 前端架构 + +本文记录当前前端的模块划分与依赖规则。2026-07-30 按当日评审意见重写:本阶段只提交模块边界与接口,实现进后续 PR。 + +--- + +## 1. 模块划分 + +业务模块都在 `src/entities/` 下: + +| 模块 | 职责 | +|---|---| +| `project` | 项目级全局约束:视角、朝向数、精灵尺寸、画风 | +| `character` | 角色资产。造型、动作、帧是它内部的一棵树 | +| `action-template` | 能跨角色复用的动作配方 | +| `generation` | 一次生成任务这份业务数据 | +| `media` | 已上传媒体的不透明引用 | +| `task` | 后端异步步骤的状态 | +| `workflow-run` | 制作流程的运行记录 | +| `playtest-inspection` | 核验结论,独立于角色与流程 | + +**模块判据:这个东西能不能被单独取到。** + +能单独取,说明它需要自己的一套取数逻辑,才值得一个模块;取不到的,它只是别人身上的一个字段。 + +按这条判据,`Outfit`、`Action`、`Frame` 没有独立模块——它们不能脱离 `Character` 被取到,所以是 `character` 内部的类型。`ActionTemplate` 有独立模块,因为它能被不同角色复用。 + +--- + +## 2. 层次 + +```text +pages -> features -> entities -> shared +``` + +| 层 | 内容 | +|---|---| +| `pages` | 八个路由页面 | +| `features` | 用户操作:角色设置、生成、审核、导出;以及流程推进 `workflow-controller` | +| `entities` | 上表业务模块 | +| `shared` | 无业务语义的形状,目前只有分页 | + +`app` 只做启动和路由,不构造服务、不向下注入。 + +### 依赖规则 + +1. 只能向下依赖,不允许反向。 +2. 同层模块之间不互相导入。要共用就往下沉。 +3. 跨模块只从模块目录的 `index.ts` 进入;`entities` 统一从 `@/entities` 使用。 +4. `entities` 内部模块之间可以互相导入,对外仍是一个门。 + +--- + +## 3. 接口命名 + +每个模块暴露一组后端接口,统一叫 `XxxApis`: + +```text +ProjectApis CharacterApis ActionTemplateApis GenerationApis +TaskApis WorkflowRunApis PlaytestInspectionApis +``` + +**不使用 `Repository` / `Port` / `Adapter` 这些叫法**,也不做接口与实现的分离——实现跟着接口放在同一个模块里。 + +--- + +## 4. 流程推进 + +`features/workflow-controller` 是快速开始与手动工作流共用的推进边界,不含界面。 + +Controller 围绕同一份 WorkflowRun 提供推进、更新、重启和中断。这些操作依赖同一份步骤数据,不拆成互不共享状态的独立模块。 + +步骤顺序固定八步: + +```text +角色资料 → 角色图 → 候选选择 → 动作资料 → 首帧 → 完整动画 → 审核 → 导出 +``` + +**步骤怎么走由前端决定。** `WorkflowRunApis` 只负责存取运行记录,不负责推进。 + +从历史步骤重开会追加一个新 Revision,旧 Revision 保留为只读历史,不会被改写成失败或完成。 + +快速开始与手动模式共用同一份推进逻辑,区别只是前者连续调用、后者一次一步。隐藏步骤不等于跳过步骤——门禁写在流程模型里,不在界面里。 + +--- + +## 5. 本次不包含 + +- 任何实现代码(真实请求、假数据、组件内部逻辑) +- 测试文件 +- 图片上传模块(体量太小,本次不单独体现) +- 穿戴道具相关(产品侧未设计) +- 第三方登录 + +页面当前是占位外壳,只声明路由与模块边界。 + +--- + +## 6. 未与后端对齐的部分 + +明细见 `frontend/API_CONTRACT.md`。 diff --git a/frontend/API_CONTRACT.md b/frontend/API_CONTRACT.md new file mode 100644 index 0000000..f5a3206 --- /dev/null +++ b/frontend/API_CONTRACT.md @@ -0,0 +1,114 @@ +# 前后端接口对齐清单 + +前端各模块的 `XxxApis` 与后端 PR #64(head `10dd958`)逐条比对结果。 + +后端现有四个相关模块:`project`、`character`、`generation`、`media`。`asset` 与 `wearable` 已按 07-30 评审要求删除。 + +--- + +## 一、前端预期有、后端目前没有 + +**这几条需要后端明确做或不做;不做的前端删掉。** + +| 前端接口 | 后端情况 | +|---|---| +| `WorkflowRunApis`(`get` / `create` / `save`) | 没有 workflow 模块 | +| `ActionTemplateApis.listAvailable` | 没有 action template 模块 | +| `PlaytestInspectionApis`(`getLatest` / `record`) | 没有对应模块 | +| 独立的 `task` 模块 | 后端 Task 不独立,是 `generation` 内的 `GenerationTask` | + +前端已按服务端现状去掉 `TaskApis.cancel`——`GenerationService` 没有取消能力,不声明前端用不到的接口。 + +--- + +## 二、形状不一致 + +### 生成步骤:后端一步,前端两步 + +| 后端 `GenerationType` | 前端 `GenerationType` | +|---|---| +| `character_image` | `character_template` | +| `character_action` | `first_frame` → `complete_animation` | + +前端设计是先出动作首帧、用户确认后再生成完整动画。后端 `generate_character_action` 是一次到底。 + +**这不是命名差异,是交互差异。需要确认首帧确认这一步保不保。** + +### 其他 + +| 项 | 后端 | 前端 | +|---|---|---| +| 角色列表 | `list_characters` 分页,返回 `(list, total)` | `listByProject` 无分页 | +| 更新角色 | `update_character(character_id, **fields)` 部分更新 | `update(character)` 整棵树替换 | +| 查任务 | `get_task(project_id, task_id)` 需要 `project_id` | `get(taskId)` 只传 taskId | +| 动作类型 | `walk` `idle` `attack` `custom` | 多一个 `jump` | +| 删除项目 | 返回 `bool`(是否找到) | 返回 `void` | + +ID 类型后端为 `int`、前端为 `string`,由前端转换层处理,不需要后端改动。 + +--- + +## 三、后端有、前端没接 + +| 后端 | 说明 | +|---|---| +| `project_name_exists(user_id, project_name)` | 建项目时的重名校验,前端未接 | +| `delete_character` | 前端 `CharacterApis` 没有删除 | +| `Character.description` | 后端存在实体上;前端只在创建入参里,创建完查不到 | +| `Character.reference_image_url` | 后端存在实体上;前端 `Character` 类型没有这个字段 | +| `MediaService.upload` | 前端本次未提交上传模块 | + +--- + +## 四、审核数据无处保存 + +后端 `character_data` 的嵌套结构(见 `character/model.py`): + +```text +outfits[] → id / name / preview_url / actions[] +actions[] → id / type / name / loop / fps / frame_count / frames[] +frames[] → index / image_url / duration_ms +``` + +前端以下字段在后端结构里没有落点: + +- `Frame.qc`(系统质检结论) +- `Frame.rejected`(人工打回) +- `Action.kind`(preset / custom 来源) +- `Action.status`(planned / generating / candidate / confirmed / failed) +- `Action.keyFrameIndex` +- `Frame.rootMotion` +- `Outfit.candidateCharacterTemplates`(母版候选列表) +- `Outfit.baseFrames` + +**其中 `qc` 与 `rejected` 是审核台的全部依据。后端不存这两个字段,审核结果就落不了库。** + +`candidateCharacterTemplates` 同理:前端设计是生成多张候选让用户选,后端 `CharacterImageInput.num_images` 默认 1,`character_data` 里也没有候选列表。 + +--- + +## 五、概念不一致 + +后端 `character/model.py` 字段说明: + +> `reference_image_url`: 角色参考图,即旧概念中的 Character Template + +前端把这两者当成不同的东西: + +- 用户上传的参考图 —— 创建角色时的输入 +- AI 生成后用户选定的角色图(母版)—— `Outfit.characterTemplateUrl` + +**后端合成了一个字段。** 07-30 评审也提到「模板」这个叫法容易与 action template 混淆,暂改称「角色图」。三方对这里是几个概念的理解需要统一。 + +--- + +## 待确认 + +- [ ] 第一节四条:后端做还是不做 +- [ ] 动作生成一步还是两步 +- [ ] `Frame.qc` / `Frame.rejected` 落不落库 +- [ ] 母版候选几张 +- [ ] 参考图与角色图是一个字段还是两个 +- [ ] `Character.description` 前端要不要跟着存 +- [ ] `jump` 动作类型后端加不加 +- [ ] 上传模块何时提交 diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..6e01fa3 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,30 @@ +# Windup 前端 + +React + Vite + TypeScript。 + +## 开发 + +```bash +npm ci +npm run dev +``` + +## 检查 + +```bash +npm run format:check # 格式 +npm run lint # 静态检查 +npm run typecheck # 类型 +npm run test # 测试(本阶段无测试文件) +npm run build # 构建 +``` + +CI 按上面顺序全跑一遍。 + +## 结构 + +模块划分、依赖规则与命名约定见仓库根目录 `frontend-architecture-v3.md`。 + +**本阶段只提交模块边界与接口,不含实现。** 页面是占位外壳,各模块只有类型与 `XxxApis` 接口。实现按模块拆成后续 PR。 + +与后端尚未对齐的接口见 `API_CONTRACT.md`。 From 01b0e0a1ffca078486a13850f4b3ae9a44fad4a2 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 17:10:30 +0800 Subject: [PATCH 11/24] fix(workflow): persist the in-flight task id on the step A run reopened after a refresh could not tell that a generation was still running, so the page had to either resubmit it or stall. Record the in-flight task id on WorkflowStep so it is saved with the run, and drop WorkflowTaskLink, which no structure ever held. Reopening a run recovers the pending task from its step instead of starting a second generation. --- frontend/src/entities/index.ts | 1 - frontend/src/entities/task/index.ts | 3 ++- frontend/src/entities/workflow-run/index.ts | 16 ++++++---------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/frontend/src/entities/index.ts b/frontend/src/entities/index.ts index 804de8b..113ddaa 100644 --- a/frontend/src/entities/index.ts +++ b/frontend/src/entities/index.ts @@ -74,7 +74,6 @@ export type { WorkflowRunApis, WorkflowRunPurpose, WorkflowRunStatus, - WorkflowTaskLink, } from './workflow-run' /* Playtest 核验记录 —— 独立于 Character 与 WorkflowRun */ diff --git a/frontend/src/entities/task/index.ts b/frontend/src/entities/task/index.ts index eaccba1..bd64883 100644 --- a/frontend/src/entities/task/index.ts +++ b/frontend/src/entities/task/index.ts @@ -2,7 +2,8 @@ * 异步任务实体,与 WorkflowRun 的页面节点是两回事。 * * 任务粒度 = 前端可见的一个异步步骤,而不是内部某次模型调用。 - * 任务生命周期不等于工作流节点状态,WorkflowRun 只通过 WorkflowTaskLink 记录两者关系。 + * 任务生命周期不等于工作流节点状态,两者只由 WorkflowStep.taskId 单向关联: + * 步骤记得自己发起过哪个任务,任务不认识步骤。 */ /** 后端异步任务状态;pending 表示已提交但尚未执行。 */ diff --git a/frontend/src/entities/workflow-run/index.ts b/frontend/src/entities/workflow-run/index.ts index 04ec0e2..c4a715d 100644 --- a/frontend/src/entities/workflow-run/index.ts +++ b/frontend/src/entities/workflow-run/index.ts @@ -62,6 +62,12 @@ export interface WorkflowStep { input: unknown /** 步骤完成后的结果或引用;尚无结果时为 null。 */ output: unknown + /** + * 本步骤已提交、结果尚未写回 output 的生成任务 ID;没有在途任务时为 null。 + * 它随 WorkflowRun 一起持久化,页面重开后据此查回在途任务的状态, + * 因而不会重复发起同一次生成。任务本身不认识步骤,反向关联不存在。 + */ + taskId: Task['id'] | null /** 该步骤沿用或依赖的步骤 ID,用于版本来源追踪,不代表后端执行依赖。 */ referenceStepIds: string[] } @@ -136,16 +142,6 @@ export type CreateWorkflowRunInput = CreateWorkflowRunInputBase & } ) -/** 前端编排关联;后端 Task 不需要认识 WorkflowRun、Revision 或流程步骤。 */ -export interface WorkflowTaskLink { - taskId: Task['id'] - runId: WorkflowRun['id'] - /** 发起任务时所在的版本 ID,避免结果写入后来创建的新版本。 */ - revisionId: WorkflowRevision['id'] - /** 发起任务的步骤 ID,用于把结果映射回正确页面阶段。 */ - stepId: WorkflowStep['id'] -} - /** WorkflowRun 对应的一组后端接口;只负责存取,不负责推进。 */ export interface WorkflowRunApis { get(runId: WorkflowRun['id']): Promise From 1f0ddcae55fd46b5b7b6635b8ded4fd768809809 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 17:27:26 +0800 Subject: [PATCH 12/24] build(frontend): typecheck the vitest config The vitest config sat outside every tsconfig include, so a type error in it would only surface when the runner started. Add it to the node project alongside the vite config. Both build-side configs are now covered by tsc -b. --- frontend/tsconfig.node.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json index 8455dcb..d3daab6 100644 --- a/frontend/tsconfig.node.json +++ b/frontend/tsconfig.node.json @@ -19,5 +19,5 @@ "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true }, - "include": ["vite.config.ts"] + "include": ["vite.config.ts", "vitest.config.ts"] } From 2be18e3ad535cef96acf91a459a7af57ded4cc1f Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 17:27:26 +0800 Subject: [PATCH 13/24] refactor(entities): keep only confirmed content in the asset tree A character is written when the user exports to the asset library, so it cannot hold work-in-progress state; the backend also returns no QC verdict and the product has no per-frame reject. Drop the action generation status, the frame qc and rejected fields, and the standalone playtest inspection entity; review now only reads frames. The asset tree describes confirmed assets only, and review results no longer need a second home. --- frontend/src/entities/character/index.ts | 49 ++++++++++--------- frontend/src/entities/index.ts | 10 ---- .../src/entities/playtest-inspection/index.ts | 29 ----------- frontend/src/features/review/index.ts | 9 +++- 4 files changed, 32 insertions(+), 65 deletions(-) delete mode 100644 frontend/src/entities/playtest-inspection/index.ts diff --git a/frontend/src/entities/character/index.ts b/frontend/src/entities/character/index.ts index db8315b..eecd8a1 100644 --- a/frontend/src/entities/character/index.ts +++ b/frontend/src/entities/character/index.ts @@ -10,22 +10,6 @@ export type ActionKind = 'preset' | 'custom' */ export type ActionType = 'walk' | 'idle' | 'attack' | 'jump' | 'custom' -/** 动作在生成流水线上的位置。 */ -export type ActionStatus = - /** 已加入工作流但还没开始生成 */ - | 'planned' - /** 后端任务进行中 */ - | 'generating' - /** 生成完成,是候选,还没被用户确认 */ - | 'candidate' - /** 用户确认后成为正式资产 */ - | 'confirmed' - /** 生成失败 */ - | 'failed' - -/** 系统质检结论。系统通过不等于人工通过,分开记。 */ -export type FrameQcResult = 'pending' | 'passed' | 'failed' - /** 单帧相对动作首帧的根位移,单位为像素。 */ export interface FrameRootMotion { dx: number @@ -33,7 +17,12 @@ export interface FrameRootMotion { dy: number } -/** 动作序列中的一张有序画面;帧序号由其在 Action.frames 中的位置决定。 */ +/** + * 动作序列中的一张有序画面;帧序号由其在 Action.frames 中的位置决定。 + * + * 不带任何审核字段:服务端只交付生成好的帧,不返回质检结论;用户侧的审核也只是查看, + * 没有打回。前端若要做自动质检,那是读取帧之后在本地算出来的临时结论,不属于资产数据。 + */ export interface Frame { imageUrl: string /** @@ -43,10 +32,6 @@ export interface Frame { durationMs: number | null /** null 表示不提供根位移,Playtest 与 Export 不应据此施加任何位移。 */ rootMotion: FrameRootMotion | null - /** 后端自动质检结论,与人工 rejected 状态相互独立。 */ - qc: FrameQcResult - /** 人工退回。不设「通过此帧」,故是布尔量而非三态。 */ - rejected: boolean } /** 一张母版候选;attemptId 用于区分候选所属的生成尝试。 */ @@ -69,6 +54,10 @@ export interface BaseFrame { /** 某个角色造型下的一段动画动作。 */ export interface Action { + /** + * 仅在所属 Outfit 内唯一。动作没有自己的表,整棵树存在 character 记录里, + * 因此不存在全局唯一的动作 ID:任何按 ID 定位动作的地方都必须同时带上造型。 + */ id: string outfitId: Outfit['id'] name: string @@ -76,7 +65,6 @@ export interface Action { kind: ActionKind /** 动作业务语义;与 kind 的 preset/custom 来源维度相互独立。 */ type: ActionType - status: ActionStatus /** * 每秒播放帧数。仅当某帧 durationMs 为 null 时用于等时长回退; * Playtest 与 Export 不得用前端全局常量替代,也不得覆盖帧自己的 durationMs。 @@ -87,12 +75,20 @@ export interface Action { * 非 null 值必须指向当前 frames 数组内的成员。 */ keyFrameIndex: number | null - /** 按播放顺序排列的帧;数组下标就是零基帧序号。 */ + /** + * 按播放顺序排列的帧;数组下标就是零基帧序号。 + * 当前只表达单朝向。Project.directionalMovement 的四向/八向要如何落到这里 + * (本层再分组,还是一个朝向一条 Action)尚未有产品定义,不要凭猜先定结构。 + */ frames: Frame[] } /** 同一角色的一套独立造型;MVP UI 只展示第一套,但数据结构不折叠该层。 */ export interface Outfit { + /** + * 仅在所属 Character 内唯一。造型没有自己的表,与动作一起存在 character 记录里, + * 因此不存在全局唯一的造型 ID:任何按 ID 定位造型的地方都必须同时带上角色。 + */ id: string characterId: string name: string @@ -106,7 +102,12 @@ export interface Outfit { actions: Action[] } -/** 项目下的角色资产;造型拥有各自的母版和动作帧。 */ +/** + * 项目下的角色资产;造型拥有各自的母版和动作帧。 + * + * 这棵树只承载已导出到资产库的内容,因此其中的动作一律是已确认的,不带生成过程状态。 + * 工作流运行期间的造型、动作和帧活在 WorkflowRun 的步骤里,直到用户确认导出才整体写入。 + */ export interface Character { id: string projectId: string diff --git a/frontend/src/entities/index.ts b/frontend/src/entities/index.ts index 113ddaa..6a4ce9c 100644 --- a/frontend/src/entities/index.ts +++ b/frontend/src/entities/index.ts @@ -18,7 +18,6 @@ export type { export type { Action, ActionKind, - ActionStatus, ActionType, BaseFrame, Character, @@ -27,7 +26,6 @@ export type { ConfirmCharacterTemplateInput, CreateCharacterInput, Frame, - FrameQcResult, FrameRootMotion, Outfit, } from './character' @@ -75,11 +73,3 @@ export type { WorkflowRunPurpose, WorkflowRunStatus, } from './workflow-run' - -/* Playtest 核验记录 —— 独立于 Character 与 WorkflowRun */ -export type { - PlaytestInspection, - PlaytestInspectionApis, - PlaytestInspectionStatus, - RecordPlaytestInspectionInput, -} from './playtest-inspection' diff --git a/frontend/src/entities/playtest-inspection/index.ts b/frontend/src/entities/playtest-inspection/index.ts deleted file mode 100644 index e984111..0000000 --- a/frontend/src/entities/playtest-inspection/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** 可持久化的 Playtest 核验结论;没有记录即表示尚未核验。 */ -export type PlaytestInspectionStatus = 'passed' | 'issues_found' - -/** 一次独立核验记录,不属于 Character 或 WorkflowRevision 的内部字段。 */ -export interface PlaytestInspection { - id: string - characterId: string - outfitId: string - /** 仅从 Workflow 进入时保留返回定位;独立入口为 null。 */ - source: { runId: string; revisionId: string } | null - status: PlaytestInspectionStatus - createdAt: string - updatedAt: string -} - -/** 创建或更新指定核验目标结论所需的业务输入。 */ -export interface RecordPlaytestInspectionInput { - characterId: string - outfitId: string - /** 可选的 Workflow 来源,不是播放和核验的前置条件。 */ - source?: { runId: string; revisionId: string } | null - status: PlaytestInspectionStatus -} - -/** PlaytestInspection 对应的一组后端接口。 */ -export interface PlaytestInspectionApis { - getLatest(target: { characterId: string; outfitId: string }): Promise - record(input: RecordPlaytestInspectionInput): Promise -} diff --git a/frontend/src/features/review/index.ts b/frontend/src/features/review/index.ts index 8a897e8..3a48b66 100644 --- a/frontend/src/features/review/index.ts +++ b/frontend/src/features/review/index.ts @@ -1,7 +1,12 @@ -/** 逐帧审核:打回有问题的帧。不设「通过此帧」,默认通过。 */ +/** + * 逐帧查看已生成的动作,供用户在导出前过一遍。 + * + * 只看不改:服务端不返回质检结论,产品上也不设「打回此帧」, + * 所以这里没有任何写操作,帧数据不会因为查看而改变。 + */ export interface ReviewProps { + /** 动作 ID 只在造型内唯一,调用方须自行持有所属造型,不能拿它跨角色定位。 */ actionId: string frameIndex: number onSelectFrame(index: number): void - onRejectFrame(index: number): void } From f9776f9b385d093b2270a086df05b821189c48da Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 17:27:26 +0800 Subject: [PATCH 14/24] docs(entities): mark what the product has not defined yet Three structures carried assumptions a reader could not distinguish from settled decisions. State that multi-direction frame storage is undefined, that the MVP runs a single revision, and that an action id is unique only inside its outfit. Open questions are visible in the contracts instead of being rediscovered later. --- frontend/src/entities/project/index.ts | 6 +++++- frontend/src/entities/workflow-run/index.ts | 8 +++++++- frontend/src/features/generation/index.ts | 6 +++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/frontend/src/entities/project/index.ts b/frontend/src/entities/project/index.ts index 778acf8..4c71164 100644 --- a/frontend/src/entities/project/index.ts +++ b/frontend/src/entities/project/index.ts @@ -58,7 +58,11 @@ export const CHARACTER_PERSPECTIVE: Record = { isometric: '2.5D', } -/** 移动方向,决定一个动作要生成几套朝向的帧。 */ +/** + * 移动方向,决定一个动作要生成几套朝向的帧。 + * 多朝向在 Action 上如何存放尚未定义,当前 Action.frames 只表达单朝向; + * 选了四向/八向的项目,生成侧还接不上,见 Action.frames 的说明。 + */ export const DIRECTIONAL_MOVEMENT: Record = { single: '单向', 'four-way': '四向', diff --git a/frontend/src/entities/workflow-run/index.ts b/frontend/src/entities/workflow-run/index.ts index c4a715d..6998d27 100644 --- a/frontend/src/entities/workflow-run/index.ts +++ b/frontend/src/entities/workflow-run/index.ts @@ -72,7 +72,13 @@ export interface WorkflowStep { referenceStepIds: string[] } -/** 一次页面执行版本;当前版本会推进,从旧步骤重开则追加新版本。 */ +/** + * 一次页面执行版本;当前版本会推进,从旧步骤重开则追加新版本。 + * + * MVP 只走单条执行线:revisions 恒为一个成员,basedOnRevisionId 与 restartStepId 恒为 null。 + * 「从历史步骤重开并保留旧版本」尚未进入产品定义,结构先留出位置但不实现, + * 避免真要做时改动波及 WorkflowRun 的持久化形状。 + */ export interface WorkflowRevision { id: string /** 首次创建的版本没有来源,因此为 null。 */ diff --git a/frontend/src/features/generation/index.ts b/frontend/src/features/generation/index.ts index 47f5997..2015752 100644 --- a/frontend/src/features/generation/index.ts +++ b/frontend/src/features/generation/index.ts @@ -1,6 +1,10 @@ /** 触发并展示一次生成;不感知后端调用的是哪个模型。 */ export interface GenerationProps { runId: string - /** 生成母版等非动作任务可以省略。 */ + /** + * 目标动作,生成母版等非动作任务可以省略。 + * 动作 ID 只在造型内唯一,所以造型由 runId 对应的 WorkflowRun.outfitId 决定, + * 不能脱离 run 单独使用这个字段。 + */ actionId?: string } From a86d23b99b8e6db82db5cd1da7ad673aa92c4f57 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 18:24:18 +0800 Subject: [PATCH 15/24] docs(frontend): remove stale skeleton references The reduced module skeleton no longer includes playtest inspection or shared UI. Remove obsolete API and entity references and align the layout comment with app ownership. Documentation now matches the contracts and directories present in this branch. --- frontend-architecture-v3.md | 3 +-- frontend/API_CONTRACT.md | 1 - frontend/src/app/layout/index.tsx | 5 +---- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/frontend-architecture-v3.md b/frontend-architecture-v3.md index 01b8926..1488dfb 100644 --- a/frontend-architecture-v3.md +++ b/frontend-architecture-v3.md @@ -17,7 +17,6 @@ | `media` | 已上传媒体的不透明引用 | | `task` | 后端异步步骤的状态 | | `workflow-run` | 制作流程的运行记录 | -| `playtest-inspection` | 核验结论,独立于角色与流程 | **模块判据:这个东西能不能被单独取到。** @@ -57,7 +56,7 @@ pages -> features -> entities -> shared ```text ProjectApis CharacterApis ActionTemplateApis GenerationApis -TaskApis WorkflowRunApis PlaytestInspectionApis +TaskApis WorkflowRunApis ``` **不使用 `Repository` / `Port` / `Adapter` 这些叫法**,也不做接口与实现的分离——实现跟着接口放在同一个模块里。 diff --git a/frontend/API_CONTRACT.md b/frontend/API_CONTRACT.md index f5a3206..0774b9e 100644 --- a/frontend/API_CONTRACT.md +++ b/frontend/API_CONTRACT.md @@ -14,7 +14,6 @@ |---|---| | `WorkflowRunApis`(`get` / `create` / `save`) | 没有 workflow 模块 | | `ActionTemplateApis.listAvailable` | 没有 action template 模块 | -| `PlaytestInspectionApis`(`getLatest` / `record`) | 没有对应模块 | | 独立的 `task` 模块 | 后端 Task 不独立,是 `generation` 内的 `GenerationTask` | 前端已按服务端现状去掉 `TaskApis.cancel`——`GenerationService` 没有取消能力,不声明前端用不到的接口。 diff --git a/frontend/src/app/layout/index.tsx b/frontend/src/app/layout/index.tsx index c69669a..aa21b82 100644 --- a/frontend/src/app/layout/index.tsx +++ b/frontend/src/app/layout/index.tsx @@ -1,10 +1,7 @@ import type { ReactNode } from 'react' import { Link } from 'react-router' -/** - * 模块五:跨页面常驻导航。不属于任何单个页面,故上提到 app 层。 - * 页内头部 PageHeader 在 shared/ui —— pages 不能 import app 层。 - */ +/** 跨页面常驻导航属于应用外壳,由 app 层统一承载。 */ export interface AppShellProps { /** 渲染在全局导航下方的当前路由页面。 */ From 77fca50ae47a010ffb38e39342f96768ece4964e Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 19:48:32 +0800 Subject: [PATCH 16/24] refactor(character): remove unsupported character name The backend character contract has no standalone name field. Remove name from Character and CreateCharacterInput. The frontend now matches the confirmed character payload boundary. --- frontend/src/entities/character/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/entities/character/index.ts b/frontend/src/entities/character/index.ts index eecd8a1..616b5db 100644 --- a/frontend/src/entities/character/index.ts +++ b/frontend/src/entities/character/index.ts @@ -111,7 +111,6 @@ export interface Outfit { export interface Character { id: string projectId: string - name: string /** 角色的全部独立造型;MVP 页面至少保留这一层,即使当前只有一个成员。 */ outfits: Outfit[] createdAt: string @@ -121,7 +120,6 @@ export interface Character { /** 创建角色并发起母版生成所需的入参。 */ export interface CreateCharacterInput { projectId: string - name: string /** 交给模型生成母版。 */ description: string referenceImageUrl?: string | null From b03250c23184737cf099d133fe815d7a285897dc Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 19:48:59 +0800 Subject: [PATCH 17/24] refactor(workflow): keep workflow run frontend-owned The fixed workflow is not a backend resource. Remove WorkflowRunApis and update the public and controller contracts. Backend responsibilities now stop at generation and confirmed asset storage. --- frontend/src/entities/index.ts | 3 +-- frontend/src/entities/workflow-run/index.ts | 16 +++++----------- .../src/features/workflow-controller/index.ts | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/frontend/src/entities/index.ts b/frontend/src/entities/index.ts index 6a4ce9c..ac64b4f 100644 --- a/frontend/src/entities/index.ts +++ b/frontend/src/entities/index.ts @@ -56,7 +56,7 @@ export type { MediaReference } from './media' /* 后端异步任务 —— 与工作流节点是两回事 */ export type { Task, TaskApis, TaskEvent, TaskStatus, TaskType } from './task' -/* 工作流 —— 节点由前端推进,运行记录由后端持久化 */ +/* 工作流 —— 节点与运行状态都由前端管理 */ export { WORKFLOW_STEP_ORDER } from './workflow-run' export type { CreateWorkflowRunInput, @@ -69,7 +69,6 @@ export type { WorkflowRevision, WorkflowRevisionStatus, WorkflowRun, - WorkflowRunApis, WorkflowRunPurpose, WorkflowRunStatus, } from './workflow-run' diff --git a/frontend/src/entities/workflow-run/index.ts b/frontend/src/entities/workflow-run/index.ts index 6998d27..a2a3aef 100644 --- a/frontend/src/entities/workflow-run/index.ts +++ b/frontend/src/entities/workflow-run/index.ts @@ -64,8 +64,9 @@ export interface WorkflowStep { output: unknown /** * 本步骤已提交、结果尚未写回 output 的生成任务 ID;没有在途任务时为 null。 - * 它随 WorkflowRun 一起持久化,页面重开后据此查回在途任务的状态, - * 因而不会重复发起同一次生成。任务本身不认识步骤,反向关联不存在。 + * 它由前端随 WorkflowRun 一起维护,据此查回在途任务的状态,因而不会在同一次 + * 前端运行中重复发起生成。是否写入浏览器存储属于前端实现,不形成后端契约。 + * 任务本身不认识步骤,反向关联不存在。 */ taskId: Task['id'] | null /** 该步骤沿用或依赖的步骤 ID,用于版本来源追踪,不代表后端执行依赖。 */ @@ -98,7 +99,8 @@ export interface WorkflowRevision { /** * 一次由前端推进的页面流程。 - * 步骤怎么走由前端决定;WorkflowRun 本身由后端持久化,前端通过 WorkflowRunApis 读写。 + * 步骤推进和运行状态都由前端管理;后端不读取、不推进、也不持久化 WorkflowRun。 + * 后端只处理生成任务,并在用户最终确认时持久化角色与动作资产。 */ export interface WorkflowRun { id: string @@ -147,11 +149,3 @@ export type CreateWorkflowRunInput = CreateWorkflowRunInputBase & baseFrameUrls: readonly string[] } ) - -/** WorkflowRun 对应的一组后端接口;只负责存取,不负责推进。 */ -export interface WorkflowRunApis { - get(runId: WorkflowRun['id']): Promise - create(input: CreateWorkflowRunInput): Promise - /** 保存已由 WorkflowController 完成合法状态转换的流程快照。 */ - save(run: WorkflowRun): Promise -} diff --git a/frontend/src/features/workflow-controller/index.ts b/frontend/src/features/workflow-controller/index.ts index 3ccff25..f8ce879 100644 --- a/frontend/src/features/workflow-controller/index.ts +++ b/frontend/src/features/workflow-controller/index.ts @@ -32,7 +32,7 @@ export interface ApplyServerResultInput { * Controller 围绕同一份 WorkflowRun 提供推进、更新、重启和中断。这些操作依赖同一份 * 步骤数据,不拆成互不共享状态的独立模块。 * - * 步骤怎么走由前端决定,服务端只负责持久化 WorkflowRun 和提供生成能力。 + * 步骤和运行状态由前端管理;服务端只提供生成能力,并持久化最终确认的资产。 */ export interface WorkflowController { /** 初始化一条创建角色或增加动作的流程。 */ From 378fee028fbf9f7bdd314120db2a2450bc529feb Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 19:49:21 +0800 Subject: [PATCH 18/24] docs(frontend): record confirmed backend boundaries The API notes still described stale workflow and generation assumptions. Document frontend-owned workflow state, generation mapping, and assigned follow-ups. The architecture now reflects the latest backend reference and team decisions. --- frontend-architecture-v3.md | 8 ++-- frontend/API_CONTRACT.md | 73 ++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/frontend-architecture-v3.md b/frontend-architecture-v3.md index 1488dfb..5c06229 100644 --- a/frontend-architecture-v3.md +++ b/frontend-architecture-v3.md @@ -52,15 +52,17 @@ pages -> features -> entities -> shared ## 3. 接口命名 -每个模块暴露一组后端接口,统一叫 `XxxApis`: +需要访问后端资源的模块暴露一组接口,统一叫 `XxxApis`: ```text ProjectApis CharacterApis ActionTemplateApis GenerationApis -TaskApis WorkflowRunApis +TaskApis ``` **不使用 `Repository` / `Port` / `Adapter` 这些叫法**,也不做接口与实现的分离——实现跟着接口放在同一个模块里。 +`WorkflowRun` 是前端运行态,不声明后端接口。后端不读取、不推进、也不持久化它。 + --- ## 4. 流程推进 @@ -75,7 +77,7 @@ Controller 围绕同一份 WorkflowRun 提供推进、更新、重启和中断 角色资料 → 角色图 → 候选选择 → 动作资料 → 首帧 → 完整动画 → 审核 → 导出 ``` -**步骤怎么走由前端决定。** `WorkflowRunApis` 只负责存取运行记录,不负责推进。 +**步骤怎么走、运行状态如何保存都由前端决定。** 后端不参与 WorkflowRun,只接收各节点发起的生成请求,并在最终确认时持久化角色与动作资产。 从历史步骤重开会追加一个新 Revision,旧 Revision 保留为只读历史,不会被改写成失败或完成。 diff --git a/frontend/API_CONTRACT.md b/frontend/API_CONTRACT.md index 0774b9e..e0cd7d2 100644 --- a/frontend/API_CONTRACT.md +++ b/frontend/API_CONTRACT.md @@ -1,57 +1,63 @@ # 前后端接口对齐清单 -前端各模块的 `XxxApis` 与后端 PR #64(head `10dd958`)逐条比对结果。 +前端各模块的 `XxxApis` 与后端 2026-07-30 接口文档逐条比对结果。 后端现有四个相关模块:`project`、`character`、`generation`、`media`。`asset` 与 `wearable` 已按 07-30 评审要求删除。 --- -## 一、前端预期有、后端目前没有 +## 一、已经确认的边界 -**这几条需要后端明确做或不做;不做的前端删掉。** +- `WorkflowRun` 是前端固定工作流的运行态。后端不读取、不推进、也不持久化,前端不声明 `WorkflowRunApis`。 +- `Character` 不使用独立 `name` 字段;前端已删除。 +- 前端保留 `jump` 动作类型,由后端补充对应枚举。 +- 前端工作流节点不与后端 `GenerationType` 一一对应,按下表调用: -| 前端接口 | 后端情况 | -|---|---| -| `WorkflowRunApis`(`get` / `create` / `save`) | 没有 workflow 模块 | -| `ActionTemplateApis.listAvailable` | 没有 action template 模块 | -| 独立的 `task` 模块 | 后端 Task 不独立,是 `generation` 内的 `GenerationTask` | +| 前端工作流节点 | 后端接口 | 后端任务类型 | +|---|---|---| +| `character_template` | `POST /generation/image` | `character_image` | +| `first_frame` | `POST /generation/image`,以上一步角色图作为参考图 | `character_image` | +| `complete_animation` | `POST /generation/action`,以已确认动作首帧作为参考图 | `character_action` | -前端已按服务端现状去掉 `TaskApis.cancel`——`GenerationService` 没有取消能力,不声明前端用不到的接口。 +图片生成和动作生成只返回任务及结果,不自动修改 WorkflowRun 或角色资产。用户最终确认后,前端再通过角色更新接口保存角色图和完整动作数据。 --- -## 二、形状不一致 +## 二、前端预期有、后端目前没有 -### 生成步骤:后端一步,前端两步 +**这些接口仍需要确定由后端提供,还是改为前端本地能力。** -| 后端 `GenerationType` | 前端 `GenerationType` | +| 前端接口 | 后端情况 | |---|---| -| `character_image` | `character_template` | -| `character_action` | `first_frame` → `complete_animation` | +| `ActionTemplateApis.listAvailable` | 没有 action template 模块 | +| `ProjectApis.update` | 没有 `PATCH /projects/{project_id}` | -前端设计是先出动作首帧、用户确认后再生成完整动画。后端 `generate_character_action` 是一次到底。 +前端已按服务端现状去掉 `TaskApis.cancel`——后端没有取消能力,不声明前端用不到的接口。 -**这不是命名差异,是交互差异。需要确认首帧确认这一步保不保。** +--- + +## 三、形状不一致 -### 其他 +这些差异可以在前端接口层转换,不要求领域类型与后端 DTO 使用相同命名。 | 项 | 后端 | 前端 | |---|---|---| | 角色列表 | `list_characters` 分页,返回 `(list, total)` | `listByProject` 无分页 | | 更新角色 | `update_character(character_id, **fields)` 部分更新 | `update(character)` 整棵树替换 | | 查任务 | `get_task(project_id, task_id)` 需要 `project_id` | `get(taskId)` 只传 taskId | -| 动作类型 | `walk` `idle` `attack` `custom` | 多一个 `jump` | -| 删除项目 | 返回 `bool`(是否找到) | 返回 `void` | +| 等待任务完成 | 提供 `GET /generation/tasks/{task_id}` 轮询 | `TaskApis.subscribe`,实现时可封装轮询 | +| 图片生成数量 | 入参有 `num_images`,结果只有一个 `image_url` | 角色图候选结果是 `images[]` | +| 动作类型 | `walk` `idle` `attack` `custom`;待增加 `jump` | `walk` `idle` `attack` `jump` `custom` | +| 角色视角 | `character_perspective` 为 `1~3`,文档中 2、3 都写成“正面” | `side` `top-down` `isometric` | ID 类型后端为 `int`、前端为 `string`,由前端转换层处理,不需要后端改动。 --- -## 三、后端有、前端没接 +## 四、后端有、前端没接 | 后端 | 说明 | |---|---| -| `project_name_exists(user_id, project_name)` | 建项目时的重名校验,前端未接 | | `delete_character` | 前端 `CharacterApis` 没有删除 | | `Character.description` | 后端存在实体上;前端只在创建入参里,创建完查不到 | | `Character.reference_image_url` | 后端存在实体上;前端 `Character` 类型没有这个字段 | @@ -59,7 +65,7 @@ ID 类型后端为 `int`、前端为 `string`,由前端转换层处理,不 --- -## 四、审核数据无处保存 +## 五、前端资产字段在后端没有落点 后端 `character_data` 的嵌套结构(见 `character/model.py`): @@ -71,22 +77,18 @@ frames[] → index / image_url / duration_ms 前端以下字段在后端结构里没有落点: -- `Frame.qc`(系统质检结论) -- `Frame.rejected`(人工打回) - `Action.kind`(preset / custom 来源) -- `Action.status`(planned / generating / candidate / confirmed / failed) - `Action.keyFrameIndex` - `Frame.rootMotion` - `Outfit.candidateCharacterTemplates`(母版候选列表) +- `Outfit.characterTemplateUrl`(每套造型的已确认角色图) - `Outfit.baseFrames` -**其中 `qc` 与 `rejected` 是审核台的全部依据。后端不存这两个字段,审核结果就落不了库。** - -`candidateCharacterTemplates` 同理:前端设计是生成多张候选让用户选,后端 `CharacterImageInput.num_images` 默认 1,`character_data` 里也没有候选列表。 +`candidateCharacterTemplates` 属于生成过程数据;若只在当前 WorkflowRun 中使用,可以留在前端。其余字段若要随最终资产恢复,需要后端增加字段,或者前端在 MVP 中删除。 --- -## 五、概念不一致 +## 六、概念不一致 后端 `character/model.py` 字段说明: @@ -103,11 +105,16 @@ frames[] → index / image_url / duration_ms ## 待确认 -- [ ] 第一节四条:后端做还是不做 -- [ ] 动作生成一步还是两步 -- [ ] `Frame.qc` / `Frame.rejected` 落不落库 +- [ ] `ActionTemplateApis` 由后端提供还是前端内置 +- [ ] 查询生成任务统一使用 `taskId`,还是 `projectId + taskId` - [ ] 母版候选几张 - [ ] 参考图与角色图是一个字段还是两个 - [ ] `Character.description` 前端要不要跟着存 -- [ ] `jump` 动作类型后端加不加 +- [ ] `Action.kind` / `Action.keyFrameIndex` / `Frame.rootMotion` 是否进入最终资产 - [ ] 上传模块何时提交 + +## 已分工 + +- [x] 前端删除 `WorkflowRunApis`,WorkflowRun 全程由前端管理 +- [x] 前端删除 `Character.name` +- [ ] 后端增加 `jump` 动作类型 From 869827b61576837244ad76ac55c688235cfdd7d1 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 21:15:32 +0800 Subject: [PATCH 19/24] fix(tasks): require project context for task lookup The backend scopes generation-task queries by both project and task identifiers. Add projectId to generation reads and task reads and subscriptions. Callers can now map directly to the documented task query endpoint. --- frontend/src/entities/generation/index.ts | 4 ++-- frontend/src/entities/task/index.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/src/entities/generation/index.ts b/frontend/src/entities/generation/index.ts index 7c932fc..a9d34a1 100644 --- a/frontend/src/entities/generation/index.ts +++ b/frontend/src/entities/generation/index.ts @@ -103,6 +103,6 @@ export interface Generation { export interface GenerationApis { /** 创建一次生成任务。 */ create(input: T): Promise> - /** 读取生成任务的最新快照。 */ - get(id: Generation['id']): Promise + /** 按所属项目和任务 ID 读取生成任务的最新快照。 */ + get(projectId: Generation['projectId'], id: Generation['id']): Promise } diff --git a/frontend/src/entities/task/index.ts b/frontend/src/entities/task/index.ts index bd64883..469d263 100644 --- a/frontend/src/entities/task/index.ts +++ b/frontend/src/entities/task/index.ts @@ -39,8 +39,11 @@ export interface TaskEvent extends Omit + /** + * 按所属项目和后端 Task ID 读取最新快照。 + * projectId 不能从 taskId 推导;后端查询接口要求两者同时传入。 + */ + get(projectId: string, taskId: Task['id']): Promise /** 订阅任务状态变化,返回取消订阅函数。 */ - subscribe(taskId: Task['id'], onEvent: (event: TaskEvent) => void): () => void + subscribe(projectId: string, taskId: Task['id'], onEvent: (event: TaskEvent) => void): () => void } From 7a6515edc3a1f514f0c999bb9cba1ba88c878078 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 21:15:56 +0800 Subject: [PATCH 20/24] docs(api): record project-scoped task lookup Task lookup ownership is now confirmed against the backend API. Mark projectId plus taskId as resolved and remove the stale mismatch and question. Reviewers can see the current task query boundary without reopening the decision. --- frontend/API_CONTRACT.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/API_CONTRACT.md b/frontend/API_CONTRACT.md index e0cd7d2..45e7189 100644 --- a/frontend/API_CONTRACT.md +++ b/frontend/API_CONTRACT.md @@ -11,6 +11,7 @@ - `WorkflowRun` 是前端固定工作流的运行态。后端不读取、不推进、也不持久化,前端不声明 `WorkflowRunApis`。 - `Character` 不使用独立 `name` 字段;前端已删除。 - 前端保留 `jump` 动作类型,由后端补充对应枚举。 +- 查询生成任务统一携带 `projectId + taskId`。 - 前端工作流节点不与后端 `GenerationType` 一一对应,按下表调用: | 前端工作流节点 | 后端接口 | 后端任务类型 | @@ -44,7 +45,6 @@ |---|---|---| | 角色列表 | `list_characters` 分页,返回 `(list, total)` | `listByProject` 无分页 | | 更新角色 | `update_character(character_id, **fields)` 部分更新 | `update(character)` 整棵树替换 | -| 查任务 | `get_task(project_id, task_id)` 需要 `project_id` | `get(taskId)` 只传 taskId | | 等待任务完成 | 提供 `GET /generation/tasks/{task_id}` 轮询 | `TaskApis.subscribe`,实现时可封装轮询 | | 图片生成数量 | 入参有 `num_images`,结果只有一个 `image_url` | 角色图候选结果是 `images[]` | | 动作类型 | `walk` `idle` `attack` `custom`;待增加 `jump` | `walk` `idle` `attack` `jump` `custom` | @@ -106,7 +106,6 @@ frames[] → index / image_url / duration_ms ## 待确认 - [ ] `ActionTemplateApis` 由后端提供还是前端内置 -- [ ] 查询生成任务统一使用 `taskId`,还是 `projectId + taskId` - [ ] 母版候选几张 - [ ] 参考图与角色图是一个字段还是两个 - [ ] `Character.description` 前端要不要跟着存 From d9bd27935fdcf4db6f3ef96bfb2c6fee564afc9b Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 10:45:11 +0800 Subject: [PATCH 21/24] feat(layout): add floating product navigation The global shell needs a persistent navigation surface without moving route awareness into shared UI. Add the LiveDemo-inspired split floating header, route-group highlighting, and Windup mark. Keep the header private to app layout and reserve page content space beneath it. --- frontend/public/windup-mark.svg | 16 +++++ frontend/src/app/layout/app-header.tsx | 98 ++++++++++++++++++++++++++ frontend/src/app/layout/index.tsx | 17 ++--- 3 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 frontend/public/windup-mark.svg create mode 100644 frontend/src/app/layout/app-header.tsx diff --git a/frontend/public/windup-mark.svg b/frontend/public/windup-mark.svg new file mode 100644 index 0000000..5b16f25 --- /dev/null +++ b/frontend/public/windup-mark.svg @@ -0,0 +1,16 @@ + + Windup + 侧视机械小鸟标志 + + + + + + + + + + + + + diff --git a/frontend/src/app/layout/app-header.tsx b/frontend/src/app/layout/app-header.tsx new file mode 100644 index 0000000..9ec3461 --- /dev/null +++ b/frontend/src/app/layout/app-header.tsx @@ -0,0 +1,98 @@ +import { Link, useLocation } from 'react-router' + +interface ProductNavigationItem { + to: string + label: string + compactLabel?: string + isActive: (pathname: string) => boolean +} + +const productNavigation: ProductNavigationItem[] = [ + { + to: '/', + label: '首页', + isActive: (pathname) => pathname === '/', + }, + { + to: '/projects', + label: '项目资产', + compactLabel: '项目', + isActive: (pathname) => pathname.startsWith('/projects') || pathname.startsWith('/playtest'), + }, + { + to: '/quick-start', + label: '创作', + isActive: (pathname) => + pathname.startsWith('/quick-start') || pathname.startsWith('/workflow-editor'), + }, +] + +function getWorkspaceLabel(pathname: string): { title: string; detail: string } { + if (pathname.startsWith('/projects') || pathname.startsWith('/playtest')) { + return { title: '项目资产', detail: '角色、造型与动作' } + } + + if (pathname.startsWith('/quick-start') || pathname.startsWith('/workflow-editor')) { + return { title: '创作工作流', detail: '设定、生成与审核' } + } + + return { title: '角色资产工作台', detail: 'Windup' } +} + +/** 跨页面悬浮 Bar 知道产品路由,因此属于 app 外壳,不下沉到 shared/ui。 */ +export function AppHeader() { + const { pathname } = useLocation() + const workspace = getWorkspaceLabel(pathname) + + return ( +
+
+ + + Windup + + + + {workspace.title} + {workspace.detail} + +
+ + +
+ ) +} diff --git a/frontend/src/app/layout/index.tsx b/frontend/src/app/layout/index.tsx index aa21b82..aa84554 100644 --- a/frontend/src/app/layout/index.tsx +++ b/frontend/src/app/layout/index.tsx @@ -1,5 +1,6 @@ import type { ReactNode } from 'react' -import { Link } from 'react-router' + +import { AppHeader } from './app-header' /** 跨页面常驻导航属于应用外壳,由 app 层统一承载。 */ @@ -12,18 +13,8 @@ export interface AppShellProps { export function AppShell({ children }: AppShellProps) { return (
- -
{children}
+ +
{children}
) } From 5350293e11af8ccd3b41b0c6554b0dd714729eb8 Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 10:45:34 +0800 Subject: [PATCH 22/24] test(layout): cover floating navigation routes Route grouping controls which product navigation item is exposed as current. Render the header on a workflow route and assert the home, project, and creation links. Protect the app-level navigation contract without coupling page tests to layout markup. --- frontend/src/app/layout/app-header.test.tsx | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 frontend/src/app/layout/app-header.test.tsx diff --git a/frontend/src/app/layout/app-header.test.tsx b/frontend/src/app/layout/app-header.test.tsx new file mode 100644 index 0000000..72467eb --- /dev/null +++ b/frontend/src/app/layout/app-header.test.tsx @@ -0,0 +1,22 @@ +// @vitest-environment jsdom +import { cleanup, render, screen } from '@testing-library/react' +import { afterEach, describe, expect, it } from 'vitest' +import { MemoryRouter } from 'react-router' + +import { AppHeader } from './app-header' + +afterEach(cleanup) + +describe('AppHeader', () => { + it('保留三个产品入口,并将工作流路由归入创作', () => { + render( + + + , + ) + + expect(screen.getByRole('link', { name: '返回 Windup 首页' }).getAttribute('href')).toBe('/') + expect(screen.getByRole('link', { name: '项目资产' }).getAttribute('href')).toBe('/projects') + expect(screen.getByRole('link', { name: '创作' }).getAttribute('aria-current')).toBe('page') + }) +}) From 57851c4354c1ba49a9668df19dafea8567aff25f Mon Sep 17 00:00:00 2001 From: huyan Date: Thu, 30 Jul 2026 10:46:00 +0800 Subject: [PATCH 23/24] feat(home): add first-screen entry surface The root route needs a clear first screen for the two existing production entry points. Build a responsive editorial hero and page-private choice cards for quick start and projects. Preserve the existing routes while keeping workflow state outside the home page. --- frontend/src/pages/home/choice-card.tsx | 86 +++++++++++++++++++++ frontend/src/pages/home/index.tsx | 99 ++++++++++++++++++++++--- 2 files changed, 176 insertions(+), 9 deletions(-) create mode 100644 frontend/src/pages/home/choice-card.tsx diff --git a/frontend/src/pages/home/choice-card.tsx b/frontend/src/pages/home/choice-card.tsx new file mode 100644 index 0000000..55e88fe --- /dev/null +++ b/frontend/src/pages/home/choice-card.tsx @@ -0,0 +1,86 @@ +import { Link } from 'react-router' + +export type HomeChoiceCardTone = 'light' | 'dark' + +export interface HomeChoiceCardProps { + to: string + eyebrow: string + index: string + title: string + description: string + actionLabel: string + tone?: HomeChoiceCardTone +} + +/** Home 专用入口卡;只接收显示内容与目标路由,不持有业务状态。 */ +export function HomeChoiceCard({ + to, + eyebrow, + index, + title, + description, + actionLabel, + tone = 'light', +}: HomeChoiceCardProps) { + const dark = tone === 'dark' + + return ( + +