diff --git a/.gitignore b/.gitignore index 7f63c8e..5455db3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.swp *.swo *~ +/node_modules \ No newline at end of file diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/web/.npmrc b/web/.npmrc new file mode 100644 index 0000000..214c29d --- /dev/null +++ b/web/.npmrc @@ -0,0 +1 @@ +registry=https://registry.npmjs.org/ diff --git a/web/README.md b/web/README.md new file mode 100644 index 0000000..7dbf7eb --- /dev/null +++ b/web/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/web/eslint.config.js b/web/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/web/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..ec41311 --- /dev/null +++ b/web/index.html @@ -0,0 +1,13 @@ + + + + + + + Claude Design Skill · 视频演示 + + +
+ + + diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 0000000..34a5124 --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,3013 @@ +{ + "name": "my-app", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "my-app", + "version": "0.0.0", + "dependencies": { + "react": "^19.2.5", + "react-dom": "^19.2.5" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/node": "^24.12.2", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.5.0", + "typescript": "~6.0.2", + "typescript-eslint": "^8.58.2", + "vite": "^8.0.9" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "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.126.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.126.0.tgz", + "integrity": "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-rhY3k7Bsae9qQfOtph2Pm2jZEA+s8Gmjoz4hhmx70K9iMQ/ddeae+xhRQcM5IuVx5ry1+bGfkvMn7D6MJggVSA==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-rNz0yK078yrNn3DrdgN+PKiMOW8HfQ92jQiXxwX8yW899ayV00MLVdaCNeVBhG/TbH3ouYVObo8/yrkiectkcQ==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.16.tgz", + "integrity": "sha512-r/OmdR00HmD4i79Z//xO06uEPOq5hRXdhw7nzkxQxwSavs3PSHa1ijntdpOiZ2mzOQ3fVVu8C1M19FoNM+dMUQ==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.16.tgz", + "integrity": "sha512-KcRE5w8h0OnjUatG8pldyD14/CQ5Phs1oxfR+3pKDjboHRo9+MkqQaiIZlZRpsxC15paeXme/I127tUa9TXJ6g==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.16.tgz", + "integrity": "sha512-bT0guA1bpxEJ/ZhTRniQf7rNF8ybvXOuWbNIeLABaV5NGjx4EtOWBTSRGWFU9ZWVkPOZ+HNFP8RMcBokBiZ0Kg==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-+tHktCHWV8BDQSjemUqm/Jl/TPk3QObCTIjmdDy/nlupcujZghmKK2962LYrqFpWu+ai01AN/REOH3NEpqvYQg==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.16.tgz", + "integrity": "sha512-3fPzdREH806oRLxpTWW1Gt4tQHs0TitZFOECB2xzCFLPKnSOy90gwA7P29cksYilFO6XVRY1kzga0cL2nRjKPg==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-EKwI1tSrLs7YVw+JPJT/G2dJQ1jl9qlTTTEG0V2Ok/RdOenRfBw2PQdLPyjhIu58ocdBfP7vIRN/pvMsPxs/AQ==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-Uknladnb3Sxqu6SEcqBldQyJUpk8NleooZEc0MbRBJ4inEhRYWZX0NJu12vNf2mqAq7gsofAxHrGghiUYjhaLQ==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-FIb8+uG49sZBtLTn+zt1AJ20TqVcqWeSIyoVt0or7uAWesgKaHbiBh6OpA/k9v0LTt+PTrb1Lao133kP4uVxkg==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.16.tgz", + "integrity": "sha512-RuERhF9/EgWxZEXYWCOaViUWHIboceK4/ivdtQ3R0T44NjLkIIlGIAVAuCddFxsZ7vnRHtNQUrt2vR2n2slB2w==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-mXcXnvd9GpazCxeUCCnZ2+YF7nut+ZOEbE4GtaiPtyY6AkhZWbK70y1KK3j+RDhjVq5+U8FySkKRb/+w0EeUwA==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.16.tgz", + "integrity": "sha512-3Q2KQxnC8IJOLqXmUMoYwyIPZU9hzRbnHaoV3Euz+VVnjZKcY8ktnNP8T9R4/GGQtb27C/UYKABxesKWb8lsvQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.16.tgz", + "integrity": "sha512-tj7XRemQcOcFwv7qhpUxMTBbI5mWMlE4c1Omhg5+h8GuLXzyj8HviYgR+bB2DMDgRqUE+jiDleqSCRjx4aYk/Q==", + "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.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.16.tgz", + "integrity": "sha512-PH5DRZT+F4f2PTXRXR8uJxnBq2po/xFtddyabTJVJs/ZYVHqXPEgNIr35IHTEa6bpa0Q8Awg+ymkTaGnKITw4g==", + "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.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "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/@typescript-eslint/eslint-plugin": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz", + "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/type-utils": "8.59.0", + "@typescript-eslint/utils": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz", + "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", + "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.0", + "@typescript-eslint/types": "^8.59.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", + "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", + "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz", + "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/utils": "8.59.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", + "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", + "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.0", + "@typescript-eslint/tsconfig-utils": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", + "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", + "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "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/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz", + "integrity": "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001788", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", + "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "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/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "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/electron-to-chromium": { + "version": "1.5.340", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.340.tgz", + "integrity": "sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==", + "dev": true, + "license": "ISC" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "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/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "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/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz", + "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "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" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/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/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/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/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/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/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/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/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/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/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/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/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "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.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.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.5", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", + "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", + "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.5" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.16.tgz", + "integrity": "sha512-rzi5WqKzEZw3SooTt7cgm4eqIoujPIyGcJNGFL7iPEuajQw7vxMHUkXylu4/vhCkJGXsgRmxqMKXUpT6FEgl0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.126.0", + "@rolldown/pluginutils": "1.0.0-rc.16" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.16", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.16", + "@rolldown/binding-darwin-x64": "1.0.0-rc.16", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.16", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.16", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.16", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.16", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.16", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.16", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.16", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.16" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.16.tgz", + "integrity": "sha512-45+YtqxLYKDWQouLKCrpIZhke+nXxhsw+qAHVzHDVwttyBlHNBVs2K25rDXrZzhpTp9w1FlAlvweV1H++fdZoA==", + "dev": true, + "license": "MIT" + }, + "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/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "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/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "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/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/typescript-eslint": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.0.tgz", + "integrity": "sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.0", + "@typescript-eslint/parser": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/utils": "8.59.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.9.tgz", + "integrity": "sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.10", + "rolldown": "1.0.0-rc.16", + "tinyglobby": "^0.2.16" + }, + "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.1.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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..dd4aa46 --- /dev/null +++ b/web/package.json @@ -0,0 +1,30 @@ +{ + "name": "my-app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.5", + "react-dom": "^19.2.5" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/node": "^24.12.2", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.5.0", + "typescript": "~6.0.2", + "typescript-eslint": "^8.58.2", + "vite": "^8.0.9" + } +} diff --git a/web/public/favicon.svg b/web/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/web/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/public/video.mp4 b/web/public/video.mp4 new file mode 100644 index 0000000..6a33bfc Binary files /dev/null and b/web/public/video.mp4 differ diff --git a/web/src/App.tsx b/web/src/App.tsx new file mode 100644 index 0000000..002ae94 --- /dev/null +++ b/web/src/App.tsx @@ -0,0 +1,32 @@ +import { Stage } from './stage/Stage'; +import { ChapterHost } from './stage/ChapterHost'; +import { ProgressBar } from './stage/ProgressBar'; +import { useHotKeys } from './stage/useHotKeys'; +import { stepStore, useStep } from './store/useStep'; +import { chapters } from './chapters'; + +function App() { + useHotKeys(); + const { chapterIndex } = useStep(); + const theme = chapters[chapterIndex]?.theme ?? 'light'; + + return ( +
{ + // 任何带 data-no-step 的祖先都不触发推进 + const target = e.target as HTMLElement; + if (target.closest('[data-no-step]')) return; + stepStore.next(); + }} + > + + + +
+ +
+
+ ); +} + +export default App; diff --git a/web/src/chapters/01-opening/Opening.css b/web/src/chapters/01-opening/Opening.css new file mode 100644 index 0000000..914f7a7 --- /dev/null +++ b/web/src/chapters/01-opening/Opening.css @@ -0,0 +1,705 @@ +/* ========================================================= + Chapter 01 · Opening 「一则崩盘」 + - ink 主题(深墨底) + - 4 幕全部由 SceneFade 包裹独立铺满 stage,互不重叠 + - 没有任何"网页 chrome"(无顶部章节标记 / 底部 footer) + - 真实数据:LegalZoom -20% / CRCL -20% / CrowdStrike -7% / Figma -7% + ========================================================= */ + +.opening { + position: absolute; + inset: 0; + font-family: var(--f-sans); + color: var(--fg); + background: var(--bg); + overflow: hidden; +} + +/* 每幕都是覆盖 stage 的全屏容器 */ +.opening__sceneA, +.opening__sceneB, +.opening__sceneC, +.opening__sceneD { + position: absolute; + inset: 0; + padding: 110px 120px; +} + +.opening__sceneC { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; +} + +.opening__sceneD { + display: flex; + flex-direction: column; + justify-content: center; +} + +/* ========================================================= + SCENE A · Crash + ========================================================= */ + +/* LIVE 报价条(仅 Scene A 出现,作为"市场环境") */ +.opening__live { + position: absolute; + top: 90px; + right: 120px; + display: flex; + align-items: center; + gap: 18px; + padding: 10px 20px; + border: 1px solid var(--line-mid); + background: oklch(0.965 0.018 78 / 0.025); + backdrop-filter: blur(6px); + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-mute); + z-index: 6; + animation: opLiveIn 700ms cubic-bezier(.2,.8,.2,1) both; +} +@keyframes opLiveIn { + from { opacity: 0; transform: translateY(-12px); } + to { opacity: 1; transform: translateY(0); } +} +.opening__live-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--crimson); + animation: opPulse 1.6s ease-in-out infinite; +} +@keyframes opPulse { + 0%, 100% { opacity: 0.4; transform: scale(0.85); } + 50% { opacity: 1; transform: scale(1.15); } +} +.opening__live-label { + color: var(--fg-soft); + font-weight: 500; +} +.opening__live-clock { + color: var(--paper); + font-size: 18px; + letter-spacing: 0.08em; +} +.opening__live-sep { + color: var(--fg-faint); +} +.opening__live-quote { + display: flex; + align-items: baseline; + gap: 10px; +} +.opening__live-quote-tag { + color: var(--fg-mute); + font-size: 13px; + padding: 2px 6px; + border: 1px solid var(--line-mid); +} +.opening__live-quote-val { + color: var(--paper); + font-size: 18px; +} +.opening__live-quote-d { + color: var(--crimson); + font-size: 14px; +} + +/* "FIGMA" 巨字背景水印 */ +.opening__crash-mega { + position: absolute; + left: 100px; + bottom: 120px; + font-family: var(--f-serif); + font-style: italic; + font-size: 360px; + line-height: 0.85; + color: oklch(0.965 0.018 78 / 0.05); + letter-spacing: -0.04em; + user-select: none; + pointer-events: none; + white-space: nowrap; + z-index: 1; +} + +/* 引子小字 */ +.opening__crash-intro { + position: absolute; + top: 230px; + left: 120px; + display: flex; + align-items: center; + gap: 18px; + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.18em; + color: var(--fg-mute); + z-index: 4; +} +.opening__crash-intro-tag { + padding: 5px 12px; + border: 1px solid var(--line-mid); + color: var(--fg-soft); + text-transform: uppercase; +} +.opening__crash-intro-text { + font-family: var(--f-sans); + font-size: 22px; + letter-spacing: 0; + color: var(--fg-soft); +} + +/* 折线图 */ +.opening__chart { + position: absolute; + inset: 320px 80px 280px; + width: calc(100% - 160px); + height: calc(100% - 600px); + z-index: 2; +} +.opening__chart-grid line { + stroke: var(--line); +} +.opening__chart-history { + stroke: var(--fg-faint); + opacity: 0.65; + stroke-dasharray: 1400; + stroke-dashoffset: 1400; + animation: opPathDraw 1500ms cubic-bezier(.2,.8,.2,1) forwards, + opHistoryHum 6s ease-in-out 1500ms infinite alternate; +} +@keyframes opHistoryHum { + 0% { transform: translateY(0); } + 50% { transform: translateY(-3px); } + 100% { transform: translateY(2px); } +} +.opening__chart-crash { + stroke: var(--crimson); + stroke-linecap: round; + stroke-dasharray: 800; + stroke-dashoffset: 800; + animation: opPathDraw 720ms cubic-bezier(.4,0,1,1) 80ms forwards; + filter: drop-shadow(0 0 24px oklch(0.560 0.200 22 / 0.5)); +} +@keyframes opPathDraw { + to { stroke-dashoffset: 0; } +} +.opening__chart-fill { + opacity: 0; + animation: opFadeIn 600ms cubic-bezier(.2,.8,.2,1) 700ms forwards; +} +.opening__chart-end { + opacity: 0; + animation: opFadeIn 500ms cubic-bezier(.2,.8,.2,1) 900ms forwards; +} +.opening__chart-end circle { + fill: var(--crimson); + filter: drop-shadow(0 0 10px oklch(0.560 0.200 22 / 0.7)); + animation: opEndPulse 1.8s ease-in-out infinite; + transform-origin: 1760px 510px; +} +@keyframes opEndPulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.4); } +} +.opening__chart-end line { + stroke: var(--fg-faint); +} +.opening__chart-end text { + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.15em; + fill: var(--fg-mute); + text-transform: uppercase; +} +@keyframes opFadeIn { + to { opacity: 1; } +} + +/* 中文 headline */ +.opening__crash-headline { + position: absolute; + bottom: 140px; + left: 120px; + right: 600px; + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: 22px; + z-index: 4; +} +.opening__crash-line { + font-size: 42px; + color: var(--fg-soft); + font-weight: 300; + letter-spacing: 0.02em; +} +.opening__crash-emph { + font-family: var(--f-serif); + font-style: italic; + font-size: 130px; + line-height: 0.95; + color: var(--paper); + letter-spacing: -0.02em; + font-weight: 400; +} + +/* 跌幅大数字 */ +.opening__crash-stat { + position: absolute; + right: 120px; + top: 230px; + text-align: right; + z-index: 4; +} +.opening__crash-stat-num { + font-family: var(--f-serif); + font-size: 240px; + line-height: 0.9; + color: var(--crimson); + letter-spacing: -0.04em; + font-feature-settings: "tnum"; + display: flex; + align-items: baseline; + justify-content: flex-end; + filter: drop-shadow(0 0 40px oklch(0.560 0.200 22 / 0.35)); +} +.opening__crash-stat-sign { + margin-right: 6px; +} +.opening__crash-stat-pct { + font-size: 130px; + margin-left: 4px; +} +.opening__crash-stat-label { + margin-top: 18px; + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.22em; + text-transform: uppercase; + color: var(--fg-mute); +} + +/* ========================================================= + SCENE B · Victims + ========================================================= */ +.opening__vics-eyebrow { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 18px; + margin-bottom: 56px; +} +.opening__vics-eyebrow-inner { + display: flex; + align-items: baseline; + gap: 18px; + font-size: 38px; + color: var(--fg-soft); + font-weight: 300; +} +.opening__vics-em { + font-family: var(--f-serif); + font-style: italic; + font-size: 92px; + color: var(--accent); + font-weight: 400; + line-height: 1; + position: relative; +} +.opening__vics-em::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: 8px; + height: 8px; + background: oklch(0.700 0.170 42 / 0.25); + z-index: -1; + animation: opUnderlineGrow 700ms cubic-bezier(.2,.8,.2,1) 320ms both; + transform-origin: 0 50%; +} +@keyframes opUnderlineGrow { + from { transform: scaleX(0); } + to { transform: scaleX(1); } +} +.opening__vics-by { + display: flex; + align-items: baseline; + gap: 14px; + font-family: var(--f-mono); + text-transform: uppercase; + letter-spacing: 0.22em; +} +.opening__vics-by-tag { + font-size: 14px; + color: var(--fg-faint); +} +.opening__vics-by-name { + font-family: var(--f-serif); + font-style: italic; + font-size: 60px; + color: var(--accent); + text-transform: none; + letter-spacing: -0.01em; +} + +.opening__vics-table { + display: flex; + flex-direction: column; + border-top: 1px solid var(--line-mid); +} +.opening__vics-thead { + display: grid; + grid-template-columns: 1.4fr 1.2fr 0.5fr 0.7fr 0.5fr; + gap: 32px; + padding: 14px 0; + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.22em; + text-transform: uppercase; + color: var(--fg-faint); + border-bottom: 1px solid var(--line); +} + +.opening__vic { + display: grid; + grid-template-columns: 1.4fr 1.2fr 0.5fr 0.7fr 0.5fr; + gap: 32px; + align-items: baseline; + padding: 30px 0; + border-bottom: 1px solid var(--line); + position: relative; +} +.opening__vic-product { + font-family: var(--f-mono); + font-size: 22px; + font-weight: 500; + color: var(--accent); + letter-spacing: -0.01em; + display: flex; + align-items: baseline; + gap: 12px; +} +.opening__vic-product::before { + content: "▸"; + color: var(--accent); + font-size: 16px; +} +.opening__vic-company { + font-family: var(--f-serif); + font-size: 60px; + letter-spacing: -0.01em; + color: var(--fg); + line-height: 1; +} +.opening__vic-ticker { + font-family: var(--f-mono); + font-size: 16px; + color: var(--fg-mute); + text-transform: uppercase; + letter-spacing: 0.1em; + border: 1px solid var(--line-mid); + padding: 4px 10px; + display: inline-block; + width: fit-content; +} +.opening__vic-field { + font-size: 18px; + color: var(--fg-mute); +} +.opening__vic-drop { + font-family: var(--f-mono); + font-size: 44px; + font-weight: 500; + color: var(--fg-mute); + text-align: right; + font-feature-settings: "tnum"; + letter-spacing: -0.01em; +} +.opening__vic.is-current { + background: linear-gradient( + to right, + transparent 0%, + oklch(0.700 0.170 42 / 0.05) 30%, + oklch(0.700 0.170 42 / 0.10) 70%, + transparent 100% + ); +} +.opening__vic.is-current .opening__vic-company { + color: var(--paper); +} +.opening__vic.is-current .opening__vic-drop { + color: var(--crimson); +} +.opening__vic-flag { + position: absolute; + right: 0; + top: -4px; + font-family: var(--f-mono); + font-size: 11px; + letter-spacing: 0.3em; + text-transform: uppercase; + color: var(--accent); + padding: 3px 8px; + border: 1px solid var(--accent); + background: var(--bg); + animation: opFlagPulse 1.6s ease-in-out infinite; +} +@keyframes opFlagPulse { + 0%, 100% { opacity: 0.5; } + 50% { opacity: 1; } +} + +/* ========================================================= + SCENE C · Claude Design 揭幕 + ========================================================= */ +.opening__reveal-meta { + display: flex; + align-items: center; + gap: 22px; + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.3em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 56px; +} +.opening__reveal-meta .dot { + width: 4px; + height: 4px; + background: var(--fg-mute); + border-radius: 50%; +} + +.opening__reveal-title { + display: flex; + align-items: baseline; + gap: 32px; + margin: 0; + font-size: 220px; + line-height: 0.9; + font-family: var(--f-serif); + font-weight: 400; + letter-spacing: -0.03em; +} +.opening__reveal-word { + display: inline-block; +} +.opening__reveal-word--em { + font-style: italic; + color: var(--accent); +} + +.opening__reveal-sub { + margin-top: 50px; + display: flex; + align-items: center; + gap: 22px; + font-size: 52px; + font-weight: 300; + color: var(--fg-soft); +} +.opening__reveal-tilde { + color: var(--accent); + font-family: var(--f-serif); + font-style: italic; +} + +.opening__reveal-tags { + margin-top: 56px; + display: flex; + gap: 14px; +} +.opening__reveal-tags span { + padding: 10px 20px; + border: 1px solid var(--line-mid); + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-mute); + background: oklch(0.965 0.018 78 / 0.025); +} + +/* ========================================================= + SCENE D · Skill pivot + ========================================================= */ +.opening__pivot-eyebrow { + display: flex; + align-items: center; + gap: 16px; + font-family: var(--f-mono); + font-size: 18px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--accent); + margin-bottom: 36px; +} +.opening__pivot-eyebrow-bar { + width: 56px; + height: 1px; + background: var(--accent); +} + +.opening__pivot-title { + font-family: var(--f-serif); + font-weight: 400; + font-size: 124px; + line-height: 1.05; + letter-spacing: -0.025em; + color: var(--paper); + max-width: 1500px; + margin: 0 0 64px 0; + display: flex; + flex-wrap: wrap; + gap: 0 18px; +} +.opening__pivot-em { + color: var(--accent); + font-style: italic; + position: relative; + white-space: nowrap; +} +.opening__pivot-em::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: 6px; + height: 8px; + background: oklch(0.700 0.170 42 / 0.25); + z-index: -1; + animation: opUnderlineGrow 800ms cubic-bezier(.2,.8,.2,1) 1100ms both; + transform-origin: 0 50%; +} + +.opening__pivot-row { + display: grid; + grid-template-columns: 1.1fr 1fr; + gap: 80px; + align-items: center; +} + +.opening__skill-card { + position: relative; + border: 1px solid var(--line-mid); + background: oklch(0.965 0.018 78 / 0.025); + padding: 36px 44px; + backdrop-filter: blur(4px); +} +.opening__skill-card-deco::before, +.opening__skill-card-deco::after { + content: ""; + position: absolute; + width: 18px; + height: 18px; +} +.opening__skill-card-deco::before { + top: -1px; + left: -1px; + border-top: 2px solid var(--accent); + border-left: 2px solid var(--accent); +} +.opening__skill-card-deco::after { + bottom: -1px; + right: -1px; + border-bottom: 2px solid var(--accent); + border-right: 2px solid var(--accent); +} + +.opening__skill-card-head { + display: flex; + align-items: baseline; + gap: 18px; + padding-bottom: 22px; + border-bottom: 1px solid var(--line); + margin-bottom: 24px; +} +.opening__skill-card-tag { + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.28em; + text-transform: uppercase; + color: var(--accent); + padding: 4px 10px; + border: 1px solid var(--accent); +} +.opening__skill-card-name { + font-family: var(--f-mono); + font-size: 32px; + font-weight: 500; + color: var(--paper); + letter-spacing: -0.01em; +} +.opening__skill-card-body { + display: flex; + flex-direction: column; + gap: 12px; +} +.opening__skill-card-row { + display: grid; + grid-template-columns: 130px 1fr; + gap: 24px; + align-items: baseline; + font-size: 22px; +} +.opening__skill-card-k { + font-family: var(--f-mono); + text-transform: uppercase; + letter-spacing: 0.18em; + font-size: 13px; + color: var(--fg-mute); +} +.opening__skill-card-v { + color: var(--paper); +} +.opening__skill-card-foot { + margin-top: 24px; + padding-top: 18px; + border-top: 1px solid var(--line); + display: flex; + align-items: center; + gap: 12px; + font-family: var(--f-mono); + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.22em; + color: var(--fg-mute); +} +.opening__skill-card-pulse { + width: 8px; + height: 8px; + border-radius: 50%; + background: oklch(0.78 0.18 145); + animation: opPulse 1.6s ease-in-out infinite; +} + +.opening__pivot-aside { + font-family: var(--f-serif); + font-size: 56px; + line-height: 1.2; + color: var(--fg-soft); + position: relative; +} +.opening__pivot-aside-q { + position: absolute; + left: -56px; + top: -36px; + font-size: 200px; + color: var(--accent); + opacity: 0.4; + font-style: italic; + line-height: 1; +} +.opening__pivot-aside em { + font-style: italic; + color: var(--paper); + display: block; + margin-top: 12px; +} diff --git a/web/src/chapters/01-opening/index.tsx b/web/src/chapters/01-opening/index.tsx new file mode 100644 index 0000000..349cfe6 --- /dev/null +++ b/web/src/chapters/01-opening/index.tsx @@ -0,0 +1,383 @@ +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import { NumberTicker } from '../../shared/NumberTicker'; +import { LiveClock, FlickerNumber } from '../../shared/LiveTicker'; +import { SceneFade } from '../../shared/SceneFade'; +import './Opening.css'; + +/** + * Chapter 01 · Opening 「一则崩盘」 + * + * 4 幕 × 14 个 step: + * Scene A · Crash (0..3) Figma 股价崩盘 + * Scene B · Victims (4..8) Anthropic 历次带崩列表 + * Scene C · Reveal (9..11) Claude Design 揭幕 + * Scene D · Skill (12..13) 我把它做成了 Skill + * + * 真实数据:LegalZoom -20% / CRCL -20% / CrowdStrike -7% / Figma -7% + * + * 设计原则: + * - 没有任何"网页 chrome"(没有顶部章节标记 / 底部 footer / 永久 hint) + * - 每幕通过 SceneFade 优雅交叉淡入淡出,不会有重叠 bug + * - 每个 step 推进只增加 1 个新视觉元素 + */ + +const VICTIMS = [ + { product: 'Claude Cowork', company: 'LegalZoom', ticker: 'LZ', drop: -20.0, field: '法律服务' }, + { product: 'Claude Code Security', company: 'Circle Internet', ticker: 'CRCL', drop: -20.0, field: '云安全' }, + { product: 'Claude Mythos', company: 'CrowdStrike', ticker: 'CRWD', drop: -7.0, field: '终端安全' }, + { product: 'Claude Design', company: 'Figma', ticker: 'FIG', drop: -7.0, field: '设计协作', current: true }, +]; + +function Opening({ localStep }: ChapterContext) { + const at = (n: number) => localStep >= n; + const between = (a: number, b: number) => localStep >= a && localStep <= b; + + const sceneCrash = localStep <= 3; + const sceneVictims = between(4, 8); + const sceneReveal = between(9, 11); + const sceneSkill = at(12); + + return ( +
+ {/* ============== SCENE A · Crash (0..3) ============== */} + +
+ {/* LIVE 报价条 —— 仅在崩盘场景中作为"市场环境"出现 */} +
+ + NASDAQ · LIVE + + | + + FIG + + −7.0% + +
+ + {/* "FIGMA" 巨字背景 —— step 2 才显形 */} + {at(2) && ( + +
FIGMA
+
+ )} + + {/* 引子小字 —— step 1 */} + {at(1) && ( + + 2026 · 04 · 17 · NASDAQ CLOSE + 就在前两天 —— + + )} + + {/* 折线图 —— step 1 开始绘制历史 / step 2 崩盘 */} + + + {/* 中文 headline —— step 2 */} + {at(2) && ( + + Figma + 的股价崩了。 + + )} + + {/* −7.0% 大数字 —— step 3 */} + {at(3) && ( + +
+ + + % +
+
单日跌幅 · 收盘
+
+ )} +
+
+ + {/* ============== SCENE B · Victims (4..8) ============== */} + +
+
+ + 这已经是 + 第 N 次 + 了 —— + + + CRASHED · BY + ANTHROPIC + +
+ +
+
+ ANTHROPIC PRODUCT + AFFECTED COMPANY + TICKER + SECTOR + DAY DROP +
+ + {VICTIMS.map((v, i) => { + const showAt = 5 + i; + if (!at(showAt)) return null; + return ( + + + + ); + })} +
+
+
+ + {/* ============== SCENE C · Claude Design 揭幕 (9..11) ============== */} + +
+ + ANTHROPIC + + NEW PRODUCT + + 2026 · 04 · 17 + + +

+ + Claude + + {at(10) && ( + + Design + + )} +

+ + {at(10) && ( + + —— + 设计师的 Claude Code + + )} + + {at(11) && ( +
+ + Powered by Opus 4.7 + + + Pro · Max · Team · Enterprise + + + 左侧聊天 · 右侧画布 + +
+ )} +
+
+ + {/* ============== SCENE D · Skill (12..13) ============== */} + +
+ + + 于是 —— + + +

+ + 我把它做成了一个 + + + 人人都能用的 Skill。 + +

+ + {at(13) && ( +
+ + + + + " + 人人都能成为 + 顶级网站设计师。 + +
+ )} +
+
+
+ ); +} + +/* ────────────── 子组件 ────────────── */ + +function CrashChart({ phase }: { phase: number }) { + const showHistory = phase >= 1; + const showCrash = phase >= 2; + const showFill = phase >= 2; + const showEnd = phase >= 2; + + return ( + + + + + + + + + + {[0, 1, 2, 3].map((i) => ( + + ))} + + + {showHistory && ( + + )} + + {showCrash && ( + + )} + + {showFill && ( + + )} + + {showEnd && ( + + + + 前日收盘 51.95 + 今日收盘 48.32 + + )} + + ); +} + +interface VictimRowProps { + v: typeof VICTIMS[number]; + animate: boolean; +} + +function VictimRow({ v, animate }: VictimRowProps) { + return ( +
+ {v.product} + {v.company} + ${v.ticker} + {v.field} + + {animate ? ( + + ) : ( + + −{Math.abs(v.drop).toFixed(1)}% + + )} + + {v.current && CURRENT} +
+ ); +} + +function SkillCard() { + return ( +
+
+
+ SKILL · v1 + web-design-engineer +
+
+
+ 支持环境 + Cursor · Claude Code · Codex +
+
+ 体量 + ≈ 400 行 · 含模板库 ≈ 520 行 +
+
+ 来源 + Claude Design 系统提示词 · 提炼 + 改良 +
+
+ 效果 + 85 → 95 分 +
+
+
+ + READY · open-source +
+
+ ); +} + +const def: ChapterDef = { + id: 'opening', + title: '开场 · 一则崩盘', + eyebrow: '01', + steps: 14, + theme: 'ink', + Component: Opening, +}; + +export default def; diff --git a/web/src/chapters/02-video/Video.css b/web/src/chapters/02-video/Video.css new file mode 100644 index 0000000..f78015a --- /dev/null +++ b/web/src/chapters/02-video/Video.css @@ -0,0 +1,183 @@ +/* ========================================================= + Chapter 02 · Anthropic 官方宣传片 + light 主题 · 米色纸感底 · 中央虚拟显示器外框 + ========================================================= */ + +.vid { + position: absolute; + inset: 0; + background: var(--bg); + color: var(--fg); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 100px 80px; + font-family: var(--f-sans); + overflow: hidden; +} + +/* TV 外壳 */ +.vid__tv-wrap { + display: flex; + flex-direction: column; + align-items: center; +} + +.vid__tv { + position: relative; + width: 1280px; + background: linear-gradient( + 180deg, + oklch(0.245 0.015 60) 0%, + oklch(0.190 0.014 60) 100% + ); + border-radius: 18px; + padding: 18px 22px 22px; + box-shadow: + 0 60px 80px -40px oklch(0.180 0.014 60 / 0.35), + 0 24px 36px -20px oklch(0.180 0.014 60 / 0.20), + inset 0 1px 0 oklch(0.965 0.018 78 / 0.08), + inset 0 -1px 0 oklch(0.180 0.014 60 / 0.6); +} + +/* 角落螺丝 */ +.vid__screw { + position: absolute; + width: 8px; + height: 8px; + border-radius: 50%; + background: radial-gradient( + circle at 35% 35%, + oklch(0.700 0.012 60) 0%, + oklch(0.380 0.012 60) 70%, + oklch(0.180 0.012 60) 100% + ); + box-shadow: inset 0 0 1px oklch(0.180 0.012 60); +} +.vid__screw--tl { top: 12px; left: 12px; } +.vid__screw--tr { top: 12px; right: 12px; } +.vid__screw--bl { bottom: 14px; left: 12px; } +.vid__screw--br { bottom: 14px; right: 12px; } + +/* 顶部状态条 */ +.vid__topstrip { + display: flex; + align-items: center; + gap: 12px; + padding: 0 18px 12px; + font-family: var(--f-mono); + font-size: 11px; + letter-spacing: 0.28em; + color: oklch(0.620 0.020 60); + text-transform: uppercase; +} +.vid__led { + width: 7px; + height: 7px; + border-radius: 50%; + background: oklch(0.78 0.18 145); + box-shadow: 0 0 8px oklch(0.78 0.18 145 / 0.7); + animation: vidLedPulse 1.4s ease-in-out infinite; +} +@keyframes vidLedPulse { + 0%, 100% { opacity: 0.5; } + 50% { opacity: 1; } +} +.vid__topstrip-spacer { + flex: 1; +} + +/* 屏幕(视频容器) */ +.vid__screen { + position: relative; + aspect-ratio: 16 / 9; + background: #000; + border-radius: 6px; + overflow: hidden; + box-shadow: + inset 0 0 0 1px oklch(0.180 0.014 60), + inset 0 0 24px oklch(0.180 0.014 60 / 0.6); +} + +.vid__video { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + background: #000; +} + +/* 微弱扫描线 */ +.vid__scanlines { + position: absolute; + inset: 0; + pointer-events: none; + z-index: 2; + background-image: repeating-linear-gradient( + 180deg, + oklch(0.965 0.018 78 / 0) 0px, + oklch(0.965 0.018 78 / 0) 2px, + oklch(0.965 0.018 78 / 0.025) 3px, + oklch(0.965 0.018 78 / 0) 4px + ); + mix-blend-mode: overlay; + opacity: 0.6; +} + +/* 底部品牌条 */ +.vid__brandstrip { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + padding: 14px 18px 4px; + font-family: var(--f-mono); + color: oklch(0.620 0.020 60); + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.18em; +} +.vid__brand-mark { + width: 14px; + height: 14px; + background: var(--accent); + clip-path: polygon(50% 0, 100% 100%, 0 100%); +} +.vid__brand-name { + font-weight: 600; + color: oklch(0.880 0.020 78); + letter-spacing: 0.22em; +} + +/* 底座 */ +.vid__stand { + display: flex; + flex-direction: column; + align-items: center; + margin-top: -2px; +} +.vid__stand-neck { + width: 200px; + height: 24px; + background: linear-gradient( + 180deg, + oklch(0.220 0.015 60) 0%, + oklch(0.180 0.014 60) 100% + ); + clip-path: polygon(8% 0, 92% 0, 100% 100%, 0 100%); + box-shadow: 0 4px 8px -2px oklch(0.180 0.014 60 / 0.3); +} +.vid__stand-base { + width: 340px; + height: 8px; + background: linear-gradient( + 180deg, + oklch(0.230 0.015 60) 0%, + oklch(0.170 0.014 60) 100% + ); + border-radius: 4px; + box-shadow: + 0 12px 24px -8px oklch(0.180 0.014 60 / 0.5), + 0 4px 6px -1px oklch(0.180 0.014 60 / 0.3); +} diff --git a/web/src/chapters/02-video/index.tsx b/web/src/chapters/02-video/index.tsx new file mode 100644 index 0000000..2e1f401 --- /dev/null +++ b/web/src/chapters/02-video/index.tsx @@ -0,0 +1,90 @@ +import { useEffect, useRef } from 'react'; +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import './Video.css'; + +/** + * Chapter 02 · Anthropic 官方宣传片嵌入 + * + * 浅色纸感背景 + 虚拟电视外框 + 16:9 视频。视频源:/video.mp4 + * + * 仅保留电视外框 —— 不放任何文案 / eyebrow / caption。 + * - 进入时静音自动播放(绕过浏览器策略),用户可通过 controls 解除静音 / 暂停 + * - TV + 底座 data-no-step,操作 controls 不会推进 + * - 点击 TV 之外的留白才会推进到 Ch03 + */ +function VideoChapter(_: ChapterContext) { + const videoRef = useRef(null); + + useEffect(() => { + const v = videoRef.current; + if (!v) return; + v.currentTime = 0; + const play = v.play(); + if (play && typeof play.catch === 'function') { + play.catch(() => {/* 用户必须自行点击播放 */}); + } + return () => { + v.pause(); + }; + }, []); + + return ( +
+ +
+ {/* 角注 4 颗螺丝 */} + + + + + + {/* 顶部状态条 */} +
+ + ON · CH · 02 + + SIGNAL · STABLE +
+ + {/* 屏幕 */} +
+
+
+ + {/* 底部品牌条 */} +
+ + ANTHROPIC +
+
+ + {/* 底座 */} +
+ + +
+ +
+ ); +} + +const def: ChapterDef = { + id: 'video', + title: '官方宣传片', + eyebrow: '02', + steps: 1, + theme: 'light', + Component: VideoChapter, +}; + +export default def; diff --git a/web/src/chapters/03-core-point/CorePoint.css b/web/src/chapters/03-core-point/CorePoint.css new file mode 100644 index 0000000..334df2c --- /dev/null +++ b/web/src/chapters/03-core-point/CorePoint.css @@ -0,0 +1,480 @@ +/* ========================================================= + Chapter 03 · 核心观点 + ink 主题 · hero → 50/50 split → 提示词倾泻 → leaked → pivot + ========================================================= */ + +.cp { + position: absolute; + inset: 0; + background: var(--bg); + color: var(--fg); + font-family: var(--f-sans); + overflow: hidden; +} + +/* 背景网格氛围 */ +.cp__grid { + position: absolute; + inset: 0; + background-image: + linear-gradient(to right, var(--line) 0, var(--line) 1px, transparent 1px), + linear-gradient(to bottom, var(--line) 0, var(--line) 1px, transparent 1px); + background-size: 80px 80px; + mask-image: radial-gradient(circle at 50% 50%, #000 0%, #000 60%, transparent 95%); + pointer-events: none; + opacity: 0.5; +} + +/* 角落坐标十字 */ +.cp__cornerTL, +.cp__cornerBR { + position: absolute; + width: 28px; + height: 28px; + pointer-events: none; +} +.cp__cornerTL { top: 56px; left: 64px; } +.cp__cornerBR { bottom: 56px; right: 64px; } +.cp__cornerTL span, +.cp__cornerBR span { + position: absolute; + background: var(--fg-faint); +} +.cp__cornerTL span:nth-child(1), +.cp__cornerBR span:nth-child(1) { top: 0; left: 50%; width: 1px; height: 100%; transform: translateX(-50%); } +.cp__cornerTL span:nth-child(2), +.cp__cornerBR span:nth-child(2) { left: 0; top: 50%; width: 100%; height: 1px; transform: translateY(-50%); } + +/* ===================== Scene HERO ===================== */ + +.cp__hero { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 120px 140px; + text-align: center; +} + +.cp__hero-eyebrow { + display: flex; + align-items: center; + gap: 22px; + font-family: var(--f-mono); + font-size: 18px; + letter-spacing: 0.4em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 64px; +} +.cp__hero-eyebrow-bar { + width: 64px; + height: 1px; + background: var(--line-mid); +} + +.cp__hero-title { + font-family: var(--f-serif); + font-weight: 400; + font-size: 168px; + line-height: 1.02; + letter-spacing: -0.02em; + color: var(--fg); + margin: 0; + max-width: 1500px; +} +.cp__hero-em { + font-style: italic; + color: var(--accent); + position: relative; +} +.cp__hero-em::after { + content: ''; + position: absolute; + left: 4%; + right: 4%; + bottom: 8px; + height: 4px; + background: var(--accent); + opacity: 0.25; + transform-origin: left; + animation: cpUnderline 1100ms 320ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes cpUnderline { + from { transform: scaleX(0); opacity: 0; } + to { transform: scaleX(1); opacity: 0.25; } +} + +/* ===================== Scene SPLIT ===================== */ + +.cp__split { + position: absolute; + inset: 0; + padding: 110px 100px 100px; + display: flex; + flex-direction: column; + align-items: center; +} + +.cp__split-eyebrow { + display: flex; + align-items: center; + gap: 18px; + font-family: var(--f-mono); + font-size: 18px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 28px; +} +.cp__split-eyebrow-arrow { + color: var(--accent); + font-family: var(--f-sans); + letter-spacing: 0; +} + +/* 分隔线(中央) */ +.cp__split-divider { + position: absolute; + top: 220px; + bottom: 200px; + left: 50%; + transform: translateX(-50%); + display: flex; + flex-direction: column; + align-items: center; + pointer-events: none; +} +.cp__split-divider-line { + width: 1px; + flex: 1; + background: linear-gradient( + to bottom, + transparent, + var(--line-mid) 20%, + var(--line-mid) 80%, + transparent + ); +} +.cp__split-divider-knob { + width: 36px; + height: 36px; + border: 1px solid var(--line-strong); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-family: var(--f-serif); + font-size: 28px; + color: var(--accent); + background: var(--bg); + margin: 14px 0; +} + +/* 两栏 */ +.cp__columns { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 120px; + width: 100%; + max-width: 1640px; + margin-top: 24px; + flex: 1; +} + +.cp__col { + display: flex; + flex-direction: column; + padding: 8px 8px 0; +} +.cp__col--left { padding-right: 80px; } +.cp__col--right { padding-left: 80px; } + +.cp__col-pct { + display: flex; + align-items: baseline; + gap: 6px; + font-family: var(--f-serif); + font-weight: 400; + font-size: 200px; + line-height: 1; + color: var(--fg); + letter-spacing: -0.04em; + margin-bottom: 12px; +} +.cp__col--right .cp__col-pct { + color: var(--accent); +} +.cp__col-pct-sign { + font-family: var(--f-serif); + font-size: 100px; + color: var(--fg-mute); +} +.cp__col--right .cp__col-pct-sign { + color: var(--accent); + opacity: 0.7; +} + +.cp__col-kicker { + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.34em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 14px; +} + +.cp__col-title { + font-family: var(--f-serif); + font-weight: 400; + font-size: 86px; + line-height: 1; + margin: 0 0 22px; + color: var(--fg); +} + +.cp__col-desc { + font-family: var(--f-sans); + font-size: 26px; + line-height: 1.5; + color: var(--fg-soft); + margin: 0 0 28px; + max-width: 540px; +} + +/* 左:进度计 */ +.cp__col-meter { + position: relative; + height: 32px; + margin-bottom: 28px; + max-width: 540px; +} +.cp__col-meter-bar { + position: absolute; + inset: 0; + background: linear-gradient( + to right, + var(--accent), + var(--accent-deep) + ); + height: 4px; + top: 14px; + border-radius: 1px; + transition: width 1200ms cubic-bezier(.2,.8,.2,1) 600ms; +} +.cp__col-meter-ticks { + position: absolute; + inset: 0; + display: flex; + justify-content: space-between; + align-items: center; +} +.cp__col-meter-ticks span { + width: 1px; + height: 12px; + background: var(--line-mid); +} + +.cp__col-tags { + display: flex; + gap: 10px; + flex-wrap: wrap; +} +.cp__col-tags span { + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--fg-mute); + border: 1px solid var(--line-mid); + padding: 6px 12px; + border-radius: var(--r-pill); +} + +/* 右:文档预览 */ +.cp__doc { + margin-top: 4px; + max-width: 620px; + background: oklch(0.330 0.014 60); + border: 1px solid var(--line-mid); + border-radius: 4px; + overflow: hidden; + box-shadow: + 0 32px 60px -32px oklch(0 0 0 / 0.4), + inset 0 1px 0 oklch(0.965 0.018 78 / 0.04); +} +.cp__doc-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 14px; + background: oklch(0.380 0.014 60); + border-bottom: 1px solid var(--line-mid); + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.06em; + color: var(--fg-mute); +} +.cp__doc-bar-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: oklch(0.420 0.020 60); +} +.cp__doc-bar-name { + margin-left: 8px; +} + +.cp__doc-body { + padding: 14px 18px 22px; + min-height: 280px; + font-family: var(--f-mono); + font-size: 15px; + line-height: 1.65; + color: var(--fg-soft); + position: relative; +} +.cp__doc-line { + display: flex; + gap: 16px; + white-space: pre; + align-items: baseline; +} +.cp__doc-line-no { + width: 22px; + flex: none; + color: var(--fg-faint); + text-align: right; +} +.cp__doc-line-text { + color: var(--fg-soft); +} +.cp__doc-cursor { + display: inline-block; + margin-top: 6px; + margin-left: 38px; + color: var(--accent); + animation: cpCursor 1.1s steps(1) infinite; +} +@keyframes cpCursor { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0; } +} + +/* leaked 徽章 */ +.cp__leaked { + position: absolute; + bottom: 130px; + left: 50%; + transform: translateX(-50%); + display: flex; + align-items: center; + gap: 28px; + padding: 20px 32px; + background: oklch(0.340 0.014 60); + border: 1px solid var(--line-strong); + border-radius: 4px; + box-shadow: 0 24px 48px -20px oklch(0 0 0 / 0.45); + max-width: 920px; +} +.cp__leaked-stamp { + display: flex; + align-items: center; + gap: 10px; + font-family: var(--f-mono); + font-size: 22px; + letter-spacing: 0.32em; + color: var(--crimson); + font-weight: 600; + border: 2px solid var(--crimson); + padding: 10px 16px 8px; + border-radius: 2px; + transform: rotate(-3deg); +} +.cp__leaked-stamp-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--crimson); + box-shadow: 0 0 8px var(--crimson); + animation: cpStampPulse 1.2s ease-in-out infinite; +} +@keyframes cpStampPulse { + 0%, 100% { opacity: 0.5; } + 50% { opacity: 1; } +} +.cp__leaked-meta { + display: flex; + align-items: center; + gap: 28px; +} +.cp__leaked-meta-time { + display: flex; + align-items: baseline; + gap: 6px; + font-family: var(--f-serif); + font-size: 72px; + line-height: 1; + color: var(--fg); +} +.cp__leaked-meta-lt { + font-family: var(--f-serif); + font-size: 56px; + color: var(--fg-mute); + margin-right: 4px; +} +.cp__leaked-meta-unit { + font-family: var(--f-mono); + font-size: 18px; + letter-spacing: 0.28em; + color: var(--fg-mute); + margin-left: 6px; +} +.cp__leaked-meta-text { + font-family: var(--f-sans); + font-size: 22px; + line-height: 1.45; + color: var(--fg-soft); +} + +/* pivot 转场指引 */ +.cp__pivot { + position: absolute; + bottom: 56px; + left: 50%; + transform: translateX(-50%); + display: flex; + align-items: center; + gap: 14px; + font-family: var(--f-mono); + font-size: 18px; + letter-spacing: 0.28em; + text-transform: uppercase; + color: var(--accent); +} +.cp__pivot-arrow { + width: 56px; + height: 1px; + background: var(--accent); + position: relative; +} +.cp__pivot-arrow::after { + content: ''; + position: absolute; + right: -2px; + top: 50%; + width: 8px; + height: 8px; + border-right: 1px solid var(--accent); + border-bottom: 1px solid var(--accent); + transform: translateY(-50%) rotate(-45deg); +} +.cp__pivot-text { + animation: cpPivotBlink 2.4s ease-in-out infinite; +} +@keyframes cpPivotBlink { + 0%, 100% { opacity: 0.65; } + 50% { opacity: 1; } +} diff --git a/web/src/chapters/03-core-point/index.tsx b/web/src/chapters/03-core-point/index.tsx new file mode 100644 index 0000000..e057234 --- /dev/null +++ b/web/src/chapters/03-core-point/index.tsx @@ -0,0 +1,204 @@ +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import { SceneFade } from '../../shared/SceneFade'; +import { NumberTicker } from '../../shared/NumberTicker'; +import './CorePoint.css'; + +/** + * Chapter 03 · 核心观点 + * + * 口播主旨: + * 「Claude Design 之所以强,一半靠 Opus 4.7,另一半靠精心设计的提示词。 + * 上线不到 24 小时,完整系统提示词被扒出来。下面,我们逐条拆解。」 + * + * 节奏(6 步 / step 0..5): + * 0 环境(深墨底 + 网格氛围,一行小标 eyebrow) + * 1 hero 问句:"Claude Design · 为什么这么强?" + * 2 答案展开:50/50 分屏,左 OPUS 4.7 / 右 SYSTEM PROMPT + * 3 右侧"提示词从天而降"——真实片段逐条落入文档预览 + * 4 leaked 事件徽章:"< 24 HOURS · LEAKED" + * 5 转场指引:"下面,我们逐条拆解 ↓" + */ + +const PROMPT_LINES: string[] = [ + 'You are an expert designer working with the user as a manager.', + 'You produce design artifacts on behalf of the user using HTML.', + 'HTML is your tool, but your medium and output format vary.', + 'You must embody an expert in that domain:', + ' animator, UX designer, slide designer, prototyper, etc.', + 'Avoid web design tropes and conventions', + ' unless you are making a web page.', + '## Your workflow', + '1. Understand user needs. Ask clarifying questions ...', + '2. Explore provided resources. Read the design system ...', +]; + +function CorePoint({ localStep }: ChapterContext) { + const at = (n: number) => localStep >= n; + + // 场景:1+ 进入分析;2 之前是单独 hero 居中 + const sceneHero = localStep <= 1; + const sceneSplit = localStep >= 2; + + return ( +
+ {/* 装饰性背景网格 + 角落坐标 */} +
+
+ +
+
+ +
+ + {/* ───────── Scene HERO(step 0..1)───────── */} + +
+ + + 03 · 核心观点 + + + + {at(1) && ( + + Claude Design
+ 为什么这么强? +
+ )} +
+
+ + {/* ───────── Scene SPLIT(step 2..5)───────── */} + +
+ {/* 顶部留下问句的小回响(不再是大字) */} + + 为什么这么强? + + 答案是 + + + {/* 中央分隔线 */} + + + + + + + +
+ {/* —— 左:OPUS 4.7 —— */} + +
+ + % +
+
MODEL
+

Opus 4.7

+

+ Anthropic 当前旗舰模型 ——
+ 决策力、品味、长链推理与代码能力的综合上限 +

+ +
+
+
+ +
+
+ +
+ reasoning + taste + code +
+ + + {/* —— 右:SYSTEM PROMPT —— */} + +
+ + % +
+
SYSTEM PROMPT
+

提示词工程

+

+ ~420 行专家级 system prompt ——
+ 对模型的"角色 / 流程 / 边界 / 品味"做了极强约束 +

+ + {/* 文档预览 */} +
+
+ + + + claude-design.system.md +
+
+ {at(3) && PROMPT_LINES.map((line, i) => ( + + {String(i + 1).padStart(2, '0')} + {line} + + ))} + {at(3) && ( + + + + )} +
+
+
+
+ + {/* leaked 徽章 */} + {at(4) && ( + +
+ + LEAKED +
+
+
+ < + + HOURS +
+
+ 上线不到 24 小时,完整系统提示词被扒出,
+ 在安全 / 提示词圈广为流传 +
+
+
+ )} + + {/* 转场指引 */} + {at(5) && ( + + + 下面,我们逐条拆解这套提示词 + + )} +
+ +
+ ); +} + +const def: ChapterDef = { + id: 'core-point', + title: '核心观点', + eyebrow: '03', + steps: 6, + theme: 'ink', + Component: CorePoint, +}; + +export default def; diff --git a/web/src/chapters/04-role/Role.css b/web/src/chapters/04-role/Role.css new file mode 100644 index 0000000..698d6cc --- /dev/null +++ b/web/src/chapters/04-role/Role.css @@ -0,0 +1,641 @@ +/* ========================================================= + Chapter 04 · 角色定位 + light 主题 · 引文 → 高亮 → benefit → 名片翻 → 全卡 + 收尾 + ========================================================= */ + +.role { + position: absolute; + inset: 0; + background: var(--bg); + color: var(--fg); + font-family: var(--f-sans); + overflow: hidden; +} + +/* 顶部 eyebrow 贯穿全章节 */ +.role__eyebrow { + position: absolute; + top: 56px; + left: 96px; + display: flex; + align-items: center; + gap: 18px; + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-mute); + z-index: 4; +} +.role__eyebrow-num { + font-family: var(--f-serif); + font-size: 28px; + letter-spacing: 0; + color: var(--accent); + font-style: italic; +} +.role__eyebrow-bar { + width: 36px; + height: 1px; + background: var(--line-mid); +} +.role__eyebrow-bar--long { width: 96px; } +.role__eyebrow-text { color: var(--fg); letter-spacing: 0.18em; } +.role__eyebrow-mute { color: var(--fg-faint); font-size: 13px; } + +/* ===================== Scene QUOTE ===================== */ + +.role__quote-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 140px 140px 120px; + text-align: center; +} + +/* ===== prompt 源标签(mono 小字) ===== */ +.role__src { + display: inline-flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 28px; + padding: 6px 12px; + border: 1px solid var(--line-mid); + border-radius: 2px; + background: var(--bg-2); +} +.role__src-bracket { + color: var(--accent); + font-weight: 600; +} +.role__src-label { + color: var(--fg); + font-weight: 600; + letter-spacing: 0.22em; +} +.role__src-sep { + color: var(--fg-faint); +} +.role__src-line { + color: var(--accent); + font-weight: 600; +} +.role__src-mute { + color: var(--fg-mute); +} + +.role__quote-en { + font-family: var(--f-serif); + font-style: italic; + font-weight: 400; + font-size: 96px; + line-height: 1.18; + color: var(--fg); + margin: 0 0 48px; + max-width: 1500px; + letter-spacing: -0.01em; +} + +.role__quote-marks { + font-family: var(--f-serif); + font-style: italic; + color: var(--fg-faint); + font-size: 110px; + line-height: 0; + vertical-align: -8px; + margin: 0 4px; +} + +.role__quote-keyword { + position: relative; + font-style: italic; + color: var(--fg); + display: inline-block; + padding: 0 4px; + transition: color 360ms var(--ease-enter); +} +.role__quote-keyword.is-marked { + color: var(--accent); +} +.role__quote-keyword .role__quote-mark { + position: absolute; + left: -10%; + right: -10%; + bottom: 18px; + height: 3px; + background: var(--accent); + transform: scaleX(0); + transform-origin: left; + opacity: 0; +} +.role__quote-keyword.is-marked .role__quote-mark { + animation: roleUnderline 720ms cubic-bezier(.2,.8,.2,1) forwards; +} +.role__quote-keyword--alt.is-marked .role__quote-mark { + background: var(--accent-deep); + animation-delay: 220ms; +} +@keyframes roleUnderline { + from { transform: scaleX(0); opacity: 0; } + to { transform: scaleX(1); opacity: 0.7; } +} + +.role__quote-cn { + font-family: var(--f-serif); + font-weight: 400; + font-size: 38px; + line-height: 1.6; + color: var(--fg-soft); + margin: 0 0 36px; + letter-spacing: 0.04em; +} +.role__quote-cn-key { + position: relative; + padding: 0 6px; + transition: color 360ms; +} +.role__quote-cn-key.is-marked { + color: var(--accent); + background: linear-gradient(180deg, transparent 60%, oklch(0.860 0.060 60 / 0.6) 60%); +} +.role__quote-cn-key--alt.is-marked { + color: var(--accent-deep); + background: linear-gradient(180deg, transparent 60%, oklch(0.860 0.060 60 / 0.6) 60%); +} + +.role__quote-note { + font-family: var(--f-mono); + font-size: 18px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 56px; +} +.role__quote-note u { + color: var(--crimson); + text-decoration-thickness: 2px; + text-underline-offset: 4px; +} + +.role__benefits { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 48px; + width: 100%; + max-width: 1280px; + margin-top: 8px; +} + +.role__benefit { + position: relative; + padding: 28px 32px 32px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 6px; + text-align: left; + box-shadow: 0 24px 40px -28px oklch(0.180 0.014 60 / 0.4); +} + +.role__benefit-num { + position: absolute; + top: -22px; + left: 28px; + width: 44px; + height: 44px; + background: var(--accent); + color: var(--paper); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-family: var(--f-serif); + font-size: 28px; + font-style: italic; +} + +.role__benefit-title { + font-family: var(--f-serif); + font-size: 44px; + line-height: 1.1; + color: var(--fg); + margin: 8px 0 14px; +} +.role__benefit-desc { + font-family: var(--f-sans); + font-size: 22px; + line-height: 1.55; + color: var(--fg-soft); +} + +/* ===================== Scene FLIP ===================== */ + +.role__flip-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 140px 140px 80px; + gap: 32px; +} + +/* ===== 原文引用块(prompt 风) ===== */ +.role__excerpt { + width: 100%; + max-width: 1280px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-left: 3px solid var(--accent); + border-radius: 2px; + padding: 22px 32px 26px; + text-align: left; + box-shadow: 0 18px 32px -22px oklch(0.180 0.014 60 / 0.30); +} +.role__excerpt-head { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 14px; + padding-bottom: 12px; + border-bottom: 1px dashed var(--line-mid); +} +.role__excerpt-body { + display: flex; + align-items: flex-start; + gap: 18px; +} +.role__excerpt-gt { + font-family: var(--f-mono); + font-size: 38px; + line-height: 1.2; + color: var(--accent); + flex: none; +} +.role__excerpt-text { + font-family: var(--f-mono); + font-size: 26px; + line-height: 1.55; + color: var(--fg); + flex: 1; +} +.role__excerpt-em { + font-style: normal; + color: var(--accent); + background: linear-gradient(180deg, transparent 70%, oklch(0.860 0.060 60 / 0.5) 70%); + padding: 0 2px; +} +.role__excerpt-list { + font-style: italic; + color: var(--accent-deep); +} + +.role__flip-eyebrow { + display: flex; + align-items: baseline; + gap: 16px; + font-family: var(--f-serif); + font-size: 32px; + color: var(--fg-soft); + margin: 4px 0 0; +} +.role__flip-eyebrow-arrow { + color: var(--fg-mute); + font-family: var(--f-sans); + font-size: 28px; +} +.role__flip-eyebrow-em { + font-style: italic; + color: var(--accent); + font-size: 42px; + position: relative; +} +.role__flip-eyebrow-em::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 4px; + height: 3px; + background: var(--accent); + opacity: 0.3; + transform-origin: left; + animation: roleUnderline 800ms 220ms cubic-bezier(.2,.8,.2,1) backwards; +} + +/* 翻牌窗:3 张卡片层叠,translateY 控制可见状态 */ +.role__flip-window { + position: relative; + width: 720px; + height: 380px; + perspective: 1600px; +} + +.role__flip-slot { + position: absolute; + inset: 0; + transition: + transform 760ms cubic-bezier(.2,.8,.2,1), + opacity 600ms cubic-bezier(.2,.8,.2,1), + filter 600ms; + transform-origin: 50% 50% -200px; + will-change: transform, opacity; +} +.role__flip-slot[data-state="prev"] { + transform: translateY(-30%) rotateX(50deg); + opacity: 0; + pointer-events: none; + filter: blur(4px); +} +.role__flip-slot[data-state="current"] { + transform: translateY(0) rotateX(0deg); + opacity: 1; + filter: blur(0); +} +.role__flip-slot[data-state="next"] { + transform: translateY(30%) rotateX(-50deg); + opacity: 0; + pointer-events: none; + filter: blur(4px); +} + +/* 进度刻度(步进) */ +.role__flip-ticks { + display: flex; + gap: 64px; + margin-top: 8px; +} +.role__flip-tick { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + opacity: 0.32; + transition: opacity 380ms, transform 380ms; +} +.role__flip-tick.is-active { + opacity: 1; + transform: translateY(-2px); +} +.role__flip-tick-num { + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.32em; + color: var(--fg-mute); +} +.role__flip-tick.is-active .role__flip-tick-num { + color: var(--accent); +} +.role__flip-tick-name { + font-family: var(--f-serif); + font-size: 22px; + color: var(--fg); +} +.role__flip-tick.is-active .role__flip-tick-name { + color: var(--accent); + font-style: italic; +} + +/* ===================== Card 视觉 ===================== */ + +.role__card { + position: relative; + width: 100%; + height: 100%; + background: var(--paper); + border: 1px solid var(--line-mid); + box-shadow: + 0 30px 50px -30px oklch(0.180 0.014 60 / 0.35), + 0 12px 18px -10px oklch(0.180 0.014 60 / 0.18), + inset 0 1px 0 oklch(0.965 0.018 78 / 1); + display: flex; + flex-direction: column; + overflow: hidden; +} +.role__card::before { + /* 纸感纹理 */ + content: ''; + position: absolute; + inset: 0; + background-image: + radial-gradient(circle at 20% 20%, oklch(0.180 0.014 60 / 0.04) 0, transparent 50%), + radial-gradient(circle at 80% 80%, oklch(0.180 0.014 60 / 0.04) 0, transparent 50%); + pointer-events: none; +} +.role__card--lg { padding: 32px 44px; } +.role__card--sm { padding: 22px 26px; } + +.role__card-strip { + display: flex; + align-items: center; + gap: 12px; + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.28em; + text-transform: uppercase; + color: var(--fg-mute); + padding-bottom: 18px; + border-bottom: 1px dashed var(--line-mid); +} +.role__card--sm .role__card-strip { + font-size: 11px; + padding-bottom: 12px; +} +.role__card-strip-dot { + width: 4px; + height: 4px; + border-radius: 50%; + background: var(--fg-faint); +} + +.role__card-body { + flex: 1; + display: grid; + grid-template-columns: auto 1fr; + gap: 28px; + align-items: center; + padding: 22px 0; +} + +.role__card-meta { + width: 110px; + height: 110px; + border-radius: 6px; + background: oklch(0.928 0.024 78); + display: flex; + align-items: center; + justify-content: center; + color: var(--accent); +} +.role__card--sm .role__card-meta { + width: 64px; + height: 64px; +} +.role__icon { + width: 60px; + height: 60px; +} +.role__card--sm .role__icon { + width: 36px; + height: 36px; +} + +.role__card-text { + display: flex; + flex-direction: column; + gap: 8px; +} +.role__card-en { + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + color: var(--fg-mute); + line-height: 1; +} +.role__card--sm .role__card-en { + font-size: 22px; +} +.role__card-cn { + font-family: var(--f-serif); + font-size: 64px; + line-height: 1; + color: var(--fg); + letter-spacing: 0.02em; +} +.role__card--sm .role__card-cn { + font-size: 36px; +} + +.role__card-foot { + display: flex; + align-items: baseline; + gap: 12px; + padding-top: 18px; + border-top: 1px solid var(--line-mid); +} +.role__card--sm .role__card-foot { + padding-top: 12px; +} +.role__card-ctx { + font-family: var(--f-serif); + font-style: italic; + font-size: 28px; + color: var(--accent); +} +.role__card--sm .role__card-ctx { + font-size: 18px; +} +.role__card-ctx-en { + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.22em; + text-transform: uppercase; + color: var(--fg-faint); +} +.role__card--sm .role__card-ctx-en { + font-size: 11px; +} + +/* 角注 */ +.role__card-corner { + position: absolute; + width: 12px; + height: 12px; + border: 1px solid var(--fg-faint); +} +.role__card-corner--tl { top: 8px; left: 8px; border-right: 0; border-bottom: 0; } +.role__card-corner--tr { top: 8px; right: 8px; border-left: 0; border-bottom: 0; } +.role__card-corner--bl { bottom: 8px; left: 8px; border-right: 0; border-top: 0; } +.role__card-corner--br { bottom: 8px; right: 8px; border-left: 0; border-top: 0; } + +/* ===================== Scene ALL ===================== */ + +.role__all-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 140px 100px 120px; +} + +.role__all-row { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 36px; + width: 100%; + max-width: 1480px; + margin-bottom: 64px; +} + +.role__all-slot { + height: 280px; + animation: roleAllRise 720ms cubic-bezier(.2,.8,.2,1) backwards; +} +.role__all-slot:nth-child(1) { transform: rotate(-1.5deg); } +.role__all-slot:nth-child(2) { transform: rotate(0.5deg); } +.role__all-slot:nth-child(3) { transform: rotate(-0.8deg); } +@keyframes roleAllRise { + from { opacity: 0; transform: translateY(28px) rotate(-3deg); } + to { opacity: 1; } +} + +.role__all-takeaway { + font-family: var(--f-serif); + font-weight: 400; + font-size: 88px; + line-height: 1.15; + text-align: center; + margin: 0 0 28px; + color: var(--fg); + max-width: 1400px; +} +.role__all-takeaway em { + font-style: italic; + color: var(--fg-soft); +} +.role__all-em { + color: var(--accent); + position: relative; +} +.role__all-em::after { + content: ''; + position: absolute; + left: -4%; + right: -4%; + bottom: 6px; + height: 8px; + background: var(--accent); + opacity: 0.22; +} + +.role__all-foot { + display: flex; + align-items: center; + gap: 16px; + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.22em; + text-transform: uppercase; + color: var(--fg-mute); +} +.role__all-foot-dot { + width: 4px; + height: 4px; + border-radius: 50%; + background: var(--fg-faint); +} diff --git a/web/src/chapters/04-role/index.tsx b/web/src/chapters/04-role/index.tsx new file mode 100644 index 0000000..9ec882d --- /dev/null +++ b/web/src/chapters/04-role/index.tsx @@ -0,0 +1,297 @@ +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import { SceneFade } from '../../shared/SceneFade'; +import './Role.css'; + +/** + * Chapter 04 · 第一部分:角色定位 + * + * 口播主旨: + * "你是一个专家设计师,用户是你的产品经理。" + * - 不说 "AI 助手",说 designer / manager 关系 → 决策果断 + 关键征询 + * - 而且角色还会变:动画时 = Motion Designer;原型时 = UX Designer;幻灯时 = Deck Designer + * - 好的角色定位 = 动态的 + * + * 节奏(8 步 / step 0..7): + * 0 环境(eyebrow) + * 1 hero 引文(英文 italic + 中文) + * 2 关键词高亮(designer / manager 圈出) + * 3 双 benefit 卡片(果断 / 征询) + * 4 名片切到 Motion Designer + * 5 → UX Designer + * 6 → Deck Designer + * 7 收尾:三张名片并列 + 结论"好的角色定位 · 是动态的" + */ + +interface Role { + id: string; + en: string; + cn: string; + ctx: string; + ctxEn: string; + icon: 'motion' | 'ux' | 'deck'; +} + +const ROLES: Role[] = [ + { id: 'motion', en: 'Motion Designer', cn: '动效设计师', ctx: '做动画时', ctxEn: 'when animating', icon: 'motion' }, + { id: 'ux', en: 'UX Designer', cn: 'UX 设计师', ctx: '做原型时', ctxEn: 'when prototyping', icon: 'ux' }, + { id: 'deck', en: 'Deck Designer', cn: 'Deck 设计师', ctx: '做幻灯片时', ctxEn: 'when decking', icon: 'deck' }, +]; + +function RoleIcon({ kind }: { kind: Role['icon'] }) { + switch (kind) { + case 'motion': + return ( + + + + + + + ); + case 'ux': + return ( + + + + + + + ); + case 'deck': + return ( + + + + + + ); + } +} + +function RoleCard({ role, size = 'lg' }: { role: Role; size?: 'lg' | 'sm' }) { + return ( +
+
+ ROLE / {role.id.toUpperCase()} + + WHEN ACTIVE +
+ +
+
+ +
+
+
{role.en}
+
{role.cn}
+
+
+ +
+ {role.ctx} + {role.ctxEn} +
+ + + + + +
+ ); +} + +function Role({ localStep }: ChapterContext) { + const at = (n: number) => localStep >= n; + + // 三幕:引文 / 单卡片切换 / 收尾全卡 + const sceneQuote = localStep <= 3; + const sceneFlip = localStep >= 4 && localStep <= 6; + const sceneAll = localStep >= 7; + + const roleIndex = Math.min(2, Math.max(0, localStep - 4)); + + return ( +
+ {/* ═════════ Scene QUOTE(step 0..3)═════════ */} + +
+ {at(1) && ( + + [ + SYSTEM PROMPT + · + L01 + / + 原文 + ] + + )} + + {at(1) && ( + + " + You are an expert{' '} + + designer + + + , working with the user as a{' '} + + manager + + + . + " + + )} + + {at(1) && ( + + 你是一个专家 + 设计师 + —— 而用户,是你的 + 产品经理 + + + )} + + {at(2) && ( + + 注意 —— 这里 没有 说 "你是一个 AI 助手" + + )} + + {at(3) && ( +
+ +
A
+
决策更果断
+
+ 设计师本就该有判断力 —— AI 不再事事征询,
+ 能直接拍板的,自己拍。 +
+
+ + +
B
+
关键节点 · 请示你
+
+ 因为你是 PM —— 在方向 / 取舍 / 命名这种
+ 关键节点上,最终还是你说了算。 +
+
+
+ )} +
+
+ + {/* ═════════ Scene FLIP(step 4..6)═════════ */} + +
+ {/* 原文参考块 —— 来自系统提示词 L04 */} + +
+ [ + SYSTEM PROMPT + · + L04 + / + 紧接下一句 + ] +
+
+ > + + HTML is your tool, but your{' '} + medium and output format vary.
+ You must embody an expert in that domain:{' '} + + animator, UX designer, slide designer, prototyper, etc. + +
+
+
+ + + + 翻成大白话 —— + 角色,还会变 + + + {/* 名片"翻牌"窗 */} +
+ {ROLES.map((r, i) => { + const state = i === roleIndex + ? 'current' + : i < roleIndex ? 'prev' : 'next'; + return ( +
+ +
+ ); + })} +
+ + {/* 步进刻度 */} +
+ {ROLES.map((r, i) => ( +
+ 0{i + 1} + {r.cn} +
+ ))} +
+
+
+ + {/* ═════════ Scene ALL(step 7)═════════ */} + +
+ + {ROLES.map((r, i) => ( +
+ +
+ ))} +
+ + + 好的角色定位 ——
+ 不是固定的,而是 动态 的。 +
+ + + 不会用做网页的脑子做 PPT + + 不会给动画加页脚 + + 不会给幻灯片加导航栏 + +
+
+
+ ); +} + +const def: ChapterDef = { + id: 'role', + title: '第一部分 · 角色定位', + eyebrow: '04', + steps: 8, + theme: 'light', + Component: Role, +}; + +export default def; diff --git a/web/src/chapters/05-workflow/Workflow.css b/web/src/chapters/05-workflow/Workflow.css new file mode 100644 index 0000000..96fe28b --- /dev/null +++ b/web/src/chapters/05-workflow/Workflow.css @@ -0,0 +1,633 @@ +/* ========================================================= + Chapter 05 · 第二部分 · 工作流 + ink 主题 · 流水线 → 问 vs 干 → 极简总结 + ========================================================= */ + +.wf { + position: absolute; + inset: 0; + background: var(--bg); + color: var(--fg); + font-family: var(--f-sans); + overflow: hidden; +} + +/* —— 顶部 eyebrow —— */ +.wf__eyebrow { + position: absolute; + top: 56px; + left: 96px; + display: flex; + align-items: center; + gap: 18px; + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-mute); + z-index: 4; +} +.wf__eyebrow-num { + font-family: var(--f-serif); + font-size: 28px; + letter-spacing: 0; + color: var(--accent); + font-style: italic; +} +.wf__eyebrow-bar { + width: 36px; + height: 1px; + background: var(--line-mid); +} +.wf__eyebrow-bar--long { width: 96px; } +.wf__eyebrow-text { color: var(--fg); letter-spacing: 0.18em; } +.wf__eyebrow-mute { color: var(--fg-faint); font-size: 13px; } + +/* —— 通用 prompt 源标签子部件 —— */ +.wf__src-bracket { color: var(--accent); font-weight: 600; } +.wf__src-label { color: var(--fg); font-weight: 600; letter-spacing: 0.22em; } +.wf__src-sep { color: var(--fg-faint); } +.wf__src-line { color: var(--accent); font-weight: 600; } +.wf__src-mute { color: var(--fg-mute); } + +/* ===================== Scene PIPELINE ===================== */ + +.wf__pipe-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 140px 100px 100px; + gap: 80px; +} + +/* 原文引用块(与 ch04 同款) */ +.wf__excerpt { + width: 100%; + max-width: 1280px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-left: 3px solid var(--accent); + border-radius: 2px; + padding: 22px 32px 26px; + text-align: left; + box-shadow: 0 18px 32px -22px oklch(0 0 0 / 0.5); +} +.wf__excerpt-head { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 14px; + padding-bottom: 12px; + border-bottom: 1px dashed var(--line-mid); +} +.wf__excerpt-body { + display: flex; + flex-direction: column; + gap: 8px; +} +.wf__excerpt-title { + font-family: var(--f-mono); + font-size: 22px; + color: var(--accent); + margin-bottom: 6px; +} +.wf__excerpt-list { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 10px 36px; + font-family: var(--f-mono); + font-size: 19px; + line-height: 1.45; + color: var(--fg-soft); +} +.wf__excerpt-list b { + color: var(--accent); + font-weight: 600; + margin-right: 6px; +} +.wf__excerpt-list code { + background: oklch(0.380 0.014 60); + padding: 0 6px; + border-radius: 2px; + color: var(--accent); +} +.wf__excerpt-list em { + font-style: italic; + color: var(--accent); + background: linear-gradient(180deg, transparent 70%, oklch(0.700 0.170 42 / 0.25) 70%); + padding: 0 2px; +} + +/* —— 流水线 —— */ +.wf__pipeline { + position: relative; + width: 100%; + max-width: 1480px; + padding: 0 60px; +} + +.wf__line { + position: absolute; + top: 50%; + left: 60px; + right: 60px; + height: 2px; + background: var(--line-mid); + border-radius: 1px; + transform: translateY(-50%); + overflow: hidden; +} +.wf__line-fill { + height: 100%; + background: linear-gradient(to right, var(--accent), var(--accent-deep)); + transition: width 1100ms cubic-bezier(.2,.8,.2,1); + box-shadow: 0 0 12px var(--accent); +} + +.wf__stations { + position: relative; + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 24px; +} + +.wf__station { + display: flex; + flex-direction: column; + align-items: center; + gap: 22px; + position: relative; + transition: filter 540ms; + filter: grayscale(0.6) brightness(0.6); +} +.wf__station.is-lit { + filter: grayscale(0) brightness(1); +} + +.wf__station-cn { + font-family: var(--f-serif); + font-size: 30px; + color: var(--fg); + height: 36px; +} +.wf__station-en { + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.28em; + text-transform: uppercase; + color: var(--fg-mute); +} +.wf__station.is-lit .wf__station-en { + color: var(--accent); +} + +.wf__station-node { + position: relative; + width: 56px; + height: 56px; + border-radius: 50%; + background: var(--bg); + border: 2px solid var(--line-mid); + display: flex; + align-items: center; + justify-content: center; + font-family: var(--f-serif); + font-size: 28px; + font-style: italic; + color: var(--fg-mute); + z-index: 1; + transition: border-color 540ms, background 540ms, color 540ms, transform 540ms; +} +.wf__station.is-lit .wf__station-node { + background: var(--accent); + border-color: var(--accent); + color: var(--paper); + transform: scale(1.08); + box-shadow: 0 0 0 6px oklch(0.700 0.170 42 / 0.15); +} +.wf__station-no { + z-index: 2; +} +.wf__station-pulse { + position: absolute; + inset: -6px; + border-radius: 50%; + border: 1px solid var(--accent); + opacity: 0; + pointer-events: none; +} +.wf__station.is-lit .wf__station-pulse { + animation: wfPulse 1.6s ease-out infinite; +} +@keyframes wfPulse { + 0% { transform: scale(0.9); opacity: 0.7; } + 100% { transform: scale(1.6); opacity: 0; } +} + +/* ===================== Scene DECIDE ===================== */ + +.wf__decide-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 140px 100px 80px; +} + +.wf__decide-head { + display: flex; + flex-direction: column; + align-items: center; + gap: 14px; + margin-bottom: 56px; + text-align: center; +} +.wf__decide-num { + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--accent); +} +.wf__decide-title { + font-family: var(--f-serif); + font-weight: 400; + font-size: 76px; + line-height: 1.15; + color: var(--fg); + margin: 0; +} +.wf__decide-title em { + font-style: italic; + color: var(--accent); +} +.wf__decide-do { + position: relative; +} +.wf__decide-do::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 4px; + height: 4px; + background: var(--accent); + opacity: 0.3; + transform-origin: left; + animation: wfUnderline 800ms 320ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes wfUnderline { + from { transform: scaleX(0); opacity: 0; } + to { transform: scaleX(1); opacity: 0.3; } +} +.wf__decide-rule { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 6px 14px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.24em; + text-transform: uppercase; + color: var(--fg-mute); + border: 1px solid var(--line-mid); + border-radius: 2px; + background: var(--bg-2); +} +.wf__decide-rule-text { + color: var(--fg); + letter-spacing: 0.16em; +} + +/* —— 决策对比(两栏 + 中央 vs) —— */ +.wf__decide-grid { + position: relative; + display: grid; + grid-template-columns: 1fr 80px 1fr; + gap: 24px; + width: 100%; + max-width: 1500px; + align-items: stretch; +} + +.wf__chat { + display: flex; + flex-direction: column; + gap: 16px; + padding: 28px 28px 20px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + position: relative; +} +.wf__chat--ask { border-top: 3px solid var(--crimson); } +.wf__chat--do { border-top: 3px solid var(--accent); } + +.wf__chat-tag { + display: flex; + align-items: center; + gap: 10px; + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.24em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 4px; +} +.wf__chat-tag-dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--crimson); + box-shadow: 0 0 6px var(--crimson); + animation: wfPulse 1.6s ease-out infinite; +} +.wf__chat-tag-dot--do { + background: var(--accent); + box-shadow: 0 0 6px var(--accent); +} + +.wf__bubble { + padding: 16px 20px; + border-radius: 12px; + position: relative; + font-family: var(--f-sans); + font-size: 22px; + line-height: 1.45; +} +.wf__bubble p { margin: 0; } +.wf__bubble-meta { + display: block; + font-family: var(--f-mono); + font-size: 11px; + letter-spacing: 0.28em; + text-transform: uppercase; + color: var(--fg-faint); + margin-bottom: 6px; +} +.wf__bubble--user { + background: oklch(0.380 0.014 60); + border: 1px solid var(--line-mid); + border-bottom-left-radius: 2px; + align-self: flex-start; + max-width: 84%; +} +.wf__bubble--ai { + background: oklch(0.355 0.018 60); + border: 1px solid oklch(0.700 0.170 42 / 0.4); + border-bottom-right-radius: 2px; + align-self: flex-end; + max-width: 92%; + box-shadow: 0 8px 16px -8px oklch(0.700 0.170 42 / 0.25); +} + +.wf__qmarks { + position: absolute; + top: -22px; + right: 20px; + display: flex; + gap: 8px; + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + color: var(--crimson); + pointer-events: none; +} +.wf__qmarks span { + animation: wfQ 2.4s ease-in-out infinite; + opacity: 0; +} +@keyframes wfQ { + 0%, 100% { opacity: 0; transform: translateY(8px); } + 20%, 70% { opacity: 0.9; transform: translateY(0); } +} + +.wf__action { + margin-top: 14px; + display: flex; + flex-direction: column; + gap: 6px; +} +.wf__action-bar { + height: 6px; + background: linear-gradient(to right, var(--accent) 0%, var(--accent-deep) 100%); + border-radius: 1px; + transform-origin: left; + animation: wfBar 1.2s cubic-bezier(.2,.8,.2,1) infinite; +} +.wf__action-bar:nth-child(1) { width: 70%; animation-delay: 0ms; } +.wf__action-bar:nth-child(2) { width: 90%; animation-delay: 200ms; } +.wf__action-bar:nth-child(3) { width: 50%; animation-delay: 400ms; } +@keyframes wfBar { + 0% { transform: scaleX(0); opacity: 0.4; } + 60% { transform: scaleX(1); opacity: 1; } + 100% { transform: scaleX(1); opacity: 0.4; } +} + +.wf__chat-verdict { + margin-top: auto; + padding: 12px 14px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.22em; + text-transform: uppercase; + text-align: center; + border: 1px dashed var(--line-mid); + border-radius: 2px; +} +.wf__chat-verdict--ask { color: var(--crimson); border-color: var(--crimson); } +.wf__chat-verdict--do { color: var(--accent); border-color: var(--accent); } + +.wf__decide-vs { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + pointer-events: none; +} +.wf__decide-vs-line { + width: 1px; + flex: 1; + background: var(--line-mid); +} +.wf__decide-vs-knob { + width: 44px; + height: 44px; + border: 1px solid var(--line-strong); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-family: var(--f-serif); + font-style: italic; + font-size: 22px; + color: var(--fg-mute); + background: var(--bg); +} + +/* ===================== Scene SUMMARY ===================== */ + +.wf__sum-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 140px 100px 80px; + gap: 28px; +} + +.wf__sum-num { + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--accent); +} + +.wf__sum-hero { + font-family: var(--f-serif); + font-weight: 400; + font-size: 88px; + line-height: 1.15; + text-align: center; + margin: 0; + color: var(--fg); + display: flex; + flex-direction: column; + gap: 6px; +} +.wf__sum-hero-en em { + font-style: italic; + color: var(--accent); +} +.wf__sum-hero-cn { + font-size: 56px; + color: var(--fg-soft); +} +.wf__sum-hero-cn em { + font-style: italic; + color: var(--accent); +} + +.wf__sum-source { + display: inline-flex; + align-items: center; + gap: 10px; + padding: 6px 14px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-mute); + border: 1px solid var(--line-mid); + border-radius: 2px; + background: var(--bg-2); +} +.wf__sum-source-quote { + font-family: var(--f-mono); + letter-spacing: 0.04em; + text-transform: none; + color: var(--fg); + margin-left: 6px; +} + +.wf__sum-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 36px; + width: 100%; + max-width: 1400px; + margin-top: 18px; +} + +.wf__sum-card { + position: relative; + padding: 22px 26px 24px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + overflow: hidden; +} +.wf__sum-card--bad { + border-top: 3px solid var(--crimson); +} +.wf__sum-card--good { + border-top: 3px solid var(--accent); + box-shadow: 0 16px 28px -18px oklch(0.700 0.170 42 / 0.35); +} +.wf__sum-card-tag { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.22em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 14px; + padding-bottom: 12px; + border-bottom: 1px dashed var(--line-mid); +} +.wf__sum-x { + font-family: var(--f-serif); + font-style: italic; + font-size: 20px; + color: var(--crimson); +} +.wf__sum-check { + font-family: var(--f-serif); + font-style: italic; + font-size: 20px; + color: var(--accent); +} +.wf__sum-card-body { + font-family: var(--f-sans); + font-size: 22px; + line-height: 1.5; + color: var(--fg-soft); +} +.wf__sum-card-body p { margin: 0 0 8px; } +.wf__sum-card-body p:last-child { margin: 0; } +.wf__sum-card-body b { + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--accent); + margin-right: 10px; +} +.wf__sum-card-body s { + text-decoration-color: var(--crimson); + text-decoration-thickness: 2px; + color: var(--fg-mute); +} + +.wf__sum-strike { + position: absolute; + left: -10%; + right: -10%; + top: 50%; + height: 2px; + background: var(--crimson); + transform: translateY(-50%) rotate(-3deg); + opacity: 0; + animation: wfStrike 700ms 480ms cubic-bezier(.2,.8,.2,1) forwards; + transform-origin: left; +} +@keyframes wfStrike { + from { opacity: 0; transform: translateY(-50%) rotate(-3deg) scaleX(0); } + to { opacity: 0.7; transform: translateY(-50%) rotate(-3deg) scaleX(1); } +} diff --git a/web/src/chapters/05-workflow/index.tsx b/web/src/chapters/05-workflow/index.tsx new file mode 100644 index 0000000..07ff6eb --- /dev/null +++ b/web/src/chapters/05-workflow/index.tsx @@ -0,0 +1,266 @@ +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import { SceneFade } from '../../shared/SceneFade'; +import './Workflow.css'; + +/** + * Chapter 05 · 第二部分:工作流 + * + * 口播主旨: + * - 六步流程:理解需求 → 探索资源 → 制定计划 → 搭建结构 → 完成验证 → 极简总结 + * - 细节①:什么时候问 / 什么时候直接干 —— 信息够就干,不够才问 + * 用户: "做个 PPT" → AI 先问几个问题 + * 用户: "做个 PPT, 工程全员 All Hands, 10 min" → AI 直接动手 + * - 细节②:极简总结 —— "Summarize EXTREMELY BRIEFLY" + * + * 节奏(7 步 / step 0..6): + * 0 环境(eyebrow) + * 1 原文 prompt block + 六站流水线(空) + * 2 点亮第 1-3 站(line 推进) + * 3 点亮第 4-6 站 + * 4 pivot:细节① "何时问 vs 何时干" + * 5 双栏对话气泡对比 + * 6 细节② "Summarize EXTREMELY BRIEFLY" 原文 + 反例 / 正例对比 + */ + +interface Station { + no: string; + en: string; + cn: string; +} + +const STATIONS: Station[] = [ + { no: '1', en: 'Understand', cn: '理解需求' }, + { no: '2', en: 'Explore', cn: '探索资源' }, + { no: '3', en: 'Plan', cn: '制定计划' }, + { no: '4', en: 'Build', cn: '搭建结构' }, + { no: '5', en: 'Verify', cn: '完成验证' }, + { no: '6', en: 'Brief', cn: '极简总结' }, +]; + +function Workflow({ localStep }: ChapterContext) { + const at = (n: number) => localStep >= n; + + // —— 三幕 —— + const scenePipe = localStep <= 3; + const sceneDecide = localStep === 4 || localStep === 5; + const sceneSummary = localStep >= 6; + + // 流水线点亮进度 0..6 + const litCount = (() => { + if (localStep < 1) return 0; + if (localStep === 1) return 0; + if (localStep === 2) return 3; + return 6; + })(); + const linePct = (litCount / 6) * 100; + + return ( +
+ {/* ════════════ Scene PIPELINE(step 0..3)════════════ */} + +
+ {at(1) && ( + +
+ [ + SYSTEM PROMPT + · + L17-23 + / + 原文 + ] +
+
+
## Your workflow
+
+ 1. Understand user needs ... + 2. Explore provided resources ... + 3. Plan and/or make a todo list. + 4. Build folder structure ... + 5. Finish: call done ... + 6. Summarize EXTREMELY BRIEFLY — caveats and next steps only. +
+
+
+ )} + + {at(1) && ( + + {/* 底部基线 */} +
+
+
+ + {/* 6 站 */} +
+ {STATIONS.map((s, i) => { + const lit = i < litCount; + return ( +
+
{s.cn}
+
+ {s.no} + +
+
{s.en}
+
+ ); + })} +
+ + )} +
+ + + {/* ════════════ Scene DECIDE(step 4..5)════════════ */} + +
+ + 细节① +

+ 什么时候?什么时候直接干? +

+
+ [ + RULE + ] + 信息充足就干 · 信息不足才问 +
+
+ +
+ {/* —— 左:模糊请求 → 反复问 —— */} + +
+ + AMBIGUOUS · 模糊请求 +
+ +
+ USER +

帮我做个 PPT

+
+ +
+ CLAUDE +

受众?时长?正式度?品牌?数据有吗?...

+
+ ? + ? + ? + ? +
+
+ +
+ → ASK QUESTIONS +
+
+ + {/* —— 中央分隔 —— */} +
+ + vs + +
+ + {/* —— 右:详细请求 → 直接动手 —— */} + {at(5) && ( + +
+ + ENOUGH INFO · 信息够 +
+ +
+ USER +

帮我做个 PPT,工程全员 All Hands,10 分钟

+
+ +
+ CLAUDE +

好的,开始 ——

+
+ + + +
+
+ +
+ → NO QUESTIONS · GO BUILD +
+
+ )} +
+
+
+ + {/* ════════════ Scene SUMMARY(step 6)════════════ */} + +
+ + 细节② + + + + Summarize EXTREMELY BRIEFLY + 只说 注意事项下一步 + + + + [ + SYSTEM PROMPT + · + L23 + ] + + “Summarize EXTREMELY BRIEFLY — caveats and next steps only.” + + + + + {/* 反例 */} +
+
+ × 复述自己干了什么 +
+
+ 我先创建了 Header.tsx,然后又新增了 Hero.tsx, + 接着把样式拆分到 theme.ts,又给按钮加了 hover ... +
+
+
+ + {/* 正例 */} +
+
+ 注意事项 + 下一步 +
+
+

caveats — 暂未做响应式 / 文案为占位

+

next — 加 hover 状态 / 替换真实文案

+
+
+ +
+ +
+ ); +} + +const def: ChapterDef = { + id: 'workflow', + title: '第二部分 · 工作流', + eyebrow: '05', + steps: 7, + theme: 'ink', + Component: Workflow, +}; + +export default def; diff --git a/web/src/chapters/06-anti-ai/AntiAi.css b/web/src/chapters/06-anti-ai/AntiAi.css new file mode 100644 index 0000000..6dd32ee --- /dev/null +++ b/web/src/chapters/06-anti-ai/AntiAi.css @@ -0,0 +1,564 @@ +/* ========================================================= + Chapter 06 · 第三部分 · 去 AI 味 + light 主题 · hero → 反面教材 grid + 红斜线 → 字体黑名单 / 替代 + ========================================================= */ + +.aa { + position: absolute; + inset: 0; + background: var(--bg); + color: var(--fg); + font-family: var(--f-sans); + overflow: hidden; +} + +/* —— 顶部 eyebrow —— */ +.aa__eyebrow { + position: absolute; + top: 56px; + left: 96px; + display: flex; + align-items: center; + gap: 18px; + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-mute); + z-index: 4; +} +.aa__eyebrow-num { + font-family: var(--f-serif); + font-size: 28px; + letter-spacing: 0; + color: var(--accent); + font-style: italic; +} +.aa__eyebrow-bar { + width: 36px; + height: 1px; + background: var(--line-mid); +} +.aa__eyebrow-bar--long { width: 96px; } +.aa__eyebrow-text { color: var(--fg); letter-spacing: 0.18em; } +.aa__eyebrow-mute { color: var(--fg-faint); font-size: 13px; } + +/* —— 通用 prompt 源标签子部件 —— */ +.aa__src-bracket { color: var(--accent); font-weight: 600; } +.aa__src-label { color: var(--fg); font-weight: 600; letter-spacing: 0.22em; } +.aa__src-sep { color: var(--fg-faint); } +.aa__src-line { color: var(--accent); font-weight: 600; } +.aa__src-mute { color: var(--fg-mute); } + +/* ===================== Scene HERO ===================== */ + +.aa__hero { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 140px 140px 120px; + text-align: center; + gap: 32px; +} + +.aa__hero-title { + font-family: var(--f-serif); + font-weight: 400; + font-size: 168px; + line-height: 1.05; + margin: 0; +} +.aa__hero-em { + font-style: italic; + color: var(--accent); + position: relative; +} +.aa__hero-em::after { + content: ''; + position: absolute; + left: 4%; + right: 4%; + bottom: 8px; + height: 6px; + background: var(--accent); + opacity: 0.22; + transform-origin: left; + animation: aaUnderline 1100ms 360ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes aaUnderline { + from { transform: scaleX(0); opacity: 0; } + to { transform: scaleX(1); opacity: 0.22; } +} + +.aa__hero-sub { + font-family: var(--f-serif); + font-size: 36px; + line-height: 1.4; + color: var(--fg-soft); + margin: 0; + max-width: 1100px; +} +.aa__hero-sub em { + font-style: italic; + color: var(--accent); +} + +.aa__excerpt { + width: 100%; + max-width: 1100px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-left: 3px solid var(--accent); + border-radius: 2px; + padding: 18px 28px 22px; + text-align: left; + margin-top: 12px; +} +.aa__excerpt-head { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 10px; +} +.aa__excerpt-body { + display: flex; + align-items: flex-start; + gap: 14px; +} +.aa__excerpt-gt { + font-family: var(--f-mono); + font-size: 28px; + color: var(--accent); +} +.aa__excerpt-text { + font-family: var(--f-mono); + font-size: 22px; + line-height: 1.5; + color: var(--fg); +} +.aa__excerpt-text em { + font-style: normal; + color: var(--accent); + background: linear-gradient(180deg, transparent 70%, oklch(0.860 0.060 60 / 0.6) 70%); + padding: 0 2px; +} + +/* ===================== Scene GRID ===================== */ + +.aa__grid-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 140px 100px 90px; + gap: 32px; +} + +.aa__grid-cap { + font-family: var(--f-serif); + font-size: 32px; + font-style: italic; + color: var(--fg-soft); + text-align: center; +} + +.aa__grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 28px; + width: 100%; + max-width: 1480px; +} + +.aa__grid-slot { + animation: aaGridIn 720ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes aaGridIn { + from { opacity: 0; transform: translateY(28px) scale(0.94); } + to { opacity: 1; transform: none; } +} + +.aa__bad { + display: flex; + flex-direction: column; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + overflow: hidden; + transition: filter 600ms, opacity 600ms; +} +.aa__bad.is-slashed { + filter: grayscale(0.55) brightness(0.92); + opacity: 0.78; +} + +.aa__bad-canvas { + position: relative; + height: 220px; + display: flex; + align-items: center; + justify-content: center; + background: + repeating-linear-gradient(45deg, + oklch(0.180 0.014 60 / 0.025) 0 6px, + transparent 6px 12px); + overflow: hidden; +} + +/* —— 反面教材 1:紫粉蓝渐变 —— */ +.aa__bad-gradient { + position: absolute; + inset: 14px; + border-radius: 12px; + background: linear-gradient(135deg, #a78bfa 0%, #f0abfc 35%, #67e8f9 70%, #fda4af 100%); + box-shadow: 0 12px 36px -10px oklch(0.6 0.18 320 / 0.4); +} + +/* —— 反面教材 2:Emoji 滥用 —— */ +.aa__bad-emoji { + display: flex; + gap: 12px; + font-size: 44px; + line-height: 1; + filter: drop-shadow(0 2px 4px oklch(0 0 0 / 0.15)); +} + +/* —— 反面教材 3:左侧色条卡 —— */ +.aa__bad-leftbar { + position: relative; + width: 220px; + height: 130px; + background: white; + border-radius: 14px; + display: flex; + align-items: stretch; + overflow: hidden; + box-shadow: 0 16px 28px -10px oklch(0.180 0.014 60 / 0.18); +} +.aa__bad-leftbar-stripe { + width: 8px; + background: linear-gradient(to bottom, #6366f1, #a855f7); +} +.aa__bad-leftbar-body { + flex: 1; + padding: 18px 16px; + display: flex; + flex-direction: column; + gap: 8px; +} +.aa__bad-leftbar-t { + width: 60%; + height: 14px; + background: oklch(0.4 0.04 280); + border-radius: 4px; +} +.aa__bad-leftbar-l { + width: 90%; + height: 8px; + background: oklch(0.85 0.02 280); + border-radius: 4px; +} +.aa__bad-leftbar-l--short { width: 60%; } + +/* —— 反面教材 4:烂大街字体 —— */ +.aa__bad-font { + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; + font-size: 22px; + color: var(--fg); +} +.aa__bad-font span:first-child { font-weight: 600; font-size: 26px; } + +/* —— 反面教材 5:data slop —— */ +.aa__bad-data { + display: grid; + grid-template-columns: repeat(2, auto); + gap: 18px 32px; + font-family: var(--f-mono); + font-size: 22px; + color: var(--fg); +} +.aa__bad-data > div { + display: flex; + align-items: center; + gap: 8px; +} +.aa__bad-data span { + color: oklch(0.6 0.18 320); + font-size: 20px; +} +.aa__bad-data b { + font-family: var(--f-serif); + font-style: italic; + font-weight: 400; + font-size: 28px; + color: var(--fg); +} + +/* —— 反面教材 6:复杂 SVG —— */ +.aa__bad-svg { + width: 130px; + height: 130px; + filter: drop-shadow(0 6px 12px oklch(0.6 0.18 320 / 0.35)); +} + +/* —— 红斜杠 —— */ +.aa__bad-slash { + position: absolute; + inset: 0; + pointer-events: none; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 360ms; +} +.aa__bad.is-slashed .aa__bad-slash { + opacity: 1; +} +.aa__bad-slash-line { + display: block; + width: 130%; + height: 5px; + background: var(--crimson); + transform: rotate(-18deg) scaleX(0); + transform-origin: left; + border-radius: 1px; + box-shadow: + 0 0 12px oklch(0.560 0.200 22 / 0.6), + 0 0 24px oklch(0.560 0.200 22 / 0.25); + animation-fill-mode: forwards; +} +.aa__bad.is-slashed .aa__bad-slash-line { + animation: aaSlash 520ms cubic-bezier(.6,-0.05,.2,1.2) forwards; +} +@keyframes aaSlash { + from { transform: rotate(-18deg) scaleX(0); } + to { transform: rotate(-18deg) scaleX(1); } +} + +/* 错开每张卡的斜线触发节奏 */ +.aa__grid-slot:nth-child(1) .aa__bad-slash-line { animation-delay: 80ms; } +.aa__grid-slot:nth-child(2) .aa__bad-slash-line { animation-delay: 180ms; } +.aa__grid-slot:nth-child(3) .aa__bad-slash-line { animation-delay: 280ms; } +.aa__grid-slot:nth-child(4) .aa__bad-slash-line { animation-delay: 380ms; } +.aa__grid-slot:nth-child(5) .aa__bad-slash-line { animation-delay: 480ms; } +.aa__grid-slot:nth-child(6) .aa__bad-slash-line { animation-delay: 580ms; } + +.aa__bad-foot { + display: flex; + flex-direction: column; + gap: 4px; + padding: 14px 18px 16px; + border-top: 1px solid var(--line-mid); + background: var(--bg); +} +.aa__bad-en { + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.28em; + text-transform: uppercase; + color: var(--fg-faint); +} +.aa__bad-cn { + font-family: var(--f-serif); + font-size: 26px; + color: var(--fg); +} + +.aa__grid-verdict { + font-family: var(--f-serif); + font-size: 32px; + color: var(--fg-soft); + text-align: center; + display: flex; + align-items: center; + gap: 16px; +} +.aa__grid-verdict em { + font-style: italic; + color: var(--accent); +} +.aa__grid-verdict-mark { + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + color: var(--crimson); +} + +/* ===================== Scene FONTS ===================== */ + +.aa__fonts-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 130px 100px 90px; + gap: 32px; +} + +.aa__fonts-head { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + text-align: center; +} +.aa__fonts-num { + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--accent); +} +.aa__fonts-title { + font-family: var(--f-serif); + font-weight: 400; + font-size: 64px; + line-height: 1.15; + color: var(--fg); + margin: 0; +} +.aa__fonts-title em { + font-style: italic; + color: var(--accent); +} + +.aa__fonts-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 36px; + width: 100%; + max-width: 1480px; +} + +.aa__fonts-col { + display: flex; + flex-direction: column; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + padding: 22px 28px 26px; +} +.aa__fonts-col--ban { + border-top: 3px solid var(--crimson); +} +.aa__fonts-col--alt { + border-top: 3px solid var(--accent); + box-shadow: 0 16px 28px -18px oklch(0.700 0.170 42 / 0.35); +} + +.aa__fonts-col-tag { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.24em; + text-transform: uppercase; + color: var(--fg-mute); + padding-bottom: 14px; + margin-bottom: 14px; + border-bottom: 1px dashed var(--line-mid); +} +.aa__fonts-col-mark { + font-family: var(--f-serif); + font-style: italic; + font-size: 22px; + color: var(--crimson); +} +.aa__fonts-col-mark--ok { + color: var(--accent); +} + +.aa__fonts-list { + display: flex; + flex-direction: column; + gap: 14px; +} + +.aa__fonts-row { + position: relative; + display: grid; + grid-template-columns: 1fr 1.2fr auto; + gap: 16px; + align-items: center; + padding: 8px 4px; + animation: aaFontIn 540ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes aaFontIn { + from { opacity: 0; transform: translateX(-12px); } + to { opacity: 1; transform: none; } +} +.aa__fonts-row-name { + font-size: 28px; + color: var(--fg); +} +.aa__fonts-row-sample { + font-size: 24px; + color: var(--fg-soft); +} +.aa__fonts-row-tag { + font-family: var(--f-mono); + font-size: 12px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-faint); +} + +.aa__fonts-row-strike { + position: absolute; + left: -4px; + right: -4px; + top: 50%; + height: 2px; + background: var(--crimson); + transform-origin: left; + transform: translateY(-50%) scaleX(0); + animation: aaRowStrike 520ms cubic-bezier(.6,-0.05,.2,1.2) forwards; + opacity: 0.85; +} +.aa__fonts-row--ban:nth-child(1) .aa__fonts-row-strike { animation-delay: 480ms; } +.aa__fonts-row--ban:nth-child(2) .aa__fonts-row-strike { animation-delay: 580ms; } +.aa__fonts-row--ban:nth-child(3) .aa__fonts-row-strike { animation-delay: 680ms; } +.aa__fonts-row--ban:nth-child(4) .aa__fonts-row-strike { animation-delay: 780ms; } +.aa__fonts-row--ban:nth-child(5) .aa__fonts-row-strike { animation-delay: 880ms; } +@keyframes aaRowStrike { + to { transform: translateY(-50%) scaleX(1); } +} + +.aa__fonts-foot { + display: flex; + align-items: center; + gap: 16px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.24em; + text-transform: uppercase; + color: var(--fg-mute); +} +.aa__fonts-foot span:last-child { + color: var(--accent); + font-weight: 600; +} +.aa__fonts-foot-dot { + width: 4px; + height: 4px; + border-radius: 50%; + background: var(--fg-faint); + flex: none; +} diff --git a/web/src/chapters/06-anti-ai/index.tsx b/web/src/chapters/06-anti-ai/index.tsx new file mode 100644 index 0000000..ec84287 --- /dev/null +++ b/web/src/chapters/06-anti-ai/index.tsx @@ -0,0 +1,290 @@ +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import { SceneFade } from '../../shared/SceneFade'; +import './AntiAi.css'; + +/** + * Chapter 06 · 第三部分:去 AI 味 + * + * 口播主旨: + * - "AI 味" 来自一组反复出现的视觉套路:渐变 / Emoji / 左侧色条卡 / 烂大街字体 / data slop / 复杂硬画 SVG + * - 单个看都行,反复出现就成了"味道" + * - 字体清单:重点不是该用什么,是别用什么 (Inter / Roboto / Arial / Fraunces / system-ui) + * - 替代方案给了小众但品质高的字体 (Plus Jakarta Sans / Space Grotesk / Sora / Newsreader) + * + * 节奏(6 步 / step 0..5): + * 0 环境(eyebrow) + * 1 hero "什么是 AI 味?" + 原文 prompt block + * 2 6 张反面教材依次出现(stagger) + * 3 全部被红色斜杠"叉掉" + * 4 字体小专题:黑名单(实际字体渲染) + * 5 推荐替代(实际字体渲染)+ takeaway + */ + +interface AntiPattern { + id: string; + cn: string; + en: string; +} + +const PATTERNS: AntiPattern[] = [ + { id: 'gradient', cn: '紫粉蓝渐变背景', en: 'PASTEL GRADIENT' }, + { id: 'emoji', cn: 'Emoji 当图标', en: 'EMOJI ICONS' }, + { id: 'leftbar', cn: '左侧彩色色条卡', en: 'LEFT COLOR BAR' }, + { id: 'font', cn: '烂大街字体', en: 'STOCK FONTS' }, + { id: 'data', cn: '堆砌假数据', en: 'DATA SLOP' }, + { id: 'svg', cn: '复杂硬画 SVG', en: 'OVER-DRAWN SVG' }, +]; + +const BANNED_FONTS = [ + { name: 'Inter', family: 'Inter, sans-serif', sample: 'AaBbCc 123' }, + { name: 'Roboto', family: 'Roboto, sans-serif', sample: 'AaBbCc 123' }, + { name: 'Arial', family: 'Arial, sans-serif', sample: 'AaBbCc 123' }, + { name: 'Fraunces', family: '"Fraunces", serif', sample: 'AaBbCc 123' }, + { name: 'system-ui', family: 'system-ui, sans-serif', sample: 'AaBbCc 123' }, +]; + +const BETTER_FONTS = [ + { name: 'Plus Jakarta Sans', family: '"Plus Jakarta Sans", sans-serif', sample: 'AaBbCc 123', tag: 'sans · workhorse' }, + { name: 'Space Grotesk', family: '"Space Grotesk", sans-serif', sample: 'AaBbCc 123', tag: 'sans · 工程感' }, + { name: 'Sora', family: '"Sora", sans-serif', sample: 'AaBbCc 123', tag: 'sans · 现代克制' }, + { name: 'Newsreader', family: '"Newsreader", serif', sample: 'AaBbCc 123', tag: 'serif · 编辑感' }, +]; + +/** 单个反面教材小卡片 —— 视觉示意 + 名称 + (后期) 红斜线 */ +function BadCard({ p, slashed }: { p: AntiPattern; slashed: boolean }) { + return ( +
+
+ {p.id === 'gradient' && ( +
+ )} + {p.id === 'emoji' && ( +
+ 🚀🎯💡🔥 +
+ )} + {p.id === 'leftbar' && ( +
+ +
+
+
+
+
+
+ )} + {p.id === 'font' && ( +
+ Welcome to Our Platform + Get Started in Seconds +
+ )} + {p.id === 'data' && ( +
+
4.9
+
+42%
+
12k
+
99.9%
+
+ )} + {p.id === 'svg' && ( + + + + + + + + + + + + + )} + + {/* 红色斜杠覆盖层 */} +
+ +
+
+ +
+ {p.en} + {p.cn} +
+
+ ); +} + +function AntiAi({ localStep }: ChapterContext) { + const at = (n: number) => localStep >= n; + + const sceneHero = localStep <= 1; + const sceneGrid = localStep === 2 || localStep === 3; + const sceneFonts = localStep >= 4; + + const slashed = localStep >= 3; + const showAlt = localStep >= 5; + + return ( +
+ {/* ════════ Scene HERO(step 0..1)════════ */} + +
+ {at(1) && ( + <> + + 什么是 AI 味? + + + + 单看每一条都不算错 ——
+ 反复堆在一起,就成了一种味道。 +
+ + +
+ [ + SYSTEM PROMPT + · + L04 + ] +
+
+ > + + Avoid web design tropes and conventions unless you are + making a web page. + +
+
+ + )} +
+
+ + {/* ════════ Scene GRID(step 2..3)════════ */} + +
+ + 举几个 AI 反复用的"老套路" —— 一看就知道 + + +
+ {PATTERNS.map((p, i) => ( +
+ +
+ ))} +
+ + {at(3) && ( + + × + Claude Design 把这些雷区一条条列出来,逼着 AI 走新路。 + + )} +
+
+ + {/* ════════ Scene FONTS(step 4..5)════════ */} + +
+ + 小专题 +

+ 字体推荐 —— 重点是 别用什么 +

+
+ +
+ {/* 黑名单 */} + +
+ × + BLACKLIST · 别用 +
+
+ {BANNED_FONTS.map((f, i) => ( +
+
+ {f.name} +
+
+ {f.sample} +
+ +
+ ))} +
+
+ + {/* 推荐替代 */} + {showAlt && ( + +
+ + ALTERNATIVES · 替代方案 +
+
+ {BETTER_FONTS.map((f, i) => ( +
+
+ {f.name} +
+
+ {f.sample} +
+ {f.tag} +
+ ))} +
+
+ )} +
+ + {showAlt && ( + + 小众 + + 免费 + + 品质高 + + 立刻去掉 AI 味 + + )} +
+
+
+ ); +} + +const def: ChapterDef = { + id: 'anti-ai', + title: '第三部分 · 去 AI 味', + eyebrow: '06', + steps: 6, + theme: 'light', + Component: AntiAi, +}; + +export default def; diff --git a/web/src/chapters/07-oklch/Oklch.css b/web/src/chapters/07-oklch/Oklch.css new file mode 100644 index 0000000..96970c9 --- /dev/null +++ b/web/src/chapters/07-oklch/Oklch.css @@ -0,0 +1,539 @@ +/* ========================================================= + Chapter 07 · 第四部分 · oklch 配色 + light 主题 · 顺序对齐口播稿: + 原文 prompt → 三层策略 → 大字提问 → 双色相条对比 → 收尾 + ========================================================= */ + +.ok { + position: absolute; + inset: 0; + background: var(--bg); + color: var(--fg); + font-family: var(--f-sans); + overflow: hidden; +} + +/* —— 通用 prompt 源标签 —— */ +.ok__src-bracket { color: var(--accent); font-weight: 600; } +.ok__src-label { color: var(--fg); font-weight: 600; letter-spacing: 0.22em; } +.ok__src-sep { color: var(--fg-faint); } +.ok__src-line { color: var(--accent); font-weight: 600; } +.ok__src-mute { color: var(--fg-mute); } + +/* ===================== Scene SOURCE (step 0) ===================== */ + +.ok__src-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 140px 140px 120px; + gap: 36px; + text-align: center; +} + +.ok__src-eyebrow { + display: flex; + align-items: center; + gap: 10px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.22em; + text-transform: uppercase; + color: var(--fg-mute); +} + +.ok__src-block { + width: 100%; + max-width: 1400px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-left: 3px solid var(--accent); + border-radius: 2px; + padding: 36px 44px 40px; + text-align: left; + display: flex; + flex-direction: column; + gap: 20px; +} + +.ok__src-line-row { + display: grid; + grid-template-columns: 56px 1fr; + align-items: baseline; + gap: 24px; + font-family: var(--f-mono); + font-size: 32px; + line-height: 1.45; + color: var(--fg); + opacity: 0; + transform: translateY(10px); + animation: okSrcLineIn 720ms cubic-bezier(.2,.8,.2,1) forwards; +} +.ok__src-line-row--1 { animation-delay: 380ms; } +.ok__src-line-row--2 { animation-delay: 980ms; } +.ok__src-line-row--3 { animation-delay: 1580ms; } +@keyframes okSrcLineIn { + to { opacity: 1; transform: none; } +} + +.ok__src-num { + font-family: var(--f-mono); + font-size: 18px; + letter-spacing: 0.08em; + color: var(--fg-faint); + text-align: right; +} + +.ok__src-text em.ok__src-h { + font-style: normal; + color: var(--accent); + background: linear-gradient(180deg, transparent 65%, oklch(0.860 0.060 60 / 0.7) 65%); + padding: 0 4px; + font-weight: 600; +} +.ok__src-h--1 { animation: okSrcHi 700ms 700ms cubic-bezier(.2,.8,.2,1) backwards; } +.ok__src-h--2 { animation: okSrcHi 700ms 1300ms cubic-bezier(.2,.8,.2,1) backwards; } +.ok__src-h--3 { animation: okSrcHi 700ms 1900ms cubic-bezier(.2,.8,.2,1) backwards; } +@keyframes okSrcHi { + from { + background: linear-gradient(180deg, transparent 65%, oklch(0.860 0.060 60 / 0) 65%); + color: var(--fg); + } + to { + background: linear-gradient(180deg, transparent 65%, oklch(0.860 0.060 60 / 0.7) 65%); + color: var(--accent); + } +} + +.ok__src-foot { + font-family: var(--f-serif); + font-style: italic; + font-size: 28px; + color: var(--fg-mute); +} +.ok__src-foot em { + font-style: italic; + color: var(--accent); +} + +/* ===================== Scene RULES (step 1) ===================== */ + +.ok__rules-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 130px 140px 100px; + gap: 40px; +} + +.ok__rules-head { + font-family: var(--f-serif); + font-weight: 400; + font-size: 72px; + line-height: 1.1; + margin: 0; + text-align: center; +} +.ok__rules-head em { + font-style: italic; + color: var(--accent); +} + +.ok__rules { + width: 100%; + max-width: 1400px; + display: flex; + flex-direction: column; + gap: 18px; +} + +.ok__rule { + display: grid; + grid-template-columns: 90px 1fr auto; + align-items: center; + gap: 28px; + padding: 22px 32px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-left: 4px solid var(--line-mid); + border-radius: 3px; + transition: border-color 360ms; +} +.ok__rule--good { border-left-color: var(--accent); } +.ok__rule--ok { border-left-color: var(--accent); } +.ok__rule--bad { border-left-color: var(--crimson); opacity: 0.94; } + +.ok__rule-num { + font-family: var(--f-mono); + font-size: 56px; + letter-spacing: -0.02em; + color: var(--fg-faint); + font-weight: 500; +} + +.ok__rule-body { + display: flex; + flex-direction: column; + gap: 8px; +} +.ok__rule-title { + font-family: var(--f-serif); + font-size: 36px; + color: var(--fg); + line-height: 1.2; +} +.ok__rule-title em { + font-style: italic; + color: var(--accent); +} +.ok__rule-desc { + font-family: var(--f-sans); + font-size: 19px; + line-height: 1.5; + color: var(--fg-mute); +} +.ok__rule-swatches { + display: flex; + gap: 6px; + margin-top: 4px; +} +.ok__rule-swatches span { + width: 32px; + height: 32px; + border-radius: 2px; + box-shadow: inset 0 0 0 1px oklch(0 0 0 / 0.08); + animation: okSwatchIn 520ms cubic-bezier(.2,.8,.2,1) backwards; +} +.ok__rule-swatches span:nth-child(2) { animation-delay: 60ms; } +.ok__rule-swatches span:nth-child(3) { animation-delay: 120ms; } +.ok__rule-swatches span:nth-child(4) { animation-delay: 180ms; } +.ok__rule-swatches span:nth-child(5) { animation-delay: 240ms; } +.ok__rule-swatches span:nth-child(6) { animation-delay: 300ms; } +@keyframes okSwatchIn { + from { opacity: 0; transform: translateY(14px) scale(0.92); } + to { opacity: 1; transform: none; } +} + +.ok__rule-mark { + font-family: var(--f-serif); + font-style: italic; + font-size: 56px; + line-height: 1; + width: 56px; + text-align: center; +} +.ok__rule-mark--good { color: var(--accent); } +.ok__rule-mark--bad { color: var(--crimson); } + +/* ===================== Scene PIVOT (step 2) ===================== */ + +.ok__pivot { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 24px; + padding: 140px; +} + +.ok__pivot-q { + font-family: var(--f-serif); + font-weight: 400; + font-size: 168px; + line-height: 1.05; + margin: 0; + color: var(--fg); +} +.ok__pivot-em { + font-family: var(--f-mono); + font-style: italic; + color: var(--accent); + font-size: 0.78em; + letter-spacing: -0.01em; + margin: 0 0.06em; + position: relative; +} +.ok__pivot-em::after { + content: ''; + position: absolute; + left: 6%; + right: 6%; + bottom: 6px; + height: 8px; + background: var(--accent); + opacity: 0.22; + transform-origin: left; + animation: okPivotUnderline 1100ms 380ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes okPivotUnderline { + from { transform: scaleX(0); opacity: 0; } + to { transform: scaleX(1); opacity: 0.22; } +} + +.ok__pivot-sub { + font-family: var(--f-serif); + font-size: 36px; + line-height: 1.4; + color: var(--fg-soft); + margin: 12px 0 0; +} +.ok__pivot-strike { + position: relative; + color: var(--fg-mute); + font-family: var(--f-mono); + font-style: italic; + padding: 0 6px; +} +.ok__pivot-strike::after { + content: ''; + position: absolute; + left: 0; + right: 0; + top: 55%; + height: 3px; + background: var(--crimson); + transform-origin: left; + transform: translateY(-50%) scaleX(0); + animation: okPivotStrike 520ms 980ms cubic-bezier(.6,-0.05,.2,1.2) forwards; + opacity: 0.75; +} +@keyframes okPivotStrike { + to { transform: translateY(-50%) scaleX(1); } +} + +.ok__pivot-issue { + font-family: var(--f-serif); + font-weight: 400; + font-size: 96px; + line-height: 1; + margin: 0; + color: var(--fg); + letter-spacing: -0.01em; +} +.ok__pivot-issue em { + font-style: italic; + color: var(--crimson); +} + +/* ===================== Scene COMPARE (step 3) ===================== */ + +.ok__cmp-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 130px 100px 100px; + gap: 36px; +} + +.ok__cmp-cap { + font-family: var(--f-serif); + font-size: 30px; + font-style: italic; + color: var(--fg-soft); + text-align: center; +} +.ok__cmp-cap em { + font-style: italic; + color: var(--accent); +} + +.ok__cmp-grid { + display: flex; + flex-direction: column; + gap: 64px; + width: 100%; + max-width: 1480px; +} + +.ok__cmp-row { + position: relative; + display: flex; + flex-direction: column; + gap: 14px; + padding-bottom: 88px; +} + +.ok__cmp-row-head { + display: flex; + align-items: baseline; + gap: 18px; +} +.ok__cmp-row-tag { + font-family: var(--f-mono); + font-size: 18px; + letter-spacing: 0.24em; + text-transform: uppercase; + padding: 4px 10px; + border-radius: 2px; +} +.ok__cmp-row-tag--bad { + color: var(--crimson); + background: oklch(0.560 0.200 22 / 0.10); + border: 1px solid oklch(0.560 0.200 22 / 0.45); +} +.ok__cmp-row-tag--good { + color: var(--accent-deep); + background: oklch(0.700 0.170 42 / 0.12); + border: 1px solid oklch(0.700 0.170 42 / 0.45); +} +.ok__cmp-row-formula { + font-family: var(--f-mono); + font-size: 18px; + color: var(--fg-mute); + letter-spacing: 0.04em; +} + +.ok__cmp-strip { + display: grid; + grid-template-columns: repeat(12, 1fr); + gap: 8px; + height: 110px; +} + +.ok__cmp-swatch { + position: relative; + border-radius: 2px; + box-shadow: inset 0 0 0 1px oklch(0 0 0 / 0.06); + animation: okSwatchIn 620ms cubic-bezier(.2,.8,.2,1) backwards; +} +/* HSL 行的"黄色"格子单独脉冲一下 */ +.ok__cmp-swatch.is-spot { + animation: okSwatchIn 620ms cubic-bezier(.2,.8,.2,1) backwards, + okSpotPulse 1600ms 1400ms ease-in-out infinite; + box-shadow: + inset 0 0 0 1px oklch(0 0 0 / 0.06), + 0 0 0 0 oklch(0.560 0.200 22 / 0.5); +} +@keyframes okSpotPulse { + 0%, 100% { + box-shadow: + inset 0 0 0 1px oklch(0 0 0 / 0.06), + 0 0 0 0 oklch(0.560 0.200 22 / 0.45); + transform: translateY(0); + } + 50% { + box-shadow: + inset 0 0 0 1px oklch(0 0 0 / 0.06), + 0 0 0 8px oklch(0.560 0.200 22 / 0); + transform: translateY(-4px); + } +} +.ok__cmp-swatch-tick { + position: absolute; + bottom: -22px; + left: 50%; + transform: translateX(-50%); + font-family: var(--f-mono); + font-size: 11px; + letter-spacing: 0.04em; + color: var(--fg-faint); +} + +/* —— 感知亮度曲线 —— */ +.ok__cmp-curve { + position: absolute; + bottom: 28px; + left: 0; + right: 0; + width: 100%; + height: 60px; + pointer-events: none; + animation: okCurveIn 720ms cubic-bezier(.2,.8,.2,1) backwards; +} +.ok__cmp-curve--bad { animation-delay: 1100ms; } +.ok__cmp-curve--good { animation-delay: 1280ms; } +@keyframes okCurveIn { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: none; } +} + +.ok__cmp-callout { + position: absolute; + bottom: -30px; + left: 0; + display: flex; + align-items: center; + gap: 10px; + font-family: var(--f-serif); + font-size: 22px; + font-style: italic; + opacity: 0; + animation: okCalloutIn 620ms cubic-bezier(.2,.8,.2,1) forwards; +} +.ok__cmp-callout--bad { color: var(--crimson); animation-delay: 1700ms; } +.ok__cmp-callout--good { color: var(--accent-deep); animation-delay: 1900ms; } +@keyframes okCalloutIn { + from { opacity: 0; transform: translateX(-8px); } + to { opacity: 1; transform: none; } +} +.ok__cmp-callout-arrow { + font-family: var(--f-mono); + font-style: normal; +} + +/* ===================== Scene CLOSE (step 4) ===================== */ + +.ok__close { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 20px; + padding: 140px; +} + +.ok__close-eyebrow { + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + color: var(--fg-mute); +} + +.ok__close-line { + font-family: var(--f-serif); + font-weight: 400; + font-size: 200px; + line-height: 1; + margin: 0; + color: var(--fg); +} +.ok__close-line em { + font-style: italic; + color: var(--accent); +} + +.ok__close-arrow { + font-family: var(--f-mono); + font-size: 96px; + color: var(--accent); + line-height: 1; + display: inline-block; + animation: okCloseArrow 1200ms 1100ms cubic-bezier(.6,-0.05,.2,1.2) backwards; +} +@keyframes okCloseArrow { + from { transform: translateY(40px); opacity: 0; } + to { transform: none; opacity: 1; } +} + +.ok__close-caption { + font-family: var(--f-serif); + font-style: italic; + font-size: 30px; + color: var(--fg-mute); + margin-top: 16px; +} diff --git a/web/src/chapters/07-oklch/index.tsx b/web/src/chapters/07-oklch/index.tsx new file mode 100644 index 0000000..64de199 --- /dev/null +++ b/web/src/chapters/07-oklch/index.tsx @@ -0,0 +1,306 @@ +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import { SceneFade } from '../../shared/SceneFade'; +import './Oklch.css'; + +/** + * Chapter 07 · 第四部分:oklch 配色 + * + * 口播原顺序(严格对齐): + * 1. "他的配色策略分成了:优先用品牌色;不够就用 oklch 派生衍生色;绝对不要凭空编新色。" + * 2. "为什么是 oklch 呢?" + * 3. "传统的 HSL色彩空间有个大问题 — 感知不均匀。" + * "同样的亮度值,黄色看着比蓝色亮一大截。" + * 4. "oklch 是感知均匀的色彩空间。保持亮度和色度不变,只转色相角,出来的颜色自然就和谐。" + * 5. "这个细节看着小,但网页端高级感一下就上来了。" + * + * 节奏(5 步 / step 0..4): + * 0 原文 prompt(L41-43)三段被逐句"点亮" —— 引出策略原文 + * 1 三层策略卡:① 品牌色 ② oklch 派生 ③ 禁止凭空造色 + * 2 pivot 大字 "为什么是 oklch ?" + 副:"HSL 有个大问题 —— 感知不均匀" + * 3 HSL vs OKLCH 双色相条对比 + 感知亮度曲线 + 黄色 spotlight + * 4 收尾:"网页端 高级感 ↑" + */ + +const HUES = [0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330]; + +/** HSL 感知亮度(粗略 sRGB BT.709 相对亮度) */ +function hslPerceived(h: number): number { + const s = 0.7; + const l = 0.6; + const c = (1 - Math.abs(2 * l - 1)) * s; + const hh = h / 60; + const x = c * (1 - Math.abs((hh % 2) - 1)); + let r = 0, g = 0, b = 0; + if (hh < 1) { r = c; g = x; } + else if (hh < 2) { r = x; g = c; } + else if (hh < 3) { g = c; b = x; } + else if (hh < 4) { g = x; b = c; } + else if (hh < 5) { r = x; b = c; } + else { r = c; b = x; } + const m = l - c / 2; + r += m; g += m; b += m; + return 0.2126 * r + 0.7152 * g + 0.0722 * b; +} + +function Oklch({ localStep }: ChapterContext) { + const at = (n: number) => localStep >= n; + + const sceneSrc = localStep <= 0; + const sceneRules = localStep === 1; + const scenePivot = localStep === 2; + const sceneCompare = localStep === 3; + const sceneClose = localStep >= 4; + + return ( +
+ {/* ════════ Scene SOURCE(step 0)—— 原文先出 ════════ */} + +
+ + [ + SYSTEM PROMPT + · + L41-43 + / + Color Strategy 原文 + ] + + + +
+ L41 + + Color usage: try to use colors from{' '} + brand / design system. + +
+
+ L42 + + If too restrictive, use{' '} + oklch{' '} + to define harmonious colors that match. + +
+
+ L43 + + Avoid inventing{' '} + new colors from scratch. + +
+
+ + + 三段 —— 一套分层的配色策略 + +
+
+ + {/* ════════ Scene RULES(step 1)—— 三层策略 ════════ */} + +
+ + 配色策略 —— 三层防线 + + +
+ {/* 第 1 层 */} + +
01
+
+
优先品牌色
+
已有 design system → 直接复用,别"再创造"
+
+ + + +
+
+
+
+ + {/* 第 2 层 */} + +
02
+
+
不够用?oklch 派生
+
L / C 不变,h 旋转 —— 自动得到和谐衍生色
+
+ {[42, 90, 150, 200, 260, 320].map((h) => ( + + ))} +
+
+
+
+ + {/* 第 3 层 */} + +
03
+
+
禁止凭空造色
+
"我觉得这个紫色挺好看" —— 这就是 AI 味的源头
+
+ + + + +
+
+
×
+
+
+
+
+ + {/* ════════ Scene PIVOT(step 2)—— 大字提问 ════════ */} + +
+ + 为什么是 oklch ? + + + + HSL +  有个大问题 —— + + + + 感知不均匀 + +
+
+ + {/* ════════ Scene COMPARE(step 3)—— 双色相条 + 曲线 ════════ */} + +
+ + 同一组亮度 / 饱和度 · 12 个色相 —— 看人眼对哪种更舒服 + + +
+ {/* HSL 行 */} +
+
+ HSL · 老 + hsl(h, 70%, 60%) +
+
+ {HUES.map((h, i) => ( +
+ {h}° +
+ ))} +
+ + { + const x = (i / (HUES.length - 1)) * 1200; + const y = 78 - hslPerceived(h) * 70; + return `L${x.toFixed(1)} ${y.toFixed(1)}`; + }).join(' ') + + ' L1200 80 Z' + } + fill="var(--crimson)" + fillOpacity="0.12" + stroke="var(--crimson)" + strokeWidth="2" + /> + +
+ + 同 60% 亮度 —— 黄色看着像被打了灯 +
+
+ + {/* OKLCH 行 */} +
+
+ OKLCH · 新 + oklch(0.70 0.15 h) +
+
+ {HUES.map((h, i) => ( +
+ {h}° +
+ ))} +
+ + + +
+ + L / C 不变,只转 h —— 亮度自动一致 +
+
+
+
+
+ + {/* ════════ Scene CLOSE(step 4)════════ */} + +
+ + 细节看着小 —— + + + + 网页端高级感 + + + + ↑ + + + + 一下就上来了 + +
+
+
+ ); +} + +const def: ChapterDef = { + id: 'oklch', + title: '第四部分 · oklch 配色', + eyebrow: '07', + steps: 5, + theme: 'light', + Component: Oklch, +}; + +export default def; diff --git a/web/src/chapters/08-restraint/Restraint.css b/web/src/chapters/08-restraint/Restraint.css new file mode 100644 index 0000000..8454544 --- /dev/null +++ b/web/src/chapters/08-restraint/Restraint.css @@ -0,0 +1,726 @@ +/* ========================================================= + Chapter 08 · 第五部分 · 内容克制 + light 主题 · 顺序对齐口播稿: + 乔布斯 1000No/1Yes 开场 → 落地页 → 塞满 → 砍 → 立原则 → 收尾 + ========================================================= */ + +.re { + position: absolute; + inset: 0; + background: var(--bg); + color: var(--fg); + font-family: var(--f-sans); + overflow: hidden; +} + +/* —— 通用 prompt 源标签 —— */ +.re__src-bracket { color: var(--accent); font-weight: 600; } +.re__src-label { color: var(--fg); font-weight: 600; letter-spacing: 0.22em; } +.re__src-sep { color: var(--fg-faint); } +.re__src-line { color: var(--accent); font-weight: 600; } + +/* ===================== Scene JOBS (step 0) ===================== */ + +.re__jobs { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 24px; + padding: 120px; +} + +.re__jobs-by { + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-faint); + margin-bottom: 16px; +} + +.re__jobs-row { + display: flex; + align-items: center; + gap: 64px; +} + +.re__jobs-num { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; +} +.re__jobs-num-figure { + font-family: var(--f-serif); + font-weight: 400; + font-size: 320px; + line-height: 0.9; + letter-spacing: -0.04em; +} +.re__jobs-num-label { + font-family: var(--f-mono); + font-size: 22px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-mute); +} +.re__jobs-num--no .re__jobs-num-figure { + color: var(--fg-mute); + font-style: italic; +} +.re__jobs-num--yes .re__jobs-num-figure { + color: var(--accent); + font-style: italic; + position: relative; +} +.re__jobs-num--yes .re__jobs-num-figure::after { + content: ''; + position: absolute; + left: -10%; + right: -10%; + bottom: 28px; + height: 14px; + background: var(--accent); + opacity: 0.18; + transform-origin: left; + animation: reJobsUnderline 1100ms 1100ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes reJobsUnderline { + from { transform: scaleX(0); opacity: 0; } + to { transform: scaleX(1); opacity: 0.18; } +} + +.re__jobs-arrow { + font-family: var(--f-serif); + font-style: italic; + font-size: 64px; + color: var(--fg-faint); +} + +.re__jobs-quote { + margin-top: 24px; + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + color: var(--fg-soft); + letter-spacing: 0.01em; +} +.re__jobs-quote em { + color: var(--accent); + font-style: italic; + font-size: 1.1em; +} + +.re__jobs-src { + display: flex; + align-items: center; + gap: 8px; + margin-top: 4px; + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-mute); +} + +/* ===================== Scene PAGE (step 1..3) ===================== */ + +.re__page-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + padding: 120px 120px 90px; + gap: 24px; +} + +.re__page-cap { + display: flex; + align-items: baseline; + gap: 14px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.24em; + text-transform: uppercase; + color: var(--fg-mute); +} +.re__page-cap-tag { + color: var(--accent); + font-weight: 600; +} +.re__page-cap-sep { + color: var(--fg-faint); +} +.re__page-cap-text { + font-family: var(--f-serif); + font-style: italic; + letter-spacing: 0.02em; + text-transform: none; + font-size: 22px; + color: var(--fg-soft); +} + +/* —— 浏览器外框 —— */ +.re__browser { + width: 100%; + max-width: 1500px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + overflow: hidden; + box-shadow: 0 24px 48px -28px oklch(0 0 0 / 0.18); +} +.re__browser-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 16px; + background: oklch(0.910 0.020 78); + border-bottom: 1px solid var(--line-mid); +} +.re__browser-dot { + width: 10px; + height: 10px; + border-radius: 50%; + background: oklch(0.820 0.030 78); +} +.re__browser-dot:nth-child(1) { background: oklch(0.730 0.180 28); } +.re__browser-dot:nth-child(2) { background: oklch(0.830 0.150 80); } +.re__browser-dot:nth-child(3) { background: oklch(0.770 0.140 145); } +.re__browser-url { + margin-left: 14px; + font-family: var(--f-mono); + font-size: 13px; + color: var(--fg-mute); + letter-spacing: 0.04em; +} +.re__browser-body { + background: var(--bg); + padding: 18px 22px; + height: 600px; + overflow: hidden; +} + +/* —— 6 段 section —— */ +.re__page { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-auto-rows: 1fr; + gap: 14px; + width: 100%; + height: 100%; +} + +.re__sec { + position: relative; + display: flex; + flex-direction: column; + background: var(--bg); + border: 1px solid var(--line-mid); + border-radius: 3px; + padding: 14px 18px; + gap: 10px; + overflow: hidden; + animation: reSecIn 720ms cubic-bezier(.2,.8,.2,1) backwards; + transition: + opacity 600ms cubic-bezier(.4,0,1,1), + filter 600ms cubic-bezier(.4,0,1,1), + transform 600ms cubic-bezier(.4,0,1,1); +} +@keyframes reSecIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: none; } +} + +.re__sec.is-pruned { + opacity: 0.32; + filter: grayscale(0.8); + transform: scale(0.985); +} + +.re__sec-head { + display: flex; + align-items: baseline; + gap: 10px; + font-family: var(--f-mono); + font-size: 12px; + letter-spacing: 0.22em; + text-transform: uppercase; + color: var(--fg-mute); +} +.re__sec-num { color: var(--accent); font-weight: 600; } +.re__sec-label { color: var(--fg); font-weight: 600; } +.re__sec-dot { width: 4px; height: 4px; border-radius: 50%; background: var(--fg-faint); } +.re__sec-cn { + font-family: var(--f-serif); + font-style: italic; + letter-spacing: 0.02em; + text-transform: none; + font-size: 16px; + color: var(--fg-soft); +} + +.re__sec-body { + flex: 1; + display: flex; + flex-direction: column; + gap: 8px; + align-items: stretch; + justify-content: center; +} +.re__sec-bar { + height: 8px; + background: var(--line-mid); + border-radius: 2px; +} +.re__sec-bar--w70 { width: 70%; } +.re__sec-bar--w50 { width: 50%; } + +/* —— Filler 内容(视觉上"塞满")—— */ +.re__filler { + display: flex; + width: 100%; + height: 100%; + animation: reFillerIn 520ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes reFillerIn { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: none; } +} + +.re__filler--hero { + flex-direction: column; + align-items: flex-start; + gap: 6px; + padding: 4px 0; +} +.re__filler-h { + font-family: var(--f-serif); + font-size: 24px; + font-weight: 500; + line-height: 1.1; + color: var(--fg); +} +.re__filler-sub { + font-family: var(--f-sans); + font-size: 13px; + line-height: 1.4; + color: var(--fg-mute); +} +.re__filler-cta { + margin-top: 4px; + padding: 4px 10px; + background: var(--accent); + color: var(--paper); + font-family: var(--f-mono); + font-size: 11px; + letter-spacing: 0.18em; + text-transform: uppercase; + border-radius: 2px; +} + +.re__filler--feat { + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-template-rows: repeat(2, 1fr); + gap: 6px; + width: 100%; + height: 100%; +} +.re__filler-card { + display: flex; + flex-direction: column; + gap: 4px; + padding: 6px 8px; + background: var(--bg-2); + border: 1px solid var(--line); + border-radius: 2px; +} +.re__filler-card-icon { + font-size: 14px; + color: var(--accent); +} +.re__filler-card-t { + height: 6px; + width: 60%; + background: var(--line-mid); + border-radius: 1px; +} +.re__filler-card-l { + height: 4px; + width: 90%; + background: var(--line); + border-radius: 1px; +} + +.re__filler--social { + flex-direction: row; + gap: 8px; +} +.re__filler-quote { + flex: 1; + display: flex; + flex-direction: column; + gap: 4px; + padding: 6px 8px; + background: var(--bg-2); + border: 1px solid var(--line); + border-radius: 2px; + font-family: var(--f-serif); + font-style: italic; + font-size: 11px; + line-height: 1.35; + color: var(--fg-soft); +} +.re__filler-quote-mark { + font-family: var(--f-serif); + font-size: 18px; + line-height: 1; + color: var(--accent); +} +.re__filler-quote-by { + font-family: var(--f-mono); + font-size: 9px; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--fg-faint); +} + +.re__filler--data { + display: grid; + grid-template-columns: repeat(4, 1fr); + align-items: center; + gap: 4px; + width: 100%; + font-family: var(--f-mono); + font-size: 10px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--fg-mute); + text-align: center; +} +.re__filler--data span { + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; +} +.re__filler--data b { + font-family: var(--f-serif); + font-style: italic; + font-weight: 400; + font-size: 22px; + color: var(--fg); + letter-spacing: 0; +} + +.re__filler--faq { + flex-direction: column; + gap: 4px; + font-family: var(--f-sans); + font-size: 12px; + color: var(--fg-soft); +} +.re__filler--faq span { + padding: 4px 8px; + background: var(--bg-2); + border: 1px solid var(--line); + border-radius: 2px; +} + +.re__filler--contact { + flex-direction: column; + gap: 4px; +} +.re__filler-input { + display: block; + width: 100%; + height: 14px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 2px; +} +.re__filler-btn { + align-self: flex-start; + margin-top: 2px; + padding: 4px 10px; + background: var(--accent); + color: var(--paper); + font-family: var(--f-mono); + font-size: 10px; + letter-spacing: 0.18em; + text-transform: uppercase; + border-radius: 2px; +} + +/* —— 红 × 修剪覆盖层 —— */ +.re__sec-prune { + position: absolute; + inset: 0; + pointer-events: none; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 360ms; +} +.re__sec.is-pruned .re__sec-prune { + opacity: 1; +} +.re__sec-prune-mark { + font-family: var(--f-serif); + font-style: italic; + font-size: 96px; + line-height: 1; + color: var(--crimson); + transform: scale(0); + animation: rePruneMarkIn 520ms cubic-bezier(.6,-0.05,.2,1.2) forwards; +} +.re__sec-prune-line { + position: absolute; + left: 0; + right: 0; + top: 50%; + height: 3px; + background: var(--crimson); + transform: translateY(-50%) scaleX(0); + transform-origin: left; + animation: rePruneLine 520ms cubic-bezier(.6,-0.05,.2,1.2) forwards; + opacity: 0.55; +} +@keyframes rePruneMarkIn { + from { transform: scale(0) rotate(-12deg); opacity: 0; } + to { transform: scale(1) rotate(-12deg); opacity: 0.92; } +} +@keyframes rePruneLine { + to { transform: translateY(-50%) scaleX(1); } +} + +/* 错开 6 段被砍的节奏 */ +.re__sec:nth-child(1).is-pruned .re__sec-prune-mark, +.re__sec:nth-child(1).is-pruned .re__sec-prune-line { animation-delay: 80ms; } +.re__sec:nth-child(2).is-pruned .re__sec-prune-mark, +.re__sec:nth-child(2).is-pruned .re__sec-prune-line { animation-delay: 200ms; } +.re__sec:nth-child(3).is-pruned .re__sec-prune-mark, +.re__sec:nth-child(3).is-pruned .re__sec-prune-line { animation-delay: 320ms; } +.re__sec:nth-child(4).is-pruned .re__sec-prune-mark, +.re__sec:nth-child(4).is-pruned .re__sec-prune-line { animation-delay: 440ms; } +.re__sec:nth-child(5).is-pruned .re__sec-prune-mark, +.re__sec:nth-child(5).is-pruned .re__sec-prune-line { animation-delay: 560ms; } +.re__sec:nth-child(6).is-pruned .re__sec-prune-mark, +.re__sec:nth-child(6).is-pruned .re__sec-prune-line { animation-delay: 680ms; } + +.re__page-verdict { + font-family: var(--f-serif); + font-size: 32px; + font-style: italic; + color: var(--fg-soft); + text-align: center; + display: flex; + align-items: center; + gap: 14px; +} +.re__page-verdict em { + font-style: italic; + color: var(--accent); +} +.re__page-verdict-mark { + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + color: var(--crimson); +} + +/* ===================== Scene PRINCIPLE (step 4) ===================== */ + +.re__princ { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 130px 140px 100px; + gap: 32px; + text-align: center; +} + +.re__princ-head { + font-family: var(--f-mono); + font-size: 16px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--accent); + margin: 0; +} + +.re__princ-line { + font-family: var(--f-serif); + font-weight: 400; + font-size: 88px; + line-height: 1.15; + margin: 0; + color: var(--fg); + max-width: 1500px; +} +.re__princ-line em { + font-style: italic; + color: var(--accent); + position: relative; +} +.re__princ-line em::after { + content: ''; + position: absolute; + left: 4%; + right: 4%; + bottom: 8px; + height: 8px; + background: var(--accent); + opacity: 0.22; + transform-origin: left; + animation: rePrincUnderline 1100ms 800ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes rePrincUnderline { + from { transform: scaleX(0); opacity: 0; } + to { transform: scaleX(1); opacity: 0.22; } +} + +.re__princ-rules { + display: flex; + flex-direction: column; + gap: 18px; + margin-top: 16px; + align-items: center; +} +.re__princ-rule { + display: flex; + align-items: baseline; + gap: 18px; + padding: 14px 32px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 999px; + font-family: var(--f-serif); + font-size: 32px; + color: var(--fg); + box-shadow: 0 12px 24px -16px oklch(0 0 0 / 0.18); +} +.re__princ-q { + color: var(--fg-soft); + font-style: italic; +} +.re__princ-arrow { + font-family: var(--f-mono); + font-style: normal; + font-size: 24px; + color: var(--fg-faint); +} +.re__princ-a { + color: var(--fg); + font-style: italic; + font-weight: 500; +} +.re__princ-a em { + font-style: italic; + color: var(--accent); +} + +.re__princ-excerpt { + width: 100%; + max-width: 1100px; + margin-top: 12px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-left: 3px solid var(--accent); + border-radius: 2px; + padding: 18px 28px 22px; + text-align: left; +} +.re__princ-excerpt-head { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.18em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 10px; +} +.re__princ-excerpt-body { + display: flex; + align-items: flex-start; + gap: 14px; +} +.re__princ-excerpt-gt { + font-family: var(--f-mono); + font-size: 28px; + color: var(--accent); +} +.re__princ-excerpt-text { + font-family: var(--f-mono); + font-size: 20px; + line-height: 1.55; + color: var(--fg); +} +.re__princ-excerpt-text em { + font-style: normal; + color: var(--accent); + background: linear-gradient(180deg, transparent 70%, oklch(0.860 0.060 60 / 0.6) 70%); + padding: 0 2px; +} + +/* ===================== Scene CLOSE (step 5) ===================== */ + +.re__close { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 20px; + padding: 140px; + text-align: center; +} + +.re__close-line { + font-family: var(--f-serif); + font-weight: 400; + font-size: 110px; + line-height: 1.15; + margin: 0; + color: var(--fg); + max-width: 1500px; +} +.re__close-line--alt { + color: var(--fg); +} +.re__close-line em { + font-style: italic; + color: var(--accent); +} + +.re__close-foot { + margin-top: 48px; + display: flex; + align-items: center; + gap: 24px; + font-family: var(--f-mono); + font-size: 22px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-mute); +} +.re__close-foot-eq { + color: var(--accent); + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + letter-spacing: 0; +} diff --git a/web/src/chapters/08-restraint/index.tsx b/web/src/chapters/08-restraint/index.tsx new file mode 100644 index 0000000..aa66ac8 --- /dev/null +++ b/web/src/chapters/08-restraint/index.tsx @@ -0,0 +1,303 @@ +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import { SceneFade } from '../../shared/SceneFade'; +import './Restraint.css'; + +/** + * Chapter 08 · 第五部分:内容克制 + * + * 口播原顺序(严格对齐): + * 1. "提示词里引了乔布斯一句经典的话:'一千个 No 换一个 Yes'。" + * 2. "AI 做设计有个毛病——恨不得把空间塞满。" + * "Hero、特性、评价、数据、FAQ、联系方式…一股脑全上了,但每块都很平庸。" + * 3. "Claude Design 的态度很明确:每个元素得证明自己为什么该在那。" + * 4. "想加东西?先问用户。页面看着空?那是排版的问题,用留白来解决,别靠塞东西。" + * 5. "一个大胆的留白,比十个凑数的板块有表现力得多。" + * + * 节奏(6 步 / step 0..5): + * 0 hero "1000 No · 1 Yes" 大字开场 + Steve Jobs + 原文 prompt(L75) + * 1 转场到 AI 落地页:6 段 section 线框依次出现 + * 2 filler 内容塞满("恨不得把空间塞满") + * 3 红 × 一段段砍掉("每个元素得证明自己") + * 4 留白后中央立原则: "想加?先问。空?用留白" + earn-its-place 提示词 + * 5 收尾: "一个大胆的留白 > 十个凑数的板块" + */ + +interface Section { + id: string; + label: string; + cn: string; +} + +const SECTIONS: Section[] = [ + { id: 'hero', label: 'HERO', cn: '主视觉' }, + { id: 'feat', label: 'FEATURES', cn: '6 大特性' }, + { id: 'social', label: 'TESTIMONIALS', cn: '客户评价' }, + { id: 'data', label: 'DATA', cn: '数据展示' }, + { id: 'faq', label: 'FAQ', cn: '常见问题' }, + { id: 'contact', label: 'CONTACT', cn: '联系方式' }, +]; + +function SectionBlock({ + s, + index, + filled, + pruned, +}: { + s: Section; + index: number; + filled: boolean; + pruned: boolean; +}) { + return ( +
+
+ {String(index + 1).padStart(2, '0')} + {s.label} + + {s.cn} +
+
+ {!filled && ( + <> + + + + )} + {filled && s.id === 'hero' && ( +
+ Build the Future. Today. + The all-in-one platform for the modern team — fast, simple, powerful. + Get Started → +
+ )} + {filled && s.id === 'feat' && ( +
+ {[0, 1, 2, 3, 4, 5].map((i) => ( + + + + + + ))} +
+ )} + {filled && s.id === 'social' && ( +
+ {[0, 1, 2].map((i) => ( + + " + Best product I've ever used. 10/10. + — User #{i + 1} + + ))} +
+ )} + {filled && s.id === 'data' && ( +
+ 10k+ users + 99.9% uptime + 4.9★ rating + +42% growth +
+ )} + {filled && s.id === 'faq' && ( +
+ + How does it work? + + Is there a free trial? + + Can I cancel anytime? +
+ )} + {filled && s.id === 'contact' && ( +
+ + + Send Message +
+ )} +
+ +
+ × + +
+
+ ); +} + +function Restraint({ localStep }: ChapterContext) { + const at = (n: number) => localStep >= n; + void at; + + const sceneJobs = localStep <= 0; + const scenePage = localStep >= 1 && localStep <= 3; + const scenePrinc = localStep === 4; + const sceneClose = localStep >= 5; + + const filled = localStep >= 2; + const pruned = localStep >= 3; + + return ( +
+ {/* ════════ Scene JOBS(step 0)—— 乔布斯名言开场 ════════ */} + +
+ + —— STEVE JOBS · 提示词原文引用 + + +
+ + 1000 + No + + + + 换 + + + + 1 + Yes + +
+ + + " + One thousand no's for every yes. + " + + + + [ + SYSTEM PROMPT + · + L77 + ] + +
+
+ + {/* ════════ Scene PAGE(step 1..3)—— AI 把 6 段塞满 → 砍 ════════ */} + +
+ + A TYPICAL "AI" LANDING PAGE + / + + {!filled && 'AI 一上来就把 6 段全摆好了 ——'} + {filled && !pruned && '然后把每一格都塞满 ——'} + {pruned && '一一拷问:你为什么在这?'} + + + +
+
+ + + + claude-design.demo / fake-landing-page +
+
+
+ {SECTIONS.map((s, i) => ( + + ))} +
+
+
+ + {pruned && ( + + × + 每一块都"还行",加在一起 —— 还是平庸 + + )} +
+
+ + {/* ════════ Scene PRINCIPLE(step 4)—— 立原则 ════════ */} + +
+ + Claude Design 的态度 —— + + + + 每个元素,得证明自己为什么该在那。 + + +
+ + 想加东西? + + 先问用户 + + + 页面看着空? + + 留白解决,不是塞内容 + +
+ + +
+ [ + SYSTEM PROMPT + · + L75 + ] +
+
+ > + + Never pad a design with{' '} + placeholder text, dummy sections{' '} + just to fill space. Every element should earn its place. + +
+
+
+
+ + {/* ════════ Scene CLOSE(step 5)—— 大胆留白 ════════ */} + +
+ + 一个大胆的留白, + + + + 比十个凑数的板块更有表现力。 + + + + 留白 + = + 设计 + +
+
+
+ ); +} + +const def: ChapterDef = { + id: 'restraint', + title: '第五部分 · 内容克制', + eyebrow: '08', + steps: 6, + theme: 'light', + Component: Restraint, +}; + +export default def; diff --git a/web/src/chapters/09-verification/Verification.css b/web/src/chapters/09-verification/Verification.css new file mode 100644 index 0000000..5120227 --- /dev/null +++ b/web/src/chapters/09-verification/Verification.css @@ -0,0 +1,636 @@ +/* ========================================================= + Chapter 09 · 第六部分 · 验证闭环 + ink 主题 · prompt 原文 → 主 Agent 自循环 → fork → 检查 → 收尾 + ========================================================= */ + +.vf { + position: absolute; + inset: 0; + background: var(--bg); + color: var(--fg); + font-family: var(--f-sans); + overflow: hidden; +} + +.vf__src-bracket { color: var(--accent); font-weight: 600; } +.vf__src-label { color: var(--fg); font-weight: 600; letter-spacing: 0.22em; } +.vf__src-sep { color: var(--fg-faint); } +.vf__src-line { color: var(--accent); font-weight: 600; } + +/* ===================== Scene INTRO (step 0) ===================== */ + +.vf__intro { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 140px; + gap: 32px; + text-align: center; +} + +.vf__intro-tag { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.22em; + text-transform: uppercase; + color: var(--fg-mute); +} + +.vf__intro-code { + display: flex; + align-items: flex-start; + gap: 28px; + padding: 32px 44px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-left: 3px solid var(--accent); + border-radius: 2px; + font-family: var(--f-mono); + font-size: 30px; + line-height: 1.55; + text-align: left; + max-width: 1400px; + color: var(--fg-soft); + box-shadow: 0 24px 48px -28px oklch(0 0 0 / 0.45); +} +.vf__intro-code-num { + font-size: 36px; + color: var(--accent); + font-weight: 500; + line-height: 1.4; +} +.vf__intro-code-text em { + font-style: normal; + color: var(--fg); +} +.vf__intro-fn { + font-style: normal; + color: var(--accent) !important; + background: oklch(0.700 0.170 42 / 0.14); + padding: 0 8px; + border-radius: 2px; + font-weight: 500; +} + +.vf__intro-title { + font-family: var(--f-serif); + font-weight: 400; + font-size: 96px; + line-height: 1.1; + margin: 0; + color: var(--fg); + max-width: 1500px; +} +.vf__intro-title em { + font-style: italic; + color: var(--accent); +} + +/* ===================== Scene AGENT (step 1) ===================== */ + +.vf__agent-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 130px 140px 100px; + gap: 40px; +} + +.vf__agent-cap { + font-family: var(--f-serif); + font-size: 32px; + font-style: italic; + color: var(--fg-soft); +} +.vf__agent-cap em { + color: var(--accent); +} + +.vf__board { + position: relative; + width: 100%; + max-width: 1400px; + height: 460px; + display: flex; + align-items: center; + justify-content: center; +} +.vf__board--fork { + justify-content: space-between; + padding: 0 60px; +} + +/* —— Agent 节点 —— */ +.vf__node { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + padding: 28px 40px 24px; + background: var(--bg-2); + border: 2px solid var(--accent); + border-radius: 4px; + min-width: 320px; + box-shadow: 0 24px 48px -22px oklch(0 0 0 / 0.5); + animation: vfNodeIn 720ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes vfNodeIn { + from { opacity: 0; transform: translateY(20px) scale(0.92); } + to { opacity: 1; transform: none; } +} +.vf__node--small { + min-width: 280px; + padding: 20px 30px 18px; + border-color: var(--line-strong); + box-shadow: 0 18px 32px -22px oklch(0 0 0 / 0.4); +} +.vf__node--verifier { + border: 2px dashed var(--accent); + animation: vfNodeIn 720ms 600ms cubic-bezier(.6,-0.05,.2,1.2) backwards; + box-shadow: 0 24px 48px -22px oklch(0.700 0.170 42 / 0.4); +} +.vf__node-tag { + font-family: var(--f-mono); + font-size: 12px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--accent); + font-weight: 600; +} +.vf__node-tag--alt { + color: var(--accent); +} +.vf__node-title { + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + line-height: 1.05; + color: var(--fg); + margin: 4px 0; +} +.vf__node-meta { + font-family: var(--f-mono); + font-size: 13px; + color: var(--fg-mute); + letter-spacing: 0.04em; +} +.vf__node-fresh { + color: oklch(0.770 0.140 145); + font-weight: 600; + letter-spacing: 0.06em; +} + +/* —— 自循环 SVG —— */ +.vf__loop { + position: absolute; + inset: -60px; + width: calc(100% + 120px); + height: calc(100% + 120px); + pointer-events: none; +} +.vf__loop-ring { + transform-origin: 100px 100px; + animation: vfLoopSpin 5s linear infinite; +} +@keyframes vfLoopSpin { + from { transform: rotate(0); } + to { transform: rotate(360deg); } +} + +/* —— 自言自语气泡 —— */ +.vf__bubble { + position: absolute; + top: -68px; + left: 50%; + transform: translateX(-50%); + padding: 10px 18px; + background: var(--bg); + border: 1px solid var(--line-mid); + border-radius: 999px; + font-family: var(--f-serif); + font-style: italic; + font-size: 22px; + color: var(--fg-soft); + white-space: nowrap; + animation: vfBubbleIn 620ms 800ms cubic-bezier(.2,.8,.2,1) backwards; +} +.vf__bubble::after { + content: ''; + position: absolute; + bottom: -7px; + left: 50%; + transform: translateX(-50%) rotate(45deg); + width: 10px; + height: 10px; + background: var(--bg); + border-right: 1px solid var(--line-mid); + border-bottom: 1px solid var(--line-mid); +} +@keyframes vfBubbleIn { + from { opacity: 0; transform: translate(-50%, -8px); } + to { opacity: 1; transform: translate(-50%, 0); } +} + +.vf__agent-verdict { + display: flex; + align-items: center; + gap: 14px; + font-family: var(--f-serif); + font-size: 30px; + font-style: italic; + color: var(--fg-soft); +} +.vf__agent-verdict em { + font-style: italic; + color: var(--crimson); +} +.vf__agent-verdict-x { + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + color: var(--crimson); +} + +/* ===================== Scene FORK (step 2) ===================== */ + +.vf__fork-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 130px 140px 100px; + gap: 32px; +} + +.vf__fork-cap { + display: flex; + align-items: baseline; + gap: 18px; + font-family: var(--f-serif); + font-size: 30px; + font-style: italic; + color: var(--fg-soft); +} +.vf__fork-cap em { + color: var(--accent); +} +.vf__fork-cap-fn { + font-family: var(--f-mono); + font-style: normal; + font-size: 26px; + color: var(--accent); + background: oklch(0.700 0.170 42 / 0.14); + padding: 4px 10px; + border-radius: 2px; +} +.vf__fork-cap-arrow { + font-family: var(--f-mono); + font-style: normal; + color: var(--fg-faint); +} + +.vf__fork-link { + position: absolute; + left: 50%; + top: 50%; + width: 600px; + height: 220px; + transform: translate(-50%, -50%); + pointer-events: none; +} +.vf__fork-link-branch { + stroke-dashoffset: 200; + animation: vfForkDraw 1100ms 380ms cubic-bezier(.2,.8,.2,1) forwards; +} +@keyframes vfForkDraw { + to { stroke-dashoffset: 0; } +} + +/* —— 子 Agent 模拟 iframe —— */ +.vf__node-iframe { + margin-top: 10px; + width: 240px; + background: var(--bg); + border: 1px solid var(--line-mid); + border-radius: 2px; + overflow: hidden; +} +.vf__node-iframe-bar { + display: flex; + align-items: center; + gap: 5px; + padding: 6px 10px; + background: oklch(0.355 0.014 60); + border-bottom: 1px solid var(--line-mid); +} +.vf__node-iframe-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--fg-faint); +} +.vf__node-iframe-url { + margin-left: 8px; + font-family: var(--f-mono); + font-size: 10px; + color: var(--fg-mute); + letter-spacing: 0.04em; +} +.vf__node-iframe-body { + display: block; + position: relative; + height: 80px; + background: + repeating-linear-gradient( + 45deg, + oklch(0.965 0.018 78 / 0.04) 0 6px, + transparent 6px 12px); + overflow: hidden; +} +.vf__node-iframe-flash { + position: absolute; + inset: 0; + background: linear-gradient( + 90deg, + transparent 0%, + oklch(0.700 0.170 42 / 0.45) 50%, + transparent 100%); + transform: translateX(-100%); + animation: vfFlash 1800ms 1100ms ease-in-out infinite; +} +@keyframes vfFlash { + 0% { transform: translateX(-100%); } + 60% { transform: translateX(100%); } + 100% { transform: translateX(100%); } +} + +.vf__fork-verdict { + font-family: var(--f-serif); + font-style: italic; + font-size: 26px; + color: var(--fg-mute); +} +.vf__fork-verdict em { + font-style: italic; + color: var(--accent); +} + +/* ===================== Scene CHECK (step 3) ===================== */ + +.vf__check-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 130px 140px 100px; + gap: 28px; +} + +.vf__check-cap { + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--accent); +} + +.vf__check-panel { + width: 100%; + max-width: 1280px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + overflow: hidden; + box-shadow: 0 32px 60px -32px oklch(0 0 0 / 0.5); +} +.vf__check-panel-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 14px 22px; + background: oklch(0.355 0.014 60); + border-bottom: 1px solid var(--line-mid); +} +.vf__check-panel-dot { + width: 9px; + height: 9px; + border-radius: 50%; + background: oklch(0.500 0.020 60); +} +.vf__check-panel-name { + margin-left: 10px; + font-family: var(--f-mono); + font-size: 14px; + color: var(--fg-mute); + letter-spacing: 0.06em; +} +.vf__check-panel-status { + margin-left: auto; + font-family: var(--f-mono); + font-size: 12px; + letter-spacing: 0.32em; + color: var(--accent); + animation: vfStatusBlink 1.4s ease-in-out infinite; +} +@keyframes vfStatusBlink { + 0%, 100% { opacity: 0.4; } + 50% { opacity: 1; } +} + +.vf__check-list { + display: flex; + flex-direction: column; +} + +.vf__check-row { + display: grid; + grid-template-columns: 56px 240px auto 1fr 60px; + align-items: center; + gap: 18px; + padding: 18px 28px; + border-bottom: 1px dashed var(--line); + font-family: var(--f-mono); + font-size: 22px; + color: var(--fg); + opacity: 0; + animation: vfCheckIn 520ms cubic-bezier(.2,.8,.2,1) forwards; +} +.vf__check-row:last-child { border-bottom: none; } +@keyframes vfCheckIn { + from { opacity: 0; transform: translateX(-12px); } + to { opacity: 1; transform: none; } +} + +.vf__check-num { + color: var(--fg-faint); + letter-spacing: 0.08em; +} +.vf__check-label { + color: var(--fg); + letter-spacing: 0.18em; + font-weight: 600; +} +.vf__check-cn { + font-family: var(--f-serif); + font-style: italic; + font-size: 20px; + color: var(--fg-mute); + letter-spacing: 0; +} +.vf__check-bar { + position: relative; + height: 4px; + background: var(--line); + border-radius: 2px; + overflow: hidden; +} +.vf__check-bar::after { + content: ''; + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 0; + background: var(--accent); + animation: vfCheckBar 700ms cubic-bezier(.2,.8,.2,1) forwards; +} +.vf__check-row:nth-child(1) .vf__check-bar::after { animation-delay: 320ms; } +.vf__check-row:nth-child(2) .vf__check-bar::after { animation-delay: 540ms; } +.vf__check-row:nth-child(3) .vf__check-bar::after { animation-delay: 760ms; } +.vf__check-row:nth-child(4) .vf__check-bar::after { animation-delay: 980ms; } +@keyframes vfCheckBar { + to { width: 100%; } +} + +.vf__check-mark { + position: relative; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; +} +.vf__check-mark-spin { + position: absolute; + inset: 0; + border: 2px solid var(--line-mid); + border-top-color: var(--accent); + border-radius: 50%; + animation: vfSpin 700ms linear infinite; +} +.vf__check-mark-tick { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + font-family: var(--f-serif); + font-style: italic; + font-size: 28px; + color: oklch(0.770 0.140 145); + opacity: 0; + animation: vfTickIn 320ms cubic-bezier(.6,-0.05,.2,1.2) forwards; +} +@keyframes vfSpin { + to { transform: rotate(360deg); } +} +@keyframes vfTickIn { + from { opacity: 0; transform: scale(0.4); } + to { opacity: 1; transform: scale(1); } +} + +/* 错峰:spinner 跑完才显示对勾 */ +.vf__check-row:nth-child(1) .vf__check-mark-spin { animation: vfSpin 700ms linear 1; } +.vf__check-row:nth-child(2) .vf__check-mark-spin { animation: vfSpin 700ms 220ms linear 1; } +.vf__check-row:nth-child(3) .vf__check-mark-spin { animation: vfSpin 700ms 440ms linear 1; } +.vf__check-row:nth-child(4) .vf__check-mark-spin { animation: vfSpin 700ms 660ms linear 1; } +.vf__check-row:nth-child(1) .vf__check-mark-spin { animation-fill-mode: forwards; } +.vf__check-row .vf__check-mark-spin { + animation-fill-mode: forwards; +} +.vf__check-row:nth-child(1) .vf__check-mark-tick { animation-delay: 1020ms; } +.vf__check-row:nth-child(2) .vf__check-mark-tick { animation-delay: 1240ms; } +.vf__check-row:nth-child(3) .vf__check-mark-tick { animation-delay: 1460ms; } +.vf__check-row:nth-child(4) .vf__check-mark-tick { animation-delay: 1680ms; } + +/* spinner 在对勾出现时淡出 */ +.vf__check-row:nth-child(1) .vf__check-mark-spin { animation: vfSpin 700ms linear 1, vfSpinOut 240ms 1020ms forwards; } +.vf__check-row:nth-child(2) .vf__check-mark-spin { animation: vfSpin 700ms 220ms linear 1, vfSpinOut 240ms 1240ms forwards; } +.vf__check-row:nth-child(3) .vf__check-mark-spin { animation: vfSpin 700ms 440ms linear 1, vfSpinOut 240ms 1460ms forwards; } +.vf__check-row:nth-child(4) .vf__check-mark-spin { animation: vfSpin 700ms 660ms linear 1, vfSpinOut 240ms 1680ms forwards; } +@keyframes vfSpinOut { + to { opacity: 0; transform: scale(0.6); } +} + +.vf__check-foot { + display: flex; + align-items: center; + gap: 14px; + padding: 16px 28px; + background: oklch(0.355 0.014 60); + border-top: 1px solid var(--line-mid); + font-family: var(--f-mono); + font-size: 14px; + color: var(--fg-mute); + letter-spacing: 0.04em; +} +.vf__check-foot-tag { + padding: 4px 10px; + border-radius: 2px; + letter-spacing: 0.32em; + font-weight: 600; +} +.vf__check-foot-tag--pass { + color: oklch(0.770 0.140 145); + background: oklch(0.770 0.140 145 / 0.14); + border: 1px solid oklch(0.770 0.140 145 / 0.45); +} + +/* ===================== Scene CLOSE (step 4) ===================== */ + +.vf__close { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 12px; + padding: 140px; +} +.vf__close-l1, +.vf__close-l2 { + font-family: var(--f-serif); + font-weight: 400; + font-size: 128px; + line-height: 1.05; + margin: 0; + color: var(--fg); +} +.vf__close-l1 em, +.vf__close-l2 em { + font-style: italic; + color: var(--accent); +} +.vf__close-cap { + margin-top: 32px; + font-family: var(--f-serif); + font-style: italic; + font-size: 30px; + color: var(--fg-mute); +} diff --git a/web/src/chapters/09-verification/index.tsx b/web/src/chapters/09-verification/index.tsx new file mode 100644 index 0000000..c8bb31e --- /dev/null +++ b/web/src/chapters/09-verification/index.tsx @@ -0,0 +1,277 @@ +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import { SceneFade } from '../../shared/SceneFade'; +import './Verification.css'; + +/** + * Chapter 09 · 第六部分:验证闭环 + * + * 口播原顺序(严格对齐): + * 1. "第六个点,验证。" + * 2. "在开发完成后,它会 Fork 出一个独立的子 Agent,然后对当前完成的网页做全面检查。" + * 3. "同一个 Agent 检查自己的输出的时候,天然会倾向于觉得没问题。" + * 4. "换一个全新的上下文,这种'自我感觉良好'就容易被打破。" + * + * 原文(L22): + * "Finish: call `done` to surface the file to the user and check it loads cleanly. + * If errors, fix and `done` again. If clean, call `fork_verifier_agent`." + * + * 节奏(5 步 / step 0..4): + * 0 hero · 提示词原文 (L22) + 大字 "验证 —— 不信任自己的输出" + * 1 主 Agent 节点出现,自带 self-loop("我做的没问题吧?")→ 标 "确认偏误" + * 2 fork → 子 Agent 节点弹出,标 "fresh context" + * 3 子 Agent 跑 4 项检查:SCREENSHOT / CONSOLE / LAYOUT / JS PROBE 错峰打勾 + * 4 收尾大字:"换个新脑子 / 才能跳出 自我感觉良好" + */ + +interface Check { + id: string; + label: string; + cn: string; +} + +const CHECKS: Check[] = [ + { id: 'shot', label: 'SCREENSHOT', cn: '截图比对' }, + { id: 'cons', label: 'CONSOLE LOGS', cn: '控制台错误' }, + { id: 'lay', label: 'LAYOUT', cn: '布局偏移' }, + { id: 'js', label: 'JS PROBE', cn: 'DOM 探测' }, +]; + +function Verification({ localStep }: ChapterContext) { + const at = (n: number) => localStep >= n; + + const sceneIntro = localStep <= 0; + const sceneAgent = localStep === 1; + const sceneFork = localStep === 2; + const sceneCheck = localStep === 3; + const sceneClose = localStep >= 4; + + return ( +
+ {/* ════════ Scene INTRO(step 0)════════ */} + +
+ + [ + SYSTEM PROMPT + · + L22 + ] + + + + 5. + + Finish: call done.
+ If errors, fix and done again.
+ If clean, call fork_verifier_agent(). +
+
+ + + 验证 —— 不信任自己的输出 + +
+
+ + {/* ════════ Scene AGENT(step 1)—— 主 Agent + 自循环 ════════ */} + +
+ + 一个 Agent —— 检查自己会发生什么? + + +
+ {/* 主 Agent 节点 */} +
+
MAIN AGENT
+
opus 4.7
+
role · designer · L01
+ + {/* 自循环箭头(SVG) */} + + + + + + + + + + {/* 自言自语气泡 */} +
+ "我做的应该没问题吧?" +
+
+
+ + + × + 天然会倾向于觉得没问题 —— 这就是确认偏误 + +
+
+ + {/* ════════ Scene FORK(step 2)—— fork 出子 Agent ════════ */} + +
+ + fork_verifier_agent() + + 另开一个 新脑子 + + +
+ {/* 主 Agent(左侧) */} +
+
MAIN AGENT
+
opus 4.7
+
已完成 · 等待审稿
+
+ + {/* fork 连线 */} + + + + + + + {/* 主线 */} + + {/* 分叉线(弹性曲线) */} + + {/* 节点圆点 */} + + + + {/* 子 Agent(右上) */} +
+
VERIFIER AGENT
+
subagent · 0x9c
+
+ ● fresh context +
+
+ + + + + about:blank + + + + +
+
+
+ + + 独立 iframe · 独立上下文 · 跑全面检查后再回报 + +
+
+ + {/* ════════ Scene CHECK(step 3)—— 子 Agent 跑检查清单 ════════ */} + +
+ + 子 Agent · 全面检查 + + +
+
+ + + + verifier · subagent · 0x9c + RUNNING… +
+
+ {CHECKS.map((c, i) => ( +
+ 0{i + 1} + {c.label} + / {c.cn} + + + + + +
+ ))} +
+
+ PASS + silent on pass · 不打断主 Agent +
+
+
+
+ + {/* ════════ Scene CLOSE(step 4)════════ */} + +
+ + 换个新脑子 + + + 才能跳出 自我感觉良好 + + + AI 也需要审稿人 + +
+
+
+ ); +} + +const def: ChapterDef = { + id: 'verification', + title: '第六部分 · 验证闭环', + eyebrow: '09', + steps: 5, + theme: 'ink', + Component: Verification, +}; + +export default def; diff --git a/web/src/chapters/10-to-skill/ToSkill.css b/web/src/chapters/10-to-skill/ToSkill.css new file mode 100644 index 0000000..0697d9b --- /dev/null +++ b/web/src/chapters/10-to-skill/ToSkill.css @@ -0,0 +1,631 @@ +/* ========================================================= + Chapter 10 · 过渡到 Skill + light 主题 · 回顾 → 但 → 三号被封 → 好消息 → Skill 卡 → 收尾 + ========================================================= */ + +.ts { + position: absolute; + inset: 0; + background: var(--bg); + color: var(--fg); + font-family: var(--f-sans); + overflow: hidden; +} + +.ts__src-bracket { color: var(--accent); font-weight: 600; } +.ts__src-label { color: var(--fg); font-weight: 600; letter-spacing: 0.22em; } + +/* ===================== Scene RECAP (step 0) ===================== */ + +.ts__recap { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 130px 140px 100px; + gap: 56px; + text-align: center; +} + +.ts__recap-title { + font-family: var(--f-serif); + font-weight: 400; + font-size: 112px; + line-height: 1.1; + margin: 0; + color: var(--fg); +} +.ts__recap-title em { + font-style: italic; + color: var(--accent); +} + +.ts__recap-list { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 14px 28px; + width: 100%; + max-width: 1100px; +} + +.ts__recap-item { + display: flex; + align-items: center; + gap: 14px; + padding: 14px 22px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-left: 3px solid var(--accent); + border-radius: 2px; + font-family: var(--f-serif); + font-size: 26px; + color: var(--fg); + opacity: 0; + animation: tsRecapIn 620ms cubic-bezier(.2,.8,.2,1) forwards; +} +@keyframes tsRecapIn { + from { opacity: 0; transform: translateY(12px); } + to { opacity: 1; transform: none; } +} +.ts__recap-num { + font-family: var(--f-mono); + font-size: 18px; + letter-spacing: 0.12em; + color: var(--fg-faint); +} +.ts__recap-name { + flex: 1; + font-style: italic; + text-align: left; +} +.ts__recap-tick { + font-family: var(--f-serif); + font-style: italic; + font-size: 28px; + color: var(--accent); +} + +/* ===================== Scene PROBLEM (step 1) ===================== */ + +.ts__prob { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 24px; + padding: 140px; +} + +.ts__prob-but { + font-family: var(--f-serif); + font-weight: 400; + font-size: 200px; + line-height: 0.95; + margin: 0; + color: var(--fg); + font-style: italic; +} + +.ts__prob-line { + font-family: var(--f-serif); + font-weight: 400; + font-size: 88px; + line-height: 1.15; + margin: 0; + color: var(--fg); +} +.ts__prob-line em { + font-style: italic; + color: var(--crimson); + position: relative; +} +.ts__prob-line em::after { + content: ''; + position: absolute; + left: 4%; + right: 4%; + bottom: 8px; + height: 8px; + background: var(--crimson); + opacity: 0.18; + transform-origin: left; + animation: tsProbUnderline 1100ms 1100ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes tsProbUnderline { + from { transform: scaleX(0); opacity: 0; } + to { transform: scaleX(1); opacity: 0.18; } +} + +.ts__prob-meta { + margin-top: 32px; + display: flex; + align-items: center; + gap: 18px; + font-family: var(--f-mono); + font-size: 18px; + letter-spacing: 0.24em; + text-transform: uppercase; + color: var(--fg-mute); +} +.ts__prob-meta-dot { + width: 4px; + height: 4px; + border-radius: 50%; + background: var(--fg-faint); +} + +/* ===================== Scene BANNED (step 2) ===================== */ + +.ts__banned-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 120px 100px 90px; + gap: 40px; +} + +.ts__banned-cap { + font-family: var(--f-serif); + font-style: italic; + font-size: 32px; + color: var(--fg-soft); + text-align: center; +} +.ts__banned-cap em { + color: var(--crimson); + font-style: italic; + font-weight: 500; +} + +.ts__banned-row { + display: flex; + gap: 36px; + align-items: flex-start; + justify-content: center; +} + +.ts__card { + position: relative; + width: 380px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + overflow: hidden; + box-shadow: 0 24px 48px -28px oklch(0 0 0 / 0.25); + opacity: 0; + transform: translateY(0); + animation: tsCardFall 1100ms cubic-bezier(.6,-0.05,.2,1.2) forwards; +} +.ts__card--n1 { transform-origin: 70% 100%; } +.ts__card--n2 { transform-origin: 50% 100%; margin-top: 32px; } +.ts__card--n3 { transform-origin: 30% 100%; margin-top: 64px; } +@keyframes tsCardFall { + 0% { opacity: 0; transform: translateY(-50px) rotate(-2deg); } + 20% { opacity: 1; transform: translateY(0) rotate(0); } + 60% { opacity: 1; transform: translateY(0) rotate(0); } + 100% { opacity: 0.7; transform: translateY(28px) rotate(var(--fall-rot, 4deg)); } +} +.ts__card--n1 { --fall-rot: -6deg; } +.ts__card--n2 { --fall-rot: 3deg; } +.ts__card--n3 { --fall-rot: -2deg; } + +.ts__card-bar { + display: flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + background: oklch(0.910 0.020 78); + border-bottom: 1px solid var(--line-mid); +} +.ts__card-bar-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: oklch(0.820 0.030 78); +} +.ts__card-bar-dot:nth-child(1) { background: oklch(0.730 0.180 28); } +.ts__card-bar-dot:nth-child(2) { background: oklch(0.830 0.150 80); } +.ts__card-bar-dot:nth-child(3) { background: oklch(0.770 0.140 145); } +.ts__card-bar-name { + margin-left: 10px; + font-family: var(--f-mono); + font-size: 12px; + color: var(--fg-mute); + letter-spacing: 0.04em; +} + +.ts__card-body { + display: flex; + align-items: center; + gap: 18px; + padding: 22px 24px 26px; +} +.ts__card-avatar { + width: 60px; + height: 60px; + border-radius: 999px; + background: oklch(0.700 0.170 42 / 0.18); + color: var(--accent); + display: flex; + align-items: center; + justify-content: center; + font-family: var(--f-serif); + font-style: italic; + font-size: 32px; +} +.ts__card-info { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; +} +.ts__card-name { + font-family: var(--f-serif); + font-size: 24px; + color: var(--fg); +} +.ts__card-mail { + font-family: var(--f-mono); + font-size: 13px; + color: var(--fg-mute); + letter-spacing: 0.04em; +} +.ts__card-plan { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 12px; + color: var(--fg-faint); + letter-spacing: 0.06em; + margin-top: 4px; +} +.ts__card-plan-tag { + padding: 2px 8px; + background: var(--accent); + color: var(--paper); + letter-spacing: 0.18em; + border-radius: 2px; + font-weight: 600; +} + +/* —— BANNED 印章 —— */ +.ts__stamp { + position: absolute; + top: 50%; + left: 50%; + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + padding: 16px 32px 12px; + border: 4px solid var(--crimson); + border-radius: 4px; + color: var(--crimson); + font-family: var(--f-mono); + letter-spacing: 0.08em; + text-transform: uppercase; + background: oklch(0.560 0.200 22 / 0.06); + transform: translate(-50%, -50%) rotate(-12deg) scale(0); + opacity: 0; + animation: tsStampIn 480ms cubic-bezier(.6,-0.05,.2,1.2) forwards; +} +.ts__card--n1 .ts__stamp { animation-delay: 600ms; } +.ts__card--n2 .ts__stamp { animation-delay: 920ms; } +.ts__card--n3 .ts__stamp { animation-delay: 1240ms; } +@keyframes tsStampIn { + from { opacity: 0; transform: translate(-50%, -50%) rotate(-12deg) scale(1.6); } + to { opacity: 1; transform: translate(-50%, -50%) rotate(-12deg) scale(1); } +} +.ts__stamp-text { + font-family: var(--f-mono); + font-size: 36px; + font-weight: 700; + letter-spacing: 0.16em; +} +.ts__stamp-sub { + font-family: var(--f-mono); + font-size: 11px; + letter-spacing: 0.18em; + opacity: 0.85; +} + +.ts__banned-foot { + display: flex; + align-items: center; + gap: 14px; + font-family: var(--f-serif); + font-style: italic; + font-size: 28px; + color: var(--fg-mute); +} +.ts__banned-foot em { + color: var(--crimson); + font-style: italic; + font-weight: 500; +} +.ts__banned-foot-x { + font-family: var(--f-serif); + font-style: italic; + font-size: 32px; + color: var(--crimson); +} + +/* ===================== Scene PIVOT (step 3) ===================== */ + +.ts__pivot { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 16px; + padding: 140px; +} + +.ts__pivot-eyebrow { + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + color: var(--fg-mute); +} + +.ts__pivot-good { + font-family: var(--f-serif); + font-weight: 400; + font-size: 168px; + line-height: 1; + margin: 0; + color: var(--fg); +} +.ts__pivot-good em { + font-style: italic; + color: var(--accent); + position: relative; +} +.ts__pivot-good em::after { + content: ''; + position: absolute; + left: 4%; + right: 4%; + bottom: 12px; + height: 12px; + background: var(--accent); + opacity: 0.22; + transform-origin: left; + animation: tsPivotUnderline 1100ms 600ms cubic-bezier(.2,.8,.2,1) backwards; +} +@keyframes tsPivotUnderline { + from { transform: scaleX(0); opacity: 0; } + to { transform: scaleX(1); opacity: 0.22; } +} + +.ts__pivot-line { + font-family: var(--f-serif); + font-weight: 400; + font-size: 80px; + line-height: 1.15; + margin: 0; + color: var(--fg); +} +.ts__pivot-line em { + font-style: italic; + color: var(--accent); +} + +.ts__pivot-cap { + margin-top: 16px; + font-family: var(--f-serif); + font-style: italic; + font-size: 28px; + color: var(--fg-mute); +} +.ts__pivot-cap em { + color: var(--accent); +} + +/* ===================== Scene SKILL (step 4) ===================== */ + +.ts__skill-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 110px 140px 90px; + gap: 28px; + text-align: center; +} + +.ts__skill-eyebrow { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-mute); +} + +.ts__skill-card { + width: 100%; + max-width: 1280px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + overflow: hidden; + box-shadow: 0 32px 60px -28px oklch(0 0 0 / 0.25); + text-align: left; +} +.ts__skill-bar { + display: flex; + align-items: center; + gap: 6px; + padding: 10px 18px; + background: oklch(0.910 0.020 78); + border-bottom: 1px solid var(--line-mid); +} +.ts__skill-bar-dot { + width: 9px; + height: 9px; + border-radius: 50%; + background: oklch(0.820 0.030 78); +} +.ts__skill-bar-dot:nth-child(1) { background: oklch(0.730 0.180 28); } +.ts__skill-bar-dot:nth-child(2) { background: oklch(0.830 0.150 80); } +.ts__skill-bar-dot:nth-child(3) { background: oklch(0.770 0.140 145); } +.ts__skill-bar-path { + margin-left: 14px; + font-family: var(--f-mono); + font-size: 14px; + color: var(--fg-mute); + letter-spacing: 0.04em; +} + +.ts__skill-body { + padding: 40px 56px 44px; + display: flex; + flex-direction: column; + gap: 12px; +} +.ts__skill-tag { + font-family: var(--f-mono); + font-size: 13px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--accent); + font-weight: 600; +} +.ts__skill-name { + font-family: var(--f-mono); + font-weight: 500; + font-size: 88px; + line-height: 1; + margin: 4px 0 8px; + color: var(--fg); + letter-spacing: -0.02em; +} +.ts__skill-desc { + font-family: var(--f-serif); + font-size: 32px; + line-height: 1.4; + color: var(--fg-soft); + margin: 0; +} +.ts__skill-desc em { + font-style: italic; + color: var(--accent); +} +.ts__skill-meta { + margin-top: 14px; + display: flex; + align-items: center; + gap: 14px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.24em; + text-transform: uppercase; + color: var(--fg-mute); +} +.ts__skill-meta-dot { + width: 4px; + height: 4px; + border-radius: 50%; + background: var(--fg-faint); +} + +.ts__tools-cap { + font-family: var(--f-serif); + font-style: italic; + font-size: 24px; + color: var(--fg-mute); +} + +.ts__tools-row { + display: flex; + gap: 24px; + align-items: stretch; +} + +.ts__tool { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + padding: 18px 36px 22px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-top: 3px solid var(--accent); + border-radius: 3px; + min-width: 200px; + opacity: 0; + animation: tsToolIn 720ms cubic-bezier(.2,.8,.2,1) forwards; +} +@keyframes tsToolIn { + from { opacity: 0; transform: translateY(16px); } + to { opacity: 1; transform: none; } +} +.ts__tool-glyph { + font-family: var(--f-mono); + font-size: 12px; + letter-spacing: 0.32em; + color: var(--accent); +} +.ts__tool-name { + font-family: var(--f-serif); + font-size: 32px; + color: var(--fg); +} +.ts__tool-mono { + font-family: var(--f-mono); + font-size: 12px; + color: var(--fg-mute); + letter-spacing: 0.06em; +} + +/* ===================== Scene CLOSE (step 5) ===================== */ + +.ts__close { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 16px; + padding: 140px; +} +.ts__close-l1 { + font-family: var(--f-serif); + font-weight: 400; + font-size: 110px; + line-height: 1; + margin: 0; + color: var(--fg-soft); + font-style: italic; +} +.ts__close-l2 { + font-family: var(--f-serif); + font-weight: 400; + font-size: 168px; + line-height: 1; + margin: 0; + color: var(--fg); +} +.ts__close-l2 em { + font-style: italic; + color: var(--accent); +} diff --git a/web/src/chapters/10-to-skill/index.tsx b/web/src/chapters/10-to-skill/index.tsx new file mode 100644 index 0000000..742642d --- /dev/null +++ b/web/src/chapters/10-to-skill/index.tsx @@ -0,0 +1,248 @@ +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import { SceneFade } from '../../shared/SceneFade'; +import './ToSkill.css'; + +/** + * Chapter 10 · 过渡到 Skill + * + * 口播原顺序(严格对齐): + * 1. "以上就是 Claude Design 提示词里最核心的东西。" + * 2. "但有个现实问题 — Anthropic 的产品在国内用起来都非常的难。" + * "我自己被封了三个号,彻底放弃官方渠道了。" + * "而且没有 API,没法接到自己的工作流里。" + * 3. "不过好消息是:它的提示词已经泄出来了。 + * Claude Design 厉害的另一半,主要就这套提示词里。" + * 4. "所以我做了个 Skill,叫 web-design-engineer, + * 把这套提示词的精华提炼了出来。" + * 5. "Claude Code、Cursor、Codex 都能直接用,人人都能成为顶级网页设计师。" + * + * 节奏(6 步 / step 0..5): + * 0 回顾:"以上 —— 提示词原文最核心的东西" + 五块小钩 + * 1 转折大字 "但..." + "Anthropic 在国内 —— 难" + * 2 三张账号卡片依次倒下 + BANNED 红章 + "没 API"小注 + * 3 Pivot:"好消息 —— 提示词 已经泄出来了" + * 4 Skill 卡:web-design-engineer 终端式呈现 + 三个适用工具 + * 5 收尾大字 "人人都能成为 顶级网页设计师" + */ + +const RECAP_POINTS = [ + '角色定位', + '工作流', + '去 AI 味', + 'oklch 配色', + '内容克制', + '验证闭环', +]; + +const TOOLS = [ + { id: 'cc', name: 'Claude Code', mono: 'claude.code' }, + { id: 'cu', name: 'Cursor', mono: 'cursor.sh' }, + { id: 'cx', name: 'Codex', mono: 'codex.cli' }, +]; + +function ToSkill({ localStep }: ChapterContext) { + const at = (n: number) => localStep >= n; + void at; + + const sceneRecap = localStep <= 0; + const sceneProb = localStep === 1; + const sceneBanned = localStep === 2; + const scenePivot = localStep === 3; + const sceneSkill = localStep === 4; + const sceneClose = localStep >= 5; + + return ( +
+ {/* ════════ Scene RECAP(step 0)════════ */} + +
+ + 以上 —— 提示词里
+ 最核心的东西 +
+ +
+ {RECAP_POINTS.map((p, i) => ( +
+ 0{i + 1} + {p} + +
+ ))} +
+
+
+ + {/* ════════ Scene PROBLEM(step 1)—— "但..." ════════ */} + +
+ + 但 —— + + + + Anthropic 的产品 ——
+ 在国内 真的难用 +
+ + + 无官方支付 + + 无 API + + 账号易被封 + +
+
+ + {/* ════════ Scene BANNED(step 2)—— 三张账号倒下 ════════ */} + +
+ + "我自己被封了 三个号,彻底放弃官方渠道了。" + + +
+ {[1, 2, 3].map((n, i) => ( +
+
+ + + + claude.ai / account +
+
+
{['F', 'G', 'H'][i]}
+
+
花园老师 #{n}
+
flower-{i + 1}@anthropic.user
+
+ Pro + activated · 2026.0{i + 1} +
+
+
+ {/* BANNED 印章 */} +
+ BANNED + violation · #{n} +
+
+ ))} +
+ + + × + 而且 —— 没有 API,接不进自己的工作流 + +
+
+ + {/* ════════ Scene PIVOT(step 3)—— "好消息" ════════ */} + +
+ + 不过 —— + + + + 好消息是: + + + + 提示词 —— 已经泄出来了 + + + + "Claude Design 厉害的另一半,主要就这套提示词里。" + +
+
+ + {/* ════════ Scene SKILL(step 4)—— web-design-engineer ════════ */} + +
+ + [ + SKILL · OPEN SOURCE + ] + + + +
+ + + + .claude / skills / web-design-engineer / SKILL.md +
+
+
SKILL.md
+

web-design-engineer

+

+ 把 Claude Design 提示词的精华,
+ 提炼成一个可复用的 Skill +

+
+ ≈ 400 行 + + 开源 + + 免费 +
+
+
+ + + 适用于 —— + + +
+ {TOOLS.map((t, i) => ( +
+
[ {t.id} ]
+
{t.name}
+
{t.mono}
+
+ ))} +
+
+
+ + {/* ════════ Scene CLOSE(step 5)════════ */} + +
+ + 人人都能成为 + + + 顶级网页设计师 + +
+
+
+ ); +} + +const def: ChapterDef = { + id: 'to-skill', + title: '过渡 · Skill 是怎么来的', + eyebrow: '10', + steps: 6, + theme: 'light', + Component: ToSkill, +}; + +export default def; diff --git a/web/src/chapters/11-skill-changes/SkillChanges.css b/web/src/chapters/11-skill-changes/SkillChanges.css new file mode 100644 index 0000000..2b97bd0 --- /dev/null +++ b/web/src/chapters/11-skill-changes/SkillChanges.css @@ -0,0 +1,1141 @@ +/* ========================================================= + Chapter 11 · Skill 的关键改动(重做) + light 主题 · 文件树 → 假滚动 → 剥离 → 4 改动 + 原文 → 收尾 + ========================================================= */ + +.sk { + position: absolute; + inset: 0; + background: var(--bg); + color: var(--fg); + font-family: var(--f-sans); + overflow: hidden; +} + +.sk__src-bracket { color: var(--accent); font-weight: 600; } +.sk__src-label { color: var(--fg); font-weight: 600; letter-spacing: 0.22em; } +.sk__src-sep { color: var(--fg-faint); } +.sk__src-line { color: var(--accent); font-weight: 600; } + +/* ======================================================================== + Scene TREE (step 0) —— Skill 文件目录全貌 + ======================================================================== */ + +.sk__tree-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 110px 140px 90px; + gap: 22px; + text-align: center; +} + +.sk__tree-eyebrow { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-mute); +} + +.sk__tree-title { + font-family: var(--f-serif); + font-weight: 400; + font-size: 96px; + line-height: 1.1; + margin: 0; + color: var(--fg); +} + +.sk__tree-card { + width: 100%; + max-width: 1200px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + overflow: hidden; + box-shadow: 0 32px 60px -28px oklch(0 0 0 / 0.22); + text-align: left; +} +.sk__tree-bar { + display: flex; + align-items: center; + gap: 6px; + padding: 10px 18px; + background: oklch(0.910 0.020 78); + border-bottom: 1px solid var(--line-mid); +} +.sk__tree-bar-dot { + width: 9px; + height: 9px; + border-radius: 50%; + background: oklch(0.820 0.030 78); +} +.sk__tree-bar-dot:nth-child(1) { background: oklch(0.730 0.180 28); } +.sk__tree-bar-dot:nth-child(2) { background: oklch(0.830 0.150 80); } +.sk__tree-bar-dot:nth-child(3) { background: oklch(0.770 0.140 145); } +.sk__tree-bar-path { + margin-left: 14px; + font-family: var(--f-mono); + font-size: 14px; + color: var(--fg-mute); + letter-spacing: 0.04em; +} + +.sk__tree-body { + padding: 28px 38px 30px; + display: flex; + flex-direction: column; + gap: 8px; +} + +.sk__tree-row { + display: grid; + grid-template-columns: auto auto 1fr auto auto; + align-items: baseline; + gap: 12px; + padding: 8px 14px; + border-radius: 2px; + font-family: var(--f-mono); + font-size: 22px; + color: var(--fg); + opacity: 0; + transform: translateX(-8px); + animation: skTreeRowIn 540ms cubic-bezier(.2,.8,.2,1) forwards; +} +@keyframes skTreeRowIn { + to { opacity: 1; transform: none; } +} +.sk__tree-row--main { + background: oklch(0.700 0.170 42 / 0.08); + border-left: 3px solid var(--accent); + padding-left: 12px; +} +.sk__tree-row--child { + margin-left: 56px; + background: oklch(0.700 0.170 42 / 0.05); + border-left: 2px solid oklch(0.700 0.170 42 / 0.45); + padding-left: 12px; +} +.sk__tree-row--sub { + margin-top: 4px; +} + +.sk__tree-glyph { + color: var(--accent); + font-size: 16px; + width: 16px; +} +.sk__tree-pipe { + color: var(--fg-faint); + font-size: 16px; + letter-spacing: 0; +} +.sk__tree-pipe--child { + color: oklch(0.700 0.170 42 / 0.55); +} +.sk__tree-name { + color: var(--fg); + font-weight: 500; +} +.sk__tree-name--md { + color: var(--accent); + font-weight: 500; +} +.sk__tree-slash { + color: var(--fg-faint); +} +.sk__tree-tag { + font-family: var(--f-serif); + font-style: italic; + font-size: 18px; + color: var(--fg-mute); + letter-spacing: 0; +} +.sk__tree-meta { + font-family: var(--f-mono); + font-size: 14px; + color: var(--fg-faint); + letter-spacing: 0.06em; + text-align: right; +} +.sk__tree-meta--em { + color: var(--accent); + font-weight: 600; +} + +.sk__tree-foot { + font-family: var(--f-serif); + font-style: italic; + font-size: 26px; + color: var(--fg-soft); +} +.sk__tree-foot em { + color: var(--accent); + font-style: italic; +} + +/* ======================================================================== + Scene SCROLL (step 1) —— SKILL.md 假滚动展示 + ======================================================================== */ + +.sk__scroll-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 100px 140px 80px; + gap: 18px; + text-align: center; +} + +.sk__scroll-eyebrow { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-mute); +} + +.sk__scroll-cap { + font-family: var(--f-serif); + font-style: italic; + font-size: 30px; + color: var(--fg-soft); +} +.sk__scroll-cap em { + color: var(--accent); + font-style: italic; +} + +.sk__scroll-card { + width: 100%; + max-width: 1280px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + overflow: hidden; + box-shadow: 0 32px 60px -28px oklch(0 0 0 / 0.25); + text-align: left; +} + +.sk__scroll-bar { + display: flex; + align-items: center; + gap: 6px; + padding: 10px 18px; + background: oklch(0.910 0.020 78); + border-bottom: 1px solid var(--line-mid); +} +.sk__scroll-bar-dot { + width: 9px; + height: 9px; + border-radius: 50%; + background: oklch(0.820 0.030 78); +} +.sk__scroll-bar-dot:nth-child(1) { background: oklch(0.730 0.180 28); } +.sk__scroll-bar-dot:nth-child(2) { background: oklch(0.830 0.150 80); } +.sk__scroll-bar-dot:nth-child(3) { background: oklch(0.770 0.140 145); } +.sk__scroll-bar-path { + margin-left: 14px; + font-family: var(--f-mono); + font-size: 14px; + color: var(--accent); + font-weight: 600; + letter-spacing: 0.04em; +} +.sk__scroll-bar-meta { + margin-left: auto; + font-family: var(--f-mono); + font-size: 12px; + color: var(--fg-faint); + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.sk__scroll-frame { + position: relative; + height: 480px; + background: var(--bg); + overflow: hidden; +} +.sk__scroll-frame::before, +.sk__scroll-frame::after { + content: ''; + position: absolute; + left: 0; + right: 0; + height: 80px; + pointer-events: none; + z-index: 2; +} +.sk__scroll-frame::before { + top: 0; + background: linear-gradient(180deg, var(--bg) 0%, oklch(0.965 0.018 78 / 0) 100%); +} +.sk__scroll-frame::after { + bottom: 0; + background: linear-gradient(0deg, var(--bg) 0%, oklch(0.965 0.018 78 / 0) 100%); +} + +.sk__scroll-stream { + position: absolute; + left: 0; + right: 0; + top: 0; + display: flex; + flex-direction: column; + animation: skScrollLoop 28s linear infinite; + will-change: transform; +} +@keyframes skScrollLoop { + from { transform: translateY(0); } + to { transform: translateY(-50%); } +} + +.sk__scroll-block { + display: flex; + flex-direction: column; +} + +.sk__scroll-line { + display: grid; + grid-template-columns: 56px 1fr; + align-items: baseline; + gap: 18px; + padding: 4px 24px; + font-family: var(--f-mono); + font-size: 18px; + line-height: 1.55; + color: var(--fg-soft); + min-height: 32px; +} +.sk__scroll-line.is-mute { + color: var(--fg-faint); +} +.sk__scroll-line.is-hi { + background: linear-gradient( + 90deg, + oklch(0.700 0.170 42 / 0.10) 0%, + oklch(0.700 0.170 42 / 0.04) 100%); + color: var(--fg); +} +.sk__scroll-line.is-hi .sk__scroll-text { + color: var(--accent); + font-weight: 600; +} + +.sk__scroll-num { + font-family: var(--f-mono); + font-size: 13px; + color: var(--fg-faint); + text-align: right; + letter-spacing: 0.04em; +} +.sk__scroll-text { + white-space: pre-wrap; + word-break: break-word; +} + +/* 像编辑器一样的小光标(永远在中间附近闪一下) */ +.sk__scroll-cursor { + position: absolute; + top: 50%; + left: 80px; + width: 2px; + height: 22px; + background: var(--accent); + transform: translateY(-50%); + animation: skCursorBlink 1.1s steps(1, end) infinite; + z-index: 3; + opacity: 0.55; +} +@keyframes skCursorBlink { + 0%, 50% { opacity: 0.55; } + 51%, 100% { opacity: 0; } +} + +.sk__scroll-foot { + display: flex; + align-items: baseline; + gap: 10px; + font-family: var(--f-serif); + font-style: italic; + font-size: 26px; + color: var(--fg-mute); +} +.sk__scroll-foot em { + color: var(--accent); + font-style: italic; + font-weight: 500; +} + +/* ======================================================================== + Scene STRIP (step 2) —— 第一刀:剥离 + ======================================================================== */ + +.sk__strip-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 110px 140px 90px; + gap: 24px; + text-align: center; +} + +.sk__strip { + display: grid; + grid-template-columns: 1fr 80px 1fr; + gap: 24px; + width: 100%; + max-width: 1500px; +} + +.sk__strip-col { + display: flex; + flex-direction: column; + gap: 14px; + padding: 26px 32px 30px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + text-align: left; +} +.sk__strip-col--keep { border-left: 3px solid var(--accent); } +.sk__strip-col--drop { + border-left: 3px solid var(--crimson); + opacity: 0.6; + filter: grayscale(0.5); +} + +.sk__strip-head { + display: flex; + align-items: center; + gap: 12px; + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.24em; + text-transform: uppercase; + color: var(--fg); + font-weight: 600; + border-bottom: 1px dashed var(--line); + padding-bottom: 12px; + margin-bottom: 4px; +} +.sk__strip-mark { + font-family: var(--f-serif); + font-style: italic; + font-size: 26px; + line-height: 1; +} +.sk__strip-mark--keep { color: var(--accent); } +.sk__strip-mark--drop { color: var(--crimson); } + +.sk__strip-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 10px; +} + +.sk__strip-row { + display: flex; + align-items: baseline; + gap: 14px; + font-family: var(--f-serif); + font-size: 24px; + color: var(--fg); + opacity: 0; + animation: skDiffRowIn 520ms cubic-bezier(.2,.8,.2,1) forwards; +} +@keyframes skDiffRowIn { + from { opacity: 0; transform: translateX(-12px); } + to { opacity: 1; transform: none; } +} +.sk__strip-row code { + font-family: var(--f-mono); + font-size: 19px; + background: oklch(0.910 0.020 78); + border: 1px solid var(--line); + padding: 1px 8px; + border-radius: 2px; + color: var(--fg-soft); +} +.sk__strip-row-glyph { + font-family: var(--f-mono); + font-size: 22px; + width: 18px; + text-align: center; + color: var(--accent); +} +.sk__strip-row--drop .sk__strip-row-glyph { color: var(--crimson); } +.sk__strip-row--drop { + text-decoration: line-through; + text-decoration-thickness: 1px; + text-decoration-color: var(--crimson); + color: var(--fg-mute); +} + +.sk__strip-sep { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 14px; + color: var(--fg-faint); +} +.sk__strip-sep-line { + flex: 1; + width: 1px; + background: var(--line-mid); +} +.sk__strip-sep-knob { + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + color: var(--accent); +} + +/* ======================================================================== + Scene CHANGE 通用 (step 3..6) + ======================================================================== */ + +.sk__chg-scene { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 80px 100px 60px; + gap: 18px; + text-align: center; +} + +.sk__chg-num { + font-family: var(--f-mono); + font-size: 14px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--accent); + font-weight: 600; +} + +.sk__chg-title { + font-family: var(--f-serif); + font-weight: 400; + font-size: 64px; + line-height: 1.1; + margin: 0; + color: var(--fg); +} +.sk__chg-title em { + font-style: italic; + color: var(--accent); +} + +/* 左右分屏 */ +.sk__split { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 32px; + width: 100%; + max-width: 1640px; + align-items: stretch; + margin-top: 6px; +} +.sk__split-left, +.sk__split-right { + display: flex; +} + +/* ──────── 改动 01 · 流程对比 ──────── */ + +.sk__chg-flow { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24px; + width: 100%; + align-items: stretch; +} +.sk__chg-flow-col { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + padding: 18px 22px 22px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; +} +.sk__chg-flow-col--bad { border-left: 3px solid var(--crimson); opacity: 0.85; } +.sk__chg-flow-col--good { border-left: 3px solid var(--accent); } + +.sk__chg-flow-tag { + display: flex; + align-items: center; + gap: 10px; + font-family: var(--f-mono); + font-size: 12px; + letter-spacing: 0.32em; + text-transform: uppercase; + color: var(--fg-mute); + margin-bottom: 4px; +} +.sk__chg-flow-mark { + font-family: var(--f-serif); + font-style: italic; + font-size: 22px; + line-height: 1; +} +.sk__chg-flow-mark--bad { color: var(--crimson); } +.sk__chg-flow-mark--good { color: var(--accent); } + +.sk__chg-flow-step { + width: 100%; + padding: 10px 14px; + background: var(--bg); + border: 1px solid var(--line-mid); + border-radius: 3px; + font-family: var(--f-serif); + font-size: 20px; + color: var(--fg); + text-align: center; +} +.sk__chg-flow-step--code { + font-family: var(--f-mono); + font-size: 17px; + letter-spacing: 0.04em; +} +.sk__chg-flow-step--out { + background: var(--accent); + color: var(--paper); + border-color: var(--accent); +} +.sk__chg-flow-step--system { + padding: 0; + border: 1px solid var(--accent); + background: oklch(0.700 0.170 42 / 0.06); + overflow: hidden; +} + +.sk__chg-flow-arrow { + font-family: var(--f-mono); + font-size: 18px; + color: var(--fg-faint); +} + +.sk__chg-system { + display: flex; + flex-direction: column; +} +.sk__chg-system-row { + display: grid; + grid-template-columns: 80px 1fr; + gap: 10px; + padding: 7px 14px; + border-bottom: 1px dashed var(--line); + font-family: var(--f-mono); + font-size: 14px; + color: var(--fg); + text-align: left; +} +.sk__chg-system-row:last-child { border-bottom: none; } +.sk__chg-system-row span { + color: var(--fg-mute); + letter-spacing: 0.06em; + text-transform: uppercase; + font-size: 10px; + align-self: center; +} +.sk__chg-system-row b { + font-weight: 500; + color: var(--accent); + letter-spacing: 0.02em; +} + +.sk__chg-flow-tip { + margin-top: 6px; + font-family: var(--f-serif); + font-style: italic; + font-size: 17px; + color: var(--fg-soft); +} +.sk__chg-flow-col--bad .sk__chg-flow-tip em { color: var(--crimson); font-style: italic; } +.sk__chg-flow-col--good .sk__chg-flow-tip em { color: var(--accent); font-style: italic; } + +/* ──────── 改动 02 · v0 vs v1 ──────── */ + +.sk__v { + display: grid; + grid-template-columns: 1fr 50px 1fr; + gap: 16px; + width: 100%; + align-items: stretch; +} +.sk__v-card { + position: relative; + display: flex; + flex-direction: column; + gap: 12px; + padding: 20px 22px 22px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; +} +.sk__v-card--v0 { border-left: 3px solid var(--accent); } +.sk__v-card--v1 { border-left: 3px solid var(--crimson); opacity: 0.92; } + +.sk__v-tag { + display: flex; + align-items: center; + gap: 10px; + font-family: var(--f-mono); + font-size: 12px; + letter-spacing: 0.24em; + text-transform: uppercase; + color: var(--fg-mute); + border-bottom: 1px dashed var(--line); + padding-bottom: 10px; +} +.sk__v-tag em { + font-family: var(--f-serif); + font-style: italic; + letter-spacing: 0.02em; + text-transform: none; + color: var(--fg); + font-size: 16px; +} +.sk__v-mark { + font-family: var(--f-serif); + font-style: italic; + font-size: 20px; + line-height: 1; +} +.sk__v-mark--good { color: var(--accent); } +.sk__v-mark--bad { color: var(--crimson); } + +.sk__v-mock { + display: flex; + flex-direction: column; + gap: 8px; + padding: 16px; + background: var(--bg); + border: 1px dashed var(--line-mid); + border-radius: 2px; + min-height: 160px; + align-items: flex-start; + justify-content: center; +} +.sk__v-mock--full { + border: 1px solid var(--line-mid); + border-radius: 3px; + align-items: center; + text-align: center; + background: linear-gradient(180deg, oklch(0.910 0.020 78) 0%, var(--bg) 60%); +} +.sk__v-mock-bar { + display: block; + height: 12px; + background: var(--line-mid); + border-radius: 2px; +} +.sk__v-mock-bar--w70 { width: 70%; } +.sk__v-mock-bar--w50 { width: 50%; } +.sk__v-mock-bar--w40 { width: 40%; } +.sk__v-mock-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 6px; + width: 100%; +} +.sk__v-mock-grid > span { + height: 44px; + background: var(--line-mid); + border-radius: 2px; +} +.sk__v-mock-grid--full > span { + display: flex; + align-items: center; + justify-content: center; + background: var(--bg-2); + border: 1px solid var(--line); + font-family: var(--f-serif); + font-style: italic; + font-size: 22px; + color: var(--accent); +} +.sk__v-mock-h { + font-family: var(--f-serif); + font-size: 22px; + font-weight: 500; + color: var(--fg); +} +.sk__v-mock-sub { + font-family: var(--f-sans); + font-size: 12px; + color: var(--fg-mute); + margin-bottom: 4px; +} +.sk__v-mock-cta { + margin-top: 4px; + padding: 5px 14px; + background: var(--accent); + color: var(--paper); + font-family: var(--f-mono); + font-size: 11px; + letter-spacing: 0.18em; + text-transform: uppercase; + border-radius: 2px; +} + +.sk__v-foot { + font-family: var(--f-serif); + font-style: italic; + font-size: 18px; + color: var(--fg-mute); +} +.sk__v-card--v0 .sk__v-foot em { color: var(--accent); font-style: italic; } +.sk__v-card--v1 .sk__v-foot em { color: var(--crimson); font-style: italic; } + +.sk__v-vs { + display: flex; + align-items: center; + justify-content: center; + font-family: var(--f-serif); + font-style: italic; + font-size: 36px; + color: var(--fg-faint); +} + +.sk__v-strike { + position: absolute; + inset: 0; + pointer-events: none; + overflow: hidden; +} +.sk__v-strike-line { + position: absolute; + top: 50%; + left: -10%; + right: -10%; + height: 3px; + background: var(--crimson); + transform: translateY(-50%) rotate(-8deg) scaleX(0); + transform-origin: left; + animation: skVStrike 620ms 1500ms cubic-bezier(.6,-0.05,.2,1.2) forwards; + opacity: 0.55; +} +@keyframes skVStrike { + to { transform: translateY(-50%) rotate(-8deg) scaleX(1); } +} + +/* ──────── 改动 03 / 04 共用 col ──────── */ + +.sk__cd-col { + display: flex; + flex-direction: column; + gap: 14px; + padding: 22px 26px 26px; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-left: 3px solid var(--accent); + border-radius: 4px; + width: 100%; + text-align: left; +} + +.sk__cd-tag { + display: flex; + align-items: center; + gap: 12px; + font-family: var(--f-serif); + font-style: italic; + font-size: 24px; + color: var(--fg); + border-bottom: 1px dashed var(--line); + padding-bottom: 12px; +} +.sk__cd-mark { + font-family: var(--f-mono); + font-style: normal; + font-size: 12px; + letter-spacing: 0.24em; + text-transform: uppercase; + color: var(--accent); + background: oklch(0.700 0.170 42 / 0.12); + border: 1px solid oklch(0.700 0.170 42 / 0.45); + padding: 4px 10px; + border-radius: 2px; + font-weight: 600; +} + +.sk__cd-anti { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 8px; +} +.sk__cd-anti-row { + display: flex; + align-items: baseline; + gap: 12px; + padding: 8px 14px; + background: var(--bg); + border: 1px solid var(--line); + border-radius: 2px; + font-family: var(--f-sans); + font-size: 19px; + color: var(--fg-soft); + opacity: 0; + animation: skDiffRowIn 520ms cubic-bezier(.2,.8,.2,1) forwards; +} +.sk__cd-anti-x { + font-family: var(--f-serif); + font-style: italic; + font-size: 20px; + color: var(--crimson); +} + +.sk__pairs { + display: flex; + flex-direction: column; + gap: 6px; +} +.sk__pair { + display: grid; + grid-template-columns: 110px 110px 24px 1fr; + align-items: center; + gap: 10px; + padding: 8px 12px; + background: var(--bg); + border: 1px solid var(--line); + border-radius: 2px; + font-family: var(--f-sans); + font-size: 16px; + color: var(--fg); + opacity: 0; + animation: skDiffRowIn 520ms cubic-bezier(.2,.8,.2,1) forwards; +} +.sk__pair-tag { + font-family: var(--f-serif); + font-style: italic; + font-size: 18px; + color: var(--fg); +} +.sk__pair-color { + font-family: var(--f-mono); + font-size: 13px; + color: var(--accent); + letter-spacing: 0.04em; +} +.sk__pair-x { + font-family: var(--f-mono); + color: var(--fg-faint); + text-align: center; +} +.sk__pair-font { + font-family: var(--f-mono); + font-size: 13px; + color: var(--fg-soft); + letter-spacing: 0.02em; +} + +/* ======================================================================== + 原文摘录面板(Excerpt) —— 关键行高亮 / 上下文模糊虚化 + ======================================================================== */ + +.sk__ex { + width: 100%; + display: flex; + flex-direction: column; + background: var(--bg-2); + border: 1px solid var(--line-mid); + border-radius: 4px; + overflow: hidden; + box-shadow: 0 24px 48px -28px oklch(0 0 0 / 0.22); +} + +.sk__ex-bar { + display: flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + background: oklch(0.910 0.020 78); + border-bottom: 1px solid var(--line-mid); +} +.sk__ex-bar-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: oklch(0.820 0.030 78); +} +.sk__ex-bar-dot:nth-child(1) { background: oklch(0.730 0.180 28); } +.sk__ex-bar-dot:nth-child(2) { background: oklch(0.830 0.150 80); } +.sk__ex-bar-dot:nth-child(3) { background: oklch(0.770 0.140 145); } +.sk__ex-bar-path { + margin-left: 12px; + font-family: var(--f-mono); + font-size: 13px; + color: var(--accent); + font-weight: 600; + letter-spacing: 0.04em; +} +.sk__ex-bar-range { + margin-left: auto; + font-family: var(--f-mono); + font-size: 11px; + color: var(--fg-mute); + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.sk__ex-body { + flex: 1; + display: flex; + flex-direction: column; + padding: 14px 0; + background: + linear-gradient(180deg, + oklch(0.940 0.018 78) 0%, + oklch(0.940 0.018 78) 100%); + /* 上下文虚化基底:让非高亮行整体偏淡 */ +} + +.sk__ex-line { + display: grid; + grid-template-columns: 48px 1fr; + gap: 14px; + align-items: baseline; + padding: 2px 22px 2px calc(22px + var(--ex-indent, 0px)); + font-family: var(--f-mono); + font-size: 15px; + line-height: 1.65; + color: var(--fg-mute); + opacity: 0.32; + filter: blur(0.7px); + transition: opacity 360ms, filter 360ms; + min-height: 22px; +} + +.sk__ex-line.is-hi { + opacity: 1; + filter: none; + background: oklch(0.700 0.170 42 / 0.10); + color: var(--fg); + position: relative; + animation: skExHiPulse 1100ms cubic-bezier(.2,.8,.2,1) backwards; +} +.sk__ex-line.is-hi::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 3px; + background: var(--accent); +} +@keyframes skExHiPulse { + 0% { background: oklch(0.700 0.170 42 / 0); transform: translateX(-4px); } + 35% { background: oklch(0.700 0.170 42 / 0.30); transform: translateX(0); } + 100% { background: oklch(0.700 0.170 42 / 0.10); transform: translateX(0); } +} + +.sk__ex-line.is-hi b { + color: var(--accent); + font-weight: 600; +} +.sk__ex-line.is-hi em { + font-style: italic; + color: var(--accent); + background: oklch(0.700 0.170 42 / 0.18); + padding: 0 4px; + border-radius: 2px; +} + +.sk__ex-num { + font-family: var(--f-mono); + font-size: 12px; + color: var(--fg-faint); + text-align: right; + letter-spacing: 0.04em; + user-select: none; +} +.sk__ex-line.is-hi .sk__ex-num { + color: var(--accent); + font-weight: 600; +} +.sk__ex-text { + white-space: pre-wrap; + word-break: break-word; +} + +.sk__ex-caption { + padding: 10px 22px 14px; + background: oklch(0.910 0.020 78); + border-top: 1px solid var(--line-mid); + font-family: var(--f-serif); + font-style: italic; + font-size: 18px; + color: var(--fg-soft); + text-align: center; +} +.sk__ex-caption em { + color: var(--accent); + font-style: italic; +} + +/* ======================================================================== + Scene CLOSE (step 7) + ======================================================================== */ + +.sk__close { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + gap: 18px; + padding: 140px; +} +.sk__close-l1 { + font-family: var(--f-serif); + font-weight: 400; + font-size: 128px; + line-height: 1; + margin: 0; + color: var(--fg); +} +.sk__close-l2 { + font-family: var(--f-serif); + font-weight: 400; + font-size: 64px; + line-height: 1.2; + margin: 0; + color: var(--fg-soft); +} +.sk__close-l1 em, +.sk__close-l2 em { + font-style: italic; + color: var(--accent); +} diff --git a/web/src/chapters/11-skill-changes/index.tsx b/web/src/chapters/11-skill-changes/index.tsx new file mode 100644 index 0000000..8c5a547 --- /dev/null +++ b/web/src/chapters/11-skill-changes/index.tsx @@ -0,0 +1,663 @@ +import type { ReactNode } from 'react'; +import type { ChapterContext, ChapterDef } from '../types'; +import { Reveal } from '../../shared/Reveal'; +import { SceneFade } from '../../shared/SceneFade'; +import './SkillChanges.css'; + +/** + * Chapter 11 · Skill 的关键改动(重做版) + * + * 口播原顺序(严格对齐 article/口播稿.md): + * "这个 Skill 大概 400 行,首先我把 Claude Design 中特有工具和环境的描述剥离了出去, + * 只保留了其中能通用的、最有价值的部分,然后在这个基础上我还优化了几个地方:" + * ① 写代码之前必须先用自然语言把设计系统说清楚 — 配色、字体、间距,全列出来。 + * ② 要求它尽早拿出一个带假设和占位符的最小版本(v0)。 + * ③ 在 Claude Design 的基础上,补充了更多的去除 AI 味的条目。 + * ④ 加了几套经过验证的配色 × 字体的配对参考表 —— 给 AI 一个靠谱的起点。 + * + * 节奏(8 步 / step 0..7): + * 0 Skill 全貌 —— 文件目录树(SKILL.md ≈ 400 行 / references ≈ 520 行) + * 1 SKILL.md 真原文滚动展示(虚假的编辑器持续 scroll) + * 2 第一刀:剥离 —— 工具/环境 vs 通用精华 + * 3 + 改动 01 · 先宣告设计系统 + SKILL.md L79-91 原文摘录(关键高亮 / 上下文虚化) + * 4 + 改动 02 · v0 半成品先出 + SKILL.md L93-101 原文摘录 + * 5 + 改动 03 · 反 AI 味扩展 + SKILL.md L200-219 原文摘录 + * 6 + 改动 04 · 配色 × 字体配对 + advanced-patterns.md L505-516 原文摘录 + * 7 收尾:"给 AI 一个靠谱的起点 —— 比让它自由发挥稳定" + */ + +/* ────────────────────────────────────────────────────────────────── + * 复用:原文摘录代码面板(关键行高亮、上下文虚化模糊) + * ────────────────────────────────────────────────────────────────── */ + +interface CodeLine { + /** 真实行号(用于左侧 gutter) */ + n: number; + /** 行内容 */ + text: ReactNode; + /** 是否高亮(不模糊、不弱化) */ + hi?: boolean; + /** 行视觉缩进(每级 16px) */ + indent?: number; +} + +interface ExcerptProps { + filePath: string; + range: string; + caption?: ReactNode; + lines: CodeLine[]; + /** delay before highlight pulse */ + pulseDelay?: number; +} + +function Excerpt({ filePath, range, caption, lines, pulseDelay = 600 }: ExcerptProps) { + return ( +
+
+ + + + {filePath} + {range} +
+
+ {lines.map((l, i) => ( +
+ {l.n} + {l.text} +
+ ))} +
+ {caption &&
{caption}
} +
+ ); +} + +/* ────────────────────────────────────────────────────────────────── + * SKILL.md 假滚动用的内容片段(接近真实,用于动态滚动展示) + * ────────────────────────────────────────────────────────────────── */ + +const SCROLL_LINES: { t: ReactNode; hi?: boolean; mute?: boolean }[] = [ + { t: '---', mute: true }, + { t: 'name: web-design-engineer', hi: true }, + { t: 'description: |' }, + { t: ' Build high-quality visual Web artifacts using HTML / CSS / JS / React —' }, + { t: ' web pages, dashboards, prototypes, slide decks, animated demos, …' }, + { t: ' Use this skill whenever the request involves a visual deliverable.' }, + { t: '---', mute: true }, + { t: '' }, + { t: '# Web Design Engineer', hi: true }, + { t: '' }, + { t: 'Core philosophy: the bar is "stunning," not "functional".' }, + { t: 'Every pixel is intentional. Every interaction is deliberate.' }, + { t: '' }, + { t: '## Workflow', hi: true }, + { t: '' }, + { t: '### Step 1 · Understand the Requirements' }, + { t: '### Step 2 · Gather Design Context' }, + { t: '### Step 3 · Declare the Design System Before Writing Code', hi: true }, + { t: '### Step 4 · Show a v0 Draft Early', hi: true }, + { t: '### Step 5 · Full Build' }, + { t: '### Step 6 · Verification' }, + { t: '' }, + { t: '## Technical Specifications' }, + { t: '' }, + { t: '#### Three Non-negotiable Hard Rules', hi: true }, + { t: '1. Never use `const styles = {...}`' }, + { t: '2. Separate