feat: 首页增加工具箱功能区、剧本评测可视化展示;重构剧本评分页面UI #6
Generated
+163
-174
@@ -8,20 +8,20 @@
|
||||
"name": "omniai-web-preview",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.3.0",
|
||||
"@xyflow/react": "^12.10.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"zustand": "^5.0.13"
|
||||
"@ant-design/icons": "5.3.0",
|
||||
"@xyflow/react": "12.10.2",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"zustand": "5.0.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"playwright": "^1.60.0",
|
||||
"sharp": "^0.34.5",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.1.0",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0",
|
||||
"@vitejs/plugin-react": "4.2.1",
|
||||
"playwright": "1.60.0",
|
||||
"sharp": "0.34.5",
|
||||
"typescript": "5.3.3",
|
||||
"vite": "5.1.0",
|
||||
"vite-plugin-compression2": "2.5.3"
|
||||
}
|
||||
},
|
||||
@@ -47,14 +47,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ant-design/icons": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz",
|
||||
"integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.3.0.tgz",
|
||||
"integrity": "sha512-69FgBsIkeCjw72ZU3fJpqjhmLCPrzKGEllbrAZK7MUdt1BrKsyG6A8YDCBPKea27UQ0tRXi33PcjR4tp/tEXMg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.0",
|
||||
"@ant-design/icons-svg": "^4.4.0",
|
||||
"@babel/runtime": "^7.24.8",
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"classnames": "^2.2.6",
|
||||
"rc-util": "^5.31.1"
|
||||
},
|
||||
@@ -103,7 +103,6 @@
|
||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
@@ -376,9 +375,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
||||
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
|
||||
"integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -393,9 +392,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
|
||||
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
|
||||
"integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -410,9 +409,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
|
||||
"integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -427,9 +426,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -444,9 +443,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
|
||||
"integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -461,9 +460,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -478,9 +477,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
|
||||
"integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -495,9 +494,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -512,9 +511,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
|
||||
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
|
||||
"integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -529,9 +528,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
|
||||
"integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -546,9 +545,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
|
||||
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
|
||||
"integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -563,9 +562,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
|
||||
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
|
||||
"integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -580,9 +579,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
|
||||
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
|
||||
"integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
@@ -597,9 +596,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
|
||||
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
|
||||
"integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -614,9 +613,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
|
||||
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
|
||||
"integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -631,9 +630,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
|
||||
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
|
||||
"integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -648,9 +647,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -665,9 +664,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -682,9 +681,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -699,9 +698,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -716,9 +715,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
|
||||
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
|
||||
"integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -733,9 +732,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
|
||||
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
|
||||
"integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -750,9 +749,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
|
||||
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
|
||||
"integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1306,13 +1305,6 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-beta.27",
|
||||
"resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
||||
"integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
|
||||
@@ -1795,46 +1787,52 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.28",
|
||||
"resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.28.tgz",
|
||||
"integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==",
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.2.2"
|
||||
"@types/scheduler": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "18.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz",
|
||||
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
|
||||
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-8yQrvS6sMpSwIovhPOwfyNf2Wz6v/B62LFSVYQ85+Rq3tLsBIG7rP5geMxaijTUxSkrO6RzN/IRuIAADYQsleA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.28.0",
|
||||
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
|
||||
"@rolldown/pluginutils": "1.0.0-beta.27",
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/scheduler": {
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.26.0.tgz",
|
||||
"integrity": "sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz",
|
||||
"integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.23.5",
|
||||
"@babel/plugin-transform-react-jsx-self": "^7.23.3",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.23.3",
|
||||
"@types/babel__core": "^7.20.5",
|
||||
"react-refresh": "^0.17.0"
|
||||
"react-refresh": "^0.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
||||
"vite": "^4.2.0 || ^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@xyflow/react": {
|
||||
@@ -1930,7 +1928,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.10.12",
|
||||
"caniuse-lite": "^1.0.30001782",
|
||||
@@ -2049,7 +2046,6 @@
|
||||
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -2134,9 +2130,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
|
||||
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
||||
"version": "0.19.12",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
|
||||
"integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
@@ -2147,29 +2143,29 @@
|
||||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.21.5",
|
||||
"@esbuild/android-arm": "0.21.5",
|
||||
"@esbuild/android-arm64": "0.21.5",
|
||||
"@esbuild/android-x64": "0.21.5",
|
||||
"@esbuild/darwin-arm64": "0.21.5",
|
||||
"@esbuild/darwin-x64": "0.21.5",
|
||||
"@esbuild/freebsd-arm64": "0.21.5",
|
||||
"@esbuild/freebsd-x64": "0.21.5",
|
||||
"@esbuild/linux-arm": "0.21.5",
|
||||
"@esbuild/linux-arm64": "0.21.5",
|
||||
"@esbuild/linux-ia32": "0.21.5",
|
||||
"@esbuild/linux-loong64": "0.21.5",
|
||||
"@esbuild/linux-mips64el": "0.21.5",
|
||||
"@esbuild/linux-ppc64": "0.21.5",
|
||||
"@esbuild/linux-riscv64": "0.21.5",
|
||||
"@esbuild/linux-s390x": "0.21.5",
|
||||
"@esbuild/linux-x64": "0.21.5",
|
||||
"@esbuild/netbsd-x64": "0.21.5",
|
||||
"@esbuild/openbsd-x64": "0.21.5",
|
||||
"@esbuild/sunos-x64": "0.21.5",
|
||||
"@esbuild/win32-arm64": "0.21.5",
|
||||
"@esbuild/win32-ia32": "0.21.5",
|
||||
"@esbuild/win32-x64": "0.21.5"
|
||||
"@esbuild/aix-ppc64": "0.19.12",
|
||||
"@esbuild/android-arm": "0.19.12",
|
||||
"@esbuild/android-arm64": "0.19.12",
|
||||
"@esbuild/android-x64": "0.19.12",
|
||||
"@esbuild/darwin-arm64": "0.19.12",
|
||||
"@esbuild/darwin-x64": "0.19.12",
|
||||
"@esbuild/freebsd-arm64": "0.19.12",
|
||||
"@esbuild/freebsd-x64": "0.19.12",
|
||||
"@esbuild/linux-arm": "0.19.12",
|
||||
"@esbuild/linux-arm64": "0.19.12",
|
||||
"@esbuild/linux-ia32": "0.19.12",
|
||||
"@esbuild/linux-loong64": "0.19.12",
|
||||
"@esbuild/linux-mips64el": "0.19.12",
|
||||
"@esbuild/linux-ppc64": "0.19.12",
|
||||
"@esbuild/linux-riscv64": "0.19.12",
|
||||
"@esbuild/linux-s390x": "0.19.12",
|
||||
"@esbuild/linux-x64": "0.19.12",
|
||||
"@esbuild/netbsd-x64": "0.19.12",
|
||||
"@esbuild/openbsd-x64": "0.19.12",
|
||||
"@esbuild/sunos-x64": "0.19.12",
|
||||
"@esbuild/win32-arm64": "0.19.12",
|
||||
"@esbuild/win32-ia32": "0.19.12",
|
||||
"@esbuild/win32-x64": "0.19.12"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
@@ -2399,7 +2395,7 @@
|
||||
},
|
||||
"node_modules/rc-util": {
|
||||
"version": "5.44.4",
|
||||
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.44.4.tgz",
|
||||
"resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz",
|
||||
"integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2412,11 +2408,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
@@ -2425,29 +2420,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.3.1"
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-refresh": {
|
||||
"version": "0.17.0",
|
||||
"resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz",
|
||||
"integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
|
||||
"version": "0.14.2",
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
|
||||
"integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2602,9 +2596,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -2656,16 +2650,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.21",
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz",
|
||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.0.tgz",
|
||||
"integrity": "sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.43",
|
||||
"rollup": "^4.20.0"
|
||||
"esbuild": "^0.19.3",
|
||||
"postcss": "^8.4.35",
|
||||
"rollup": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
@@ -2684,7 +2677,6 @@
|
||||
"less": "*",
|
||||
"lightningcss": "^1.21.0",
|
||||
"sass": "*",
|
||||
"sass-embedded": "*",
|
||||
"stylus": "*",
|
||||
"sugarss": "*",
|
||||
"terser": "^5.4.0"
|
||||
@@ -2702,9 +2694,6 @@
|
||||
"sass": {
|
||||
"optional": true
|
||||
},
|
||||
"sass-embedded": {
|
||||
"optional": true
|
||||
},
|
||||
"stylus": {
|
||||
"optional": true
|
||||
},
|
||||
|
||||
@@ -1218,6 +1218,8 @@ function App() {
|
||||
onOpenEcommerce={() => handleSetView("ecommerce")}
|
||||
onOpenScriptReview={() => handleSetView("scriptTokens")}
|
||||
onOpenTokenMonitor={() => handleSetView("tokenUsage")}
|
||||
onSelectView={handleSetView}
|
||||
onOpenImageTool={handleOpenImageWorkbenchTool}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.2 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.6 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.5 MiB |
+100
-17
@@ -8,8 +8,15 @@ import {
|
||||
ThunderboltOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from "react";
|
||||
import WelcomeSplash from "./WelcomeSplash";
|
||||
import type { WebViewKey, WebImageWorkbenchTool } from "../../types";
|
||||
import { useScrollEntrance } from "../../hooks/useScrollEntrance";
|
||||
import WelcomeSplash from "./WelcomeSplash";
|
||||
import ToolboxSection from "./ToolboxSection";
|
||||
import ScriptReviewShowcase from "./ScriptReviewShowcase";
|
||||
import ModelGenerationShowcase from "./ModelGenerationShowcase";
|
||||
import ecommerceTemplate1 from "../../assets/home-features/home-ecommerce-template-1.png";
|
||||
import ecommerceTemplate2 from "../../assets/home-features/home-ecommerce-template-2.png";
|
||||
import ecommerceTemplate3 from "../../assets/home-features/home-ecommerce-template-3.png";
|
||||
|
||||
function ScrollEntrance({ children, className, ...rest }: { children: React.ReactNode; className?: string } & React.HTMLAttributes<HTMLElement>) {
|
||||
const { ref, isVisible } = useScrollEntrance<HTMLElement>();
|
||||
@@ -34,6 +41,8 @@ interface HomePageProps {
|
||||
onOpenEcommerce: () => void;
|
||||
onOpenScriptReview?: () => void;
|
||||
onOpenTokenMonitor?: () => void;
|
||||
onSelectView: (view: WebViewKey) => void;
|
||||
onOpenImageTool?: (tool: WebImageWorkbenchTool) => void;
|
||||
}
|
||||
|
||||
const HOME_BACKGROUND_VIDEO = "https://stringtest.oss-cn-hangzhou.aliyuncs.com/%E6%A0%B7%E7%89%87.mp4";
|
||||
@@ -56,14 +65,14 @@ const HOME_FEATURES = [
|
||||
stats: ["六维评分", "质量量化", "逐项优化"],
|
||||
},
|
||||
{
|
||||
key: "token",
|
||||
eyebrow: "Team Tokens",
|
||||
title: "团队 Token 监控",
|
||||
description: "实时追踪团队 Token 消耗、项目分布和成员使用情况,让预算、配额和成本都能被清楚管理。",
|
||||
key: "model",
|
||||
eyebrow: "AI Generation",
|
||||
title: "模型生成",
|
||||
description: "通过AI模型生成文本、图片、视频,三种模式覆盖全内容类型,Agent对话式交互智能产出。",
|
||||
imageUrl: featureTokenImage,
|
||||
actionLabel: "查看面板",
|
||||
icon: <DashboardOutlined />,
|
||||
stats: ["实时概览", "成员明细", "成本分析"],
|
||||
actionLabel: "开始生成",
|
||||
icon: <ThunderboltOutlined />,
|
||||
stats: ["文本生成", "图片生成", "视频生成"],
|
||||
},
|
||||
{
|
||||
key: "ecommerce",
|
||||
@@ -84,6 +93,34 @@ const HOME_EXPERIENCE_POINTS = [
|
||||
{ label: "电商", meta: "商品视觉", tone: "amber" },
|
||||
];
|
||||
|
||||
const HOME_ECOMMERCE_TEMPLATES = [
|
||||
{
|
||||
title: "卖点详情图",
|
||||
tag: "详情",
|
||||
meta: "中文卖点标注",
|
||||
imageUrl: ecommerceTemplate1,
|
||||
},
|
||||
{
|
||||
title: "场景主图",
|
||||
tag: "主图",
|
||||
meta: "商品氛围构图",
|
||||
imageUrl: ecommerceTemplate2,
|
||||
},
|
||||
{
|
||||
title: "虚拟模特",
|
||||
tag: "模特",
|
||||
meta: "使用场景延展",
|
||||
imageUrl: ecommerceTemplate3,
|
||||
},
|
||||
];
|
||||
|
||||
const HOME_ECOMMERCE_TOOLS = [
|
||||
{ title: "主图", meta: "平台首图" },
|
||||
{ title: "详情", meta: "卖点拆解" },
|
||||
{ title: "模特", meta: "虚拟模特" },
|
||||
{ title: "短视频", meta: "首帧方案" },
|
||||
];
|
||||
|
||||
const HOME_CAROUSEL_SLOTS = [-4, -3, -2, -1, 0, 1, 2, 3, 4];
|
||||
const HOME_CAROUSEL_TRANSITION_MS = 860;
|
||||
|
||||
@@ -122,7 +159,44 @@ function getHomeCarouselCardStyle(offset: number): CSSProperties {
|
||||
} as CSSProperties;
|
||||
}
|
||||
|
||||
function HomePage({ onOpenGenerate, onOpenEcommerce, onOpenScriptReview, onOpenTokenMonitor }: HomePageProps) {
|
||||
function EcommerceFeatureShowcase() {
|
||||
return (
|
||||
<div className="omni-home-ecommerce-showcase">
|
||||
<div className="omni-home-ecommerce-showcase__depth" />
|
||||
<div className="omni-home-ecommerce-showcase__grain" />
|
||||
|
||||
<div className="omni-home-ecommerce-showcase__prompt">
|
||||
<span>商品图 + 生成要求</span>
|
||||
<strong>生成整套电商视觉</strong>
|
||||
<p>主图、详情页、虚拟模特、短视频首帧一次整理。</p>
|
||||
</div>
|
||||
|
||||
<div className="omni-home-ecommerce-showcase__tools" aria-hidden="true">
|
||||
{HOME_ECOMMERCE_TOOLS.map((item) => (
|
||||
<div key={item.title} className="omni-home-ecommerce-showcase__tool">
|
||||
<b>{item.title}</b>
|
||||
<small>{item.meta}</small>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="omni-home-ecommerce-showcase__gallery" aria-hidden="true">
|
||||
{HOME_ECOMMERCE_TEMPLATES.map((item, index) => (
|
||||
<article key={item.title} className={`omni-home-ecommerce-showcase__shot is-${index + 1}`}>
|
||||
<img src={item.imageUrl} alt="" />
|
||||
<div>
|
||||
<span>{item.tag}</span>
|
||||
<strong>{item.title}</strong>
|
||||
<small>{item.meta}</small>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HomePage({ onOpenGenerate, onOpenEcommerce, onOpenScriptReview, onOpenTokenMonitor, onSelectView, onOpenImageTool }: HomePageProps) {
|
||||
const [splashDismissed, setSplashDismissed] = useState(() => sessionStorage.getItem("omniai:splash-seen") === "1");
|
||||
const [activeSlideIndex, setActiveSlideIndex] = useState(0);
|
||||
const [carouselMotion, setCarouselMotion] = useState<HomeCarouselMotion | null>(null);
|
||||
@@ -200,8 +274,8 @@ function HomePage({ onOpenGenerate, onOpenEcommerce, onOpenScriptReview, onOpenT
|
||||
(onOpenScriptReview ?? onOpenGenerate)();
|
||||
return;
|
||||
}
|
||||
if (featureKey === "token") {
|
||||
(onOpenTokenMonitor ?? onOpenGenerate)();
|
||||
if (featureKey === "model") {
|
||||
onOpenGenerate();
|
||||
return;
|
||||
}
|
||||
if (featureKey === "ecommerce") {
|
||||
@@ -266,7 +340,6 @@ function HomePage({ onOpenGenerate, onOpenEcommerce, onOpenScriptReview, onOpenT
|
||||
}}
|
||||
>
|
||||
<img src={slide.imageUrl} alt={slide.title} />
|
||||
{isActive ? <span className="omni-home__carousel-card-label slide-up-in-260">{slide.title}</span> : null}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
@@ -293,7 +366,7 @@ function HomePage({ onOpenGenerate, onOpenEcommerce, onOpenScriptReview, onOpenT
|
||||
|
||||
<main className="omni-home__feature-pages" aria-label="OmniAI 功能介绍">
|
||||
{HOME_FEATURES.map((feature, index) => (
|
||||
<ScrollEntrance key={feature.key} className={`omni-home__feature-page is-${feature.key}${index % 2 ? " is-alt" : ""}`}>
|
||||
<section key={feature.key} className={`omni-home__feature-page is-${feature.key}${index % 2 ? " is-alt" : ""}`}>
|
||||
<div className="omni-home__feature-copy">
|
||||
<span>
|
||||
{feature.icon}
|
||||
@@ -307,17 +380,25 @@ function HomePage({ onOpenGenerate, onOpenEcommerce, onOpenScriptReview, onOpenT
|
||||
</button>
|
||||
</div>
|
||||
<div className="omni-home__feature-visual" aria-hidden="true">
|
||||
<img src={feature.imageUrl} alt="" />
|
||||
{feature.key === "script" ? (
|
||||
<ScriptReviewShowcase />
|
||||
) : feature.key === "model" ? (
|
||||
<ModelGenerationShowcase />
|
||||
) : feature.key === "ecommerce" ? (
|
||||
<EcommerceFeatureShowcase />
|
||||
) : (
|
||||
<img src={feature.imageUrl} alt="" />
|
||||
)}
|
||||
</div>
|
||||
<div className="omni-home__feature-stats" aria-hidden="true">
|
||||
{feature.stats.map((item) => (
|
||||
<span key={item}>{item}</span>
|
||||
))}
|
||||
</div>
|
||||
</ScrollEntrance>
|
||||
</section>
|
||||
))}
|
||||
|
||||
<ScrollEntrance className="omni-home__experience" aria-label="点击体验">
|
||||
<section className="omni-home__experience" aria-label="点击体验">
|
||||
<div className="omni-home__experience-copy">
|
||||
<span>
|
||||
<ThunderboltOutlined />
|
||||
@@ -348,7 +429,9 @@ function HomePage({ onOpenGenerate, onOpenEcommerce, onOpenScriptReview, onOpenT
|
||||
体验电商生成
|
||||
</button>
|
||||
</div>
|
||||
</ScrollEntrance>
|
||||
</section>
|
||||
|
||||
<ToolboxSection onSelectView={onSelectView} onOpenImageTool={onOpenImageTool} />
|
||||
</main>
|
||||
</section>
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
type ShowMode = "agent" | "image" | "video";
|
||||
|
||||
const MODE_TABS = [
|
||||
{ key: "agent" as const, icon: "🤖", title: "Agent 模式", desc: "文本生成,对话式交互,智能推理" },
|
||||
{ key: "image" as const, icon: "🖼️", title: "图片模式", desc: "图像生成,风格迁移,场景合成" },
|
||||
{ key: "video" as const, icon: "🎬", title: "视频模式", desc: "视频生成,动态场景,数字人演绎" },
|
||||
];
|
||||
|
||||
const AGENT_OUTPUTS = [
|
||||
{ tag: "Agent", title: "营销文案", preview: "「未来已来,腕间即达」—— 全新智能手表,让每一秒都充满可能。健康监测、智能提醒、时尚设计,三合一体验。从晨跑配速到会议提醒,它比你更懂你的节奏。" },
|
||||
{ tag: "Agent", title: "产品描述", preview: "搭载最新AI芯片,支持实时心率/血氧/睡眠监测,50米防水,AMOLED视网膜屏,续航长达14天。钛合金表壳 + 硅胶快拆表带,商务运动自由切换。" },
|
||||
{ tag: "Agent", title: "剧本大纲", preview: "第一幕:品牌故事引入 → 第二幕:产品亮点展示 → 第三幕:用户见证与CTA引导。" },
|
||||
];
|
||||
|
||||
const IMAGE_OUTPUTS = [
|
||||
{ tag: "Image", title: "写实风格", icon: "📷", styleClass: "realistic" },
|
||||
{ tag: "Image", title: "插画风格", icon: "🎨", styleClass: "illustration" },
|
||||
{ tag: "Image", title: "电商风格", icon: "🛍️", styleClass: "ecommerce" },
|
||||
];
|
||||
|
||||
const VIDEO_OUTPUTS = [
|
||||
{ tag: "Video", title: "数字人播报", duration: "0:15" },
|
||||
{ tag: "Video", title: "场景动画", duration: "0:30" },
|
||||
{ tag: "Video", title: "产品展示", duration: "0:20" },
|
||||
];
|
||||
|
||||
function ModelGenerationShowcase() {
|
||||
const [mode, setMode] = useState<ShowMode>("agent");
|
||||
const [selectedImageOpt, setSelectedImageOpt] = useState(0);
|
||||
const [, setAnimated] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const el = containerRef.current;
|
||||
if (!el) return;
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry?.isIntersecting) {
|
||||
setAnimated(true);
|
||||
observer.disconnect();
|
||||
}
|
||||
},
|
||||
{ threshold: 0.2 },
|
||||
);
|
||||
observer.observe(el);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
const workflowSteps = [
|
||||
{ label: "选择模式", active: mode === "agent" },
|
||||
{ label: "输入描述", active: mode === "image" },
|
||||
{ label: "AI生成", active: mode === "video" },
|
||||
{ label: "输出内容", active: false },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="omni-model-gen-showcase" ref={containerRef}>
|
||||
{/* Left Panel */}
|
||||
<div className="mgs-left-panel">
|
||||
<div className="mgs-brand-section">
|
||||
<h1>模型生成<br />多模态内容一键产出</h1>
|
||||
<p className="mgs-subtitle">通过AI模型生成文本、图片、视频,三种模式覆盖全内容类型</p>
|
||||
</div>
|
||||
|
||||
<div className="mgs-mode-tabs">
|
||||
{MODE_TABS.map((tab) => (
|
||||
<div
|
||||
key={tab.key}
|
||||
className={`mgs-mode-tab${mode === tab.key ? " is-active" : ""}`}
|
||||
onClick={() => setMode(tab.key)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") setMode(tab.key); }}
|
||||
>
|
||||
<div className="mgs-mode-icon">{tab.icon}</div>
|
||||
<div className="mgs-mode-info">
|
||||
<h3>{tab.title}</h3>
|
||||
<p>{tab.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mgs-workflow">
|
||||
<div className="mgs-workflow-title">工作流程</div>
|
||||
<div className="mgs-workflow-steps">
|
||||
{workflowSteps.map((step, i) => (
|
||||
<span key={step.label}>
|
||||
{i > 0 && <span className="mgs-wf-arrow">→</span>}
|
||||
<span className={`mgs-wf-step${step.active ? " is-active" : ""}`}>{step.label}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Center Area */}
|
||||
<div className="mgs-center-area">
|
||||
<div className="mgs-input-card">
|
||||
{/* Agent Mode */}
|
||||
{mode === "agent" && (
|
||||
<div className="mgs-mode-content is-active">
|
||||
<div className="mgs-card-header">
|
||||
<div className="mgs-card-mode-badge"><span className="mgs-dot" /> Agent 模式</div>
|
||||
<div className="mgs-card-status">在线 · GPT-4o</div>
|
||||
</div>
|
||||
<div className="mgs-prompt-area">
|
||||
<textarea
|
||||
className="mgs-prompt-input"
|
||||
placeholder="描述你的需求... 例如:帮我生成一段智能手表的营销文案,突出健康监测功能"
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
<div className="mgs-options">
|
||||
{["营销文案", "产品描述", "剧本大纲", "数据分析"].map((opt, i) => (
|
||||
<span key={opt} className={`mgs-opt${i === 0 ? " is-selected" : ""}`}>{opt}</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="mgs-agent-output-area">
|
||||
<div className="mgs-agent-result">
|
||||
<div className="mgs-agent-result-label">AI 生成结果</div>
|
||||
<div className="mgs-agent-result-text">
|
||||
「未来已来,腕间即达」—— 全新智能手表,让每一秒都充满可能。健康监测、智能提醒、时尚设计,三合一体验。从晨跑配速到会议提醒,它比你更懂你的节奏。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mgs-chat-input-row">
|
||||
<input className="mgs-chat-input" placeholder="继续对话或修改需求..." readOnly />
|
||||
<button className="mgs-chat-send" type="button" tabIndex={-1}>◆ 生成</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Image Mode */}
|
||||
{mode === "image" && (
|
||||
<div className="mgs-mode-content is-active">
|
||||
<div className="mgs-card-header">
|
||||
<div className="mgs-card-mode-badge"><span className="mgs-dot" /> 图片模式</div>
|
||||
<div className="mgs-card-status">SDXL · Flux</div>
|
||||
</div>
|
||||
<div className="mgs-prompt-area">
|
||||
<textarea
|
||||
className="mgs-prompt-input"
|
||||
placeholder="描述你想生成的图片... 例如:未来科技感城市,赛博朋克风格,霓虹灯光"
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
<div className="mgs-options">
|
||||
{["写实风", "插画风", "电商风", "3D渲染"].map((opt, i) => (
|
||||
<span
|
||||
key={opt}
|
||||
className={`mgs-opt${selectedImageOpt === i ? " is-selected" : ""}`}
|
||||
onClick={() => setSelectedImageOpt(i)}
|
||||
>
|
||||
{opt}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="mgs-img-grid">
|
||||
<div className="mgs-img-cell">🎨</div>
|
||||
<div className="mgs-img-cell">🖼️</div>
|
||||
<div className="mgs-img-cell">✨</div>
|
||||
<div className="mgs-img-cell">🌈</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Video Mode */}
|
||||
{mode === "video" && (
|
||||
<div className="mgs-mode-content is-active">
|
||||
<div className="mgs-card-header">
|
||||
<div className="mgs-card-mode-badge"><span className="mgs-dot" /> 视频模式</div>
|
||||
<div className="mgs-card-status">Sora · Kling</div>
|
||||
</div>
|
||||
<div className="mgs-video-config">
|
||||
<div className="mgs-config-row">
|
||||
<span className="mgs-config-label">分辨率</span>
|
||||
<span className="mgs-config-value">1080p</span>
|
||||
</div>
|
||||
<div className="mgs-config-row">
|
||||
<span className="mgs-config-label">时长</span>
|
||||
<span className="mgs-config-value">15s</span>
|
||||
</div>
|
||||
<div className="mgs-config-row">
|
||||
<span className="mgs-config-label">帧率</span>
|
||||
<span className="mgs-config-value">30fps</span>
|
||||
</div>
|
||||
<div className="mgs-config-row">
|
||||
<span className="mgs-config-label">风格</span>
|
||||
<span className="mgs-config-value">数字人</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mgs-video-preview">
|
||||
<div className="mgs-play-btn">
|
||||
<svg viewBox="0 0 24 24"><polygon points="6,3 20,12 6,21" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mgs-video-timeline">
|
||||
<div className="mgs-video-timeline-fill" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Panel */}
|
||||
<div className="mgs-right-panel">
|
||||
{/* Agent Outputs */}
|
||||
{mode === "agent" && (
|
||||
<div className="mgs-output-section">
|
||||
<div className="mgs-section-label">
|
||||
<span className="mgs-section-dot is-green" />
|
||||
Agent 文本产出
|
||||
</div>
|
||||
<div className="mgs-output-cards">
|
||||
{AGENT_OUTPUTS.map((item) => (
|
||||
<div key={item.title} className="mgs-out-card">
|
||||
<div className="mgs-out-card-top">
|
||||
<span className="mgs-out-tag">{item.tag}</span>
|
||||
<span className="mgs-out-title">{item.title}</span>
|
||||
</div>
|
||||
<div className="mgs-out-preview">{item.preview}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Image Outputs */}
|
||||
{mode === "image" && (
|
||||
<div className="mgs-output-section">
|
||||
<div className="mgs-section-label">
|
||||
<span className="mgs-section-dot is-blue" />
|
||||
图片产出
|
||||
</div>
|
||||
<div className="mgs-output-cards">
|
||||
{IMAGE_OUTPUTS.map((item) => (
|
||||
<div key={item.title} className="mgs-out-card">
|
||||
<div className="mgs-out-card-top">
|
||||
<span className="mgs-out-tag is-img">{item.tag}</span>
|
||||
<span className="mgs-out-title">{item.title}</span>
|
||||
</div>
|
||||
<div className={`mgs-out-img-placeholder is-${item.styleClass}`}>{item.icon}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Video Outputs */}
|
||||
{mode === "video" && (
|
||||
<div className="mgs-output-section">
|
||||
<div className="mgs-section-label">
|
||||
<span className="mgs-section-dot is-purple" />
|
||||
视频产出
|
||||
</div>
|
||||
<div className="mgs-output-cards">
|
||||
{VIDEO_OUTPUTS.map((item) => (
|
||||
<div key={item.title} className="mgs-out-card">
|
||||
<div className="mgs-out-card-top">
|
||||
<span className="mgs-out-tag is-video">{item.tag}</span>
|
||||
<span className="mgs-out-title">{item.title}</span>
|
||||
</div>
|
||||
<div className="mgs-out-video-placeholder">
|
||||
<div className="mgs-mini-play">
|
||||
<svg viewBox="0 0 24 24"><polygon points="6,3 20,12 6,21" /></svg>
|
||||
</div>
|
||||
<span className="mgs-video-duration">{item.duration}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModelGenerationShowcase;
|
||||
@@ -0,0 +1,208 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
const DIMS = [
|
||||
{ name: "钩子设计", score: 16, max: 20, hue: 145, desc: "吸引力·悬念·黄金三秒", isPerfect: false, isLow: false },
|
||||
{ name: "角色塑造", score: 15, max: 15, hue: 155, desc: "立体度·动机·弧光", isPerfect: true, isLow: false },
|
||||
{ name: "剧情结构", score: 16, max: 20, hue: 165, desc: "起承转合·节奏·冲突", isPerfect: false, isLow: false },
|
||||
{ name: "逻辑严密", score: 12, max: 15, hue: 175, desc: "自洽·伏笔·因果链", isPerfect: false, isLow: false },
|
||||
{ name: "场景构建", score: 10, max: 15, hue: 185, desc: "空间·视听·画面感", isPerfect: false, isLow: true },
|
||||
{ name: "内容深度", score: 8, max: 15, hue: 195, desc: "主题·情感·思想内核", isPerfect: false, isLow: true },
|
||||
];
|
||||
|
||||
const HIGHLIGHTS = [
|
||||
{ dim: "角色塑造", score: "15/15 ★", text: "满分表现!人物立体度与弧光设计均达高水准" },
|
||||
{ dim: "钩子设计", score: "16/20", text: "开篇悬念精准,黄金三秒有效抓住注意力" },
|
||||
{ dim: "剧情结构", score: "16/20", text: "起承转合清晰,节奏把控得当,冲突自然递进" },
|
||||
];
|
||||
|
||||
const WEAKNESSES = [
|
||||
{ dim: "内容深度", score: "8/15", text: "主题偏表层,情感共鸣与思想内核挖掘不足" },
|
||||
{ dim: "场景构建", score: "10/15", text: "空间描写模糊,视听语言薄弱,画面感欠缺" },
|
||||
{ dim: "逻辑严密", score: "12/15", text: "世界观细节不足,部分伏笔缺乏回收" },
|
||||
];
|
||||
|
||||
const OPTIMIZATIONS = [
|
||||
{ dim: "场景构建 → 提升", priority: "高优先", priorityClass: "badge-red", text: "增加具体空间描写与视听语言,强化沉浸感" },
|
||||
{ dim: "内容深度 → 深挖", priority: "高优先", priorityClass: "badge-red", text: "围绕核心冲突深化人物选择与情感层次" },
|
||||
{ dim: "逻辑严密 → 补强", priority: "中优先", priorityClass: "badge-orange", text: "补充世界观细节,强化因果链与伏笔回收" },
|
||||
];
|
||||
|
||||
function animateNumber(el: HTMLElement | null, target: number, duration: number) {
|
||||
if (!el) return;
|
||||
const start = performance.now();
|
||||
const targetEl = el;
|
||||
function tick(now: number) {
|
||||
const p = Math.min((now - start) / duration, 1);
|
||||
targetEl.textContent = String(Math.round((1 - Math.pow(1 - p, 3)) * target));
|
||||
if (p < 1) requestAnimationFrame(tick);
|
||||
}
|
||||
requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
function ScriptReviewShowcase() {
|
||||
const [animated, setAnimated] = useState(false);
|
||||
const scoreRef = useRef<HTMLSpanElement>(null);
|
||||
const barRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||
const scoreValRefs = useRef<(HTMLSpanElement | null)[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const el = document.getElementById("script-review-showcase");
|
||||
if (!el) return;
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry?.isIntersecting) {
|
||||
setAnimated(true);
|
||||
observer.disconnect();
|
||||
}
|
||||
},
|
||||
{ threshold: 0.25 },
|
||||
);
|
||||
observer.observe(el);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!animated) return;
|
||||
const timer = setTimeout(() => {
|
||||
animateNumber(scoreRef.current, 77, 1400);
|
||||
barRefs.current.forEach((bar, i) => {
|
||||
if (!bar) return;
|
||||
const pct = parseFloat(bar.dataset.pct ?? "0");
|
||||
setTimeout(() => { bar.style.height = `${pct}%`; }, i * 100 + 400);
|
||||
});
|
||||
scoreValRefs.current.forEach((el, i) => {
|
||||
setTimeout(() => animateNumber(el, parseInt(el?.dataset.target ?? "0"), 800), i * 100 + 400);
|
||||
});
|
||||
}, 500);
|
||||
return () => clearTimeout(timer);
|
||||
}, [animated]);
|
||||
|
||||
return (
|
||||
<div className="omni-script-review-showcase" id="script-review-showcase">
|
||||
{/* Score Hero */}
|
||||
<div className="srs-score-hero">
|
||||
<div className="srs-score-left">
|
||||
<div className="srs-score-circle">
|
||||
<div className="srs-score-circle-inner">
|
||||
<span className="srs-score-num" ref={scoreRef}>0</span>
|
||||
<span className="srs-score-den">/ 100</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="srs-score-meta">
|
||||
<div className="srs-score-grade">A 级</div>
|
||||
<div className="srs-score-tags">
|
||||
<span className="srs-score-tag">现实剧情</span>
|
||||
<span className="srs-score-tag">58min</span>
|
||||
<span className="srs-score-tag">6角色</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="srs-score-divider" />
|
||||
<div className="srs-score-right">
|
||||
<div className="srs-score-proj">电商广告片生成项目计划 · 评测结果</div>
|
||||
<div className="srs-score-summary">
|
||||
现实剧情特征清晰,角色塑造表现突出。当前最值得继续打磨的是内容深度,建议围绕人物选择、冲突升级和可拍摄细节继续压实。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Vertical Bar Chart */}
|
||||
<div className="srs-chart-card">
|
||||
<div className="srs-chart-title">六维评分 Dimension Breakdown</div>
|
||||
<div className="srs-chart-body">
|
||||
{DIMS.map((dim, i) => {
|
||||
const pct = dim.score / dim.max;
|
||||
return (
|
||||
<div key={dim.name} className="srs-chart-col">
|
||||
<div className="srs-chart-bar-wrap">
|
||||
<div className="srs-chart-bar-bg" style={{ height: "100%" }} />
|
||||
<div
|
||||
ref={(el) => { barRefs.current[i] = el; }}
|
||||
className={`srs-chart-bar-fill${dim.isPerfect ? " is-perfect" : ""}${dim.isLow ? " is-low" : ""}`}
|
||||
data-pct={String(Math.round(pct * 100))}
|
||||
style={{ height: "0%" }}
|
||||
>
|
||||
<div className="srs-chart-bar-score">
|
||||
<span
|
||||
ref={(el) => { scoreValRefs.current[i] = el; }}
|
||||
data-target={String(dim.score)}
|
||||
>0</span>
|
||||
<span className="srs-chart-bar-sub">/{dim.max}</span>
|
||||
{dim.isPerfect && <span className="srs-chart-bar-star">★</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="srs-chart-col-label">
|
||||
<div className="srs-chart-col-name">{dim.name}</div>
|
||||
<div className="srs-chart-col-desc">{dim.desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Triple Section */}
|
||||
<div className="srs-triple-section">
|
||||
{/* Highlights */}
|
||||
<div className="srs-section-card is-highlight">
|
||||
<div className="srs-section-header">
|
||||
<div className="srs-section-icon">✦</div>
|
||||
<span className="srs-section-label">亮点</span>
|
||||
</div>
|
||||
<div className="srs-section-list">
|
||||
{HIGHLIGHTS.map((item) => (
|
||||
<div key={item.dim} className="srs-section-item">
|
||||
<div className="srs-section-item-head">
|
||||
<span className="srs-section-item-dim">{item.dim}</span>
|
||||
<span className="srs-section-item-score is-green">{item.score}</span>
|
||||
</div>
|
||||
<div className="srs-section-item-text">{item.text}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Weaknesses */}
|
||||
<div className="srs-section-card is-weakness">
|
||||
<div className="srs-section-header">
|
||||
<div className="srs-section-icon">✗</div>
|
||||
<span className="srs-section-label">缺点</span>
|
||||
</div>
|
||||
<div className="srs-section-list">
|
||||
{WEAKNESSES.map((item) => (
|
||||
<div key={item.dim} className="srs-section-item">
|
||||
<div className="srs-section-item-head">
|
||||
<span className="srs-section-item-dim">{item.dim}</span>
|
||||
<span className="srs-section-item-score is-red">{item.score}</span>
|
||||
</div>
|
||||
<div className="srs-section-item-text">{item.text}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Optimization */}
|
||||
<div className="srs-section-card is-optimize">
|
||||
<div className="srs-section-header">
|
||||
<div className="srs-section-icon">⚡</div>
|
||||
<span className="srs-section-label">优化路径</span>
|
||||
</div>
|
||||
<div className="srs-section-list">
|
||||
{OPTIMIZATIONS.map((item) => (
|
||||
<div key={item.dim} className="srs-section-item">
|
||||
<div className="srs-section-item-head">
|
||||
<span className="srs-section-item-dim">{item.dim}</span>
|
||||
<span className={`srs-section-item-badge ${item.priorityClass}`}>{item.priority}</span>
|
||||
</div>
|
||||
<div className="srs-section-item-text">{item.text}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ScriptReviewShowcase;
|
||||
@@ -0,0 +1,133 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
const DIMS = [
|
||||
{ name: "钩子设计", score: 19, max: 20, hue: 145 },
|
||||
{ name: "角色塑造", score: 13, max: 15, hue: 155 },
|
||||
{ name: "剧情结构", score: 18, max: 20, hue: 165 },
|
||||
{ name: "逻辑严密", score: 14, max: 15, hue: 175 },
|
||||
{ name: "场景构建", score: 15, max: 15, hue: 185 },
|
||||
{ name: "内容深度", score: 15, max: 15, hue: 195 },
|
||||
];
|
||||
|
||||
function ScriptReviewVisual() {
|
||||
const [animated, setAnimated] = useState(false);
|
||||
const [activeDim, setActiveDim] = useState<number | null>(null);
|
||||
const [score, setScore] = useState(0);
|
||||
const scoreRef = useRef<number>(0);
|
||||
const frameRef = useRef<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const el = document.getElementById("script-review-visual");
|
||||
if (!el) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry?.isIntersecting) {
|
||||
setAnimated(true);
|
||||
observer.disconnect();
|
||||
}
|
||||
},
|
||||
{ threshold: 0.3 }
|
||||
);
|
||||
observer.observe(el);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!animated) return;
|
||||
const start = performance.now();
|
||||
const target = 94;
|
||||
const dur = 1400;
|
||||
function tick(now: number) {
|
||||
const t = Math.min((now - start) / dur, 1);
|
||||
const e = 1 - Math.pow(1 - t, 3);
|
||||
setScore(Math.round(e * target));
|
||||
if (t < 1) frameRef.current = requestAnimationFrame(tick);
|
||||
}
|
||||
frameRef.current = requestAnimationFrame(tick);
|
||||
return () => { if (frameRef.current) cancelAnimationFrame(frameRef.current); };
|
||||
}, [animated]);
|
||||
|
||||
const totalScore = 94;
|
||||
const grade = "S";
|
||||
|
||||
return (
|
||||
<div className="omni-script-review-visual" id="script-review-visual">
|
||||
<div className="omni-script-review-hero">
|
||||
<div className="omni-script-review-score-row">
|
||||
<span className="omni-script-review-num">{score}</span>
|
||||
<span className="omni-script-review-total">/ 100</span>
|
||||
<div className="omni-script-review-grade">
|
||||
<span className="omni-script-review-grade-dot" />
|
||||
<span>{grade}级</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="omni-script-review-bar">
|
||||
<div
|
||||
className="omni-script-review-bar-fill"
|
||||
style={{ width: animated ? `${totalScore}%` : "0%" }}
|
||||
/>
|
||||
</div>
|
||||
<div className="omni-script-review-beat">
|
||||
击败全国 <b>92%</b> 剧本
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="omni-script-review-chart">
|
||||
<div className="omni-script-review-chart-bars">
|
||||
{DIMS.map((dim, i) => {
|
||||
const pct = dim.score / dim.max;
|
||||
const lossPct = (dim.max - dim.score) / dim.max;
|
||||
const isPerfect = dim.score === dim.max;
|
||||
const height = animated ? pct * 76 : 0;
|
||||
const lossHeight = animated ? lossPct * 76 : 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={dim.name}
|
||||
className={`omni-script-review-bcol${activeDim === i ? " is-active" : ""}${activeDim !== null && activeDim !== i ? " is-dimmed" : ""}`}
|
||||
onClick={() => setActiveDim(activeDim === i ? null : i)}
|
||||
>
|
||||
<div className="omni-script-review-bbar-area">
|
||||
{lossPct > 0 && (
|
||||
<div
|
||||
className="omni-script-review-bseg is-loss"
|
||||
style={{ height: `${lossHeight}%`, transitionDelay: `${400 + i * 80}ms` }}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`omni-script-review-bseg is-score${isPerfect ? " is-perfect" : ""}`}
|
||||
style={{ height: `${height}%`, transitionDelay: `${400 + i * 80}ms` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="omni-script-review-blabel">
|
||||
<span>{dim.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{activeDim !== null && (() => {
|
||||
const d = DIMS[activeDim]!;
|
||||
return (
|
||||
<div className="omni-script-review-diminfo">
|
||||
<span className="omni-script-review-diminfo-name">{d.name}</span>
|
||||
<span className="omni-script-review-diminfo-score">
|
||||
{d.score}<small>/{d.max}</small>
|
||||
{d.score === d.max && " ★"}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
<div className="omni-script-review-legend">
|
||||
<span><span className="omni-script-review-legend-dot is-score" /> 得分</span>
|
||||
<span><span className="omni-script-review-legend-dot is-loss" /> 扣分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ScriptReviewVisual;
|
||||
@@ -0,0 +1,233 @@
|
||||
import { ToolOutlined } from "@ant-design/icons";
|
||||
import type { WebViewKey, WebImageWorkbenchTool } from "../../types";
|
||||
import toolImageBefore from "../../assets/toolbox/牛仔.png";
|
||||
import toolImageAfter from "../../assets/toolbox/西装.png";
|
||||
import watermarkBefore from "../../assets/toolbox/去水印前.png";
|
||||
import watermarkAfter from "../../assets/toolbox/去水印后.png";
|
||||
|
||||
interface ToolboxSectionProps {
|
||||
onSelectView: (view: WebViewKey) => void;
|
||||
onOpenImageTool?: (tool: WebImageWorkbenchTool) => void;
|
||||
}
|
||||
|
||||
const TOOLS = [
|
||||
{
|
||||
key: "image-studio",
|
||||
icon: "🎨",
|
||||
name: "图片工作室",
|
||||
desc: "图片二次加工,调色裁剪特效风格迁移",
|
||||
},
|
||||
{
|
||||
key: "lens-lab",
|
||||
icon: "📷",
|
||||
name: "镜头实验室",
|
||||
desc: "多视角镜头生成,不同角度与姿势",
|
||||
},
|
||||
{
|
||||
key: "digital-human",
|
||||
icon: "🧑",
|
||||
name: "一键数字人",
|
||||
desc: "上传图片和音频,生成数字人视频",
|
||||
},
|
||||
{
|
||||
key: "watermark-removal",
|
||||
icon: "✨",
|
||||
name: "去除水印",
|
||||
desc: "AI智能识别去除图片视频水印",
|
||||
},
|
||||
];
|
||||
|
||||
const CARDS = [
|
||||
{
|
||||
key: "image-studio",
|
||||
title: "图片工作室",
|
||||
tag: "图片加工",
|
||||
icon: "🎨",
|
||||
features: ["二次加工", "调色", "裁剪", "风格迁移"],
|
||||
targetView: "imageWorkbench" as WebViewKey,
|
||||
render: () => (
|
||||
<div className="toolbox-card1-content">
|
||||
<div className="toolbox-card1-side toolbox-card1-left">
|
||||
<div className="toolbox-card1-img">
|
||||
<img src={toolImageBefore} alt="图片加工前" />
|
||||
</div>
|
||||
<div className="toolbox-card1-label">原始图片</div>
|
||||
</div>
|
||||
<div className="toolbox-card1-divider" />
|
||||
<div className="toolbox-card1-side toolbox-card1-right">
|
||||
<div className="toolbox-card1-img">
|
||||
<img src={toolImageAfter} alt="图片加工后" />
|
||||
</div>
|
||||
<div className="toolbox-card1-label">处理后</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "lens-lab",
|
||||
title: "镜头实验室",
|
||||
tag: "多视角",
|
||||
icon: "📷",
|
||||
features: ["正面", "45°侧", "俯拍", "仰拍", "背面"],
|
||||
targetView: "imageWorkbench" as WebViewKey,
|
||||
render: () => (
|
||||
<div className="toolbox-card2-content">
|
||||
{["正面", "45°侧", "俯拍", "仰拍", "背面"].map((angle) => (
|
||||
<div key={angle} className="toolbox-card2-frame">
|
||||
<div className="toolbox-card2-product" />
|
||||
<div className="toolbox-card2-shadow" />
|
||||
<div className="toolbox-card2-angle-label">{angle}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "digital-human",
|
||||
title: "一键数字人",
|
||||
tag: "视频生成",
|
||||
icon: "🧑",
|
||||
features: ["上传人像", "匹配音频", "唇形同步", "生成视频"],
|
||||
targetView: "digitalHuman" as WebViewKey,
|
||||
render: () => (
|
||||
<div className="toolbox-card3-content">
|
||||
<div className="toolbox-card3-side toolbox-card3-left">
|
||||
<div className="toolbox-card3-portrait">
|
||||
<div className="toolbox-card3-portrait-mark">STATIC</div>
|
||||
</div>
|
||||
<div className="toolbox-card3-label">静态人像</div>
|
||||
</div>
|
||||
<div className="toolbox-card3-divider" />
|
||||
<div className="toolbox-card3-transform">⚡</div>
|
||||
<div className="toolbox-card3-side toolbox-card3-right">
|
||||
<div className="toolbox-card3-portrait">
|
||||
<div className="toolbox-card3-glow-ring" />
|
||||
<div className="toolbox-card3-lipsync">
|
||||
<span /><span /><span /><span /><span />
|
||||
</div>
|
||||
<div className="toolbox-card3-gesture" />
|
||||
<div className="toolbox-card3-live">LIVE</div>
|
||||
</div>
|
||||
<div className="toolbox-card3-label">数字人视频</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "watermark-removal",
|
||||
title: "去除水印",
|
||||
tag: "AI清除",
|
||||
icon: "✨",
|
||||
features: ["智能识别", "精准去除", "无损画质"],
|
||||
targetView: "watermarkRemoval" as WebViewKey,
|
||||
render: () => (
|
||||
<div className="toolbox-card4-content">
|
||||
<div className="toolbox-card4-side toolbox-card4-left">
|
||||
<div className="toolbox-card4-img">
|
||||
<img src={watermarkBefore} alt="去水印前" />
|
||||
</div>
|
||||
<div className="toolbox-card4-label">含水印</div>
|
||||
</div>
|
||||
<div className="toolbox-card4-divider" />
|
||||
<div className="toolbox-card4-side toolbox-card4-right">
|
||||
<div className="toolbox-card4-img">
|
||||
<img src={watermarkAfter} alt="去水印后" />
|
||||
</div>
|
||||
<div className="toolbox-card4-label">已清除</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
function ToolboxSection({ onSelectView, onOpenImageTool }: ToolboxSectionProps) {
|
||||
const handleCardClick = (targetView: WebViewKey) => {
|
||||
onSelectView(targetView);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="omni-home__toolbox-page" aria-label="OmniAI 工具箱">
|
||||
<div className="omni-home__toolbox-shell">
|
||||
{/* Left Panel */}
|
||||
<aside className="omni-home__toolbox-left">
|
||||
<div className="omni-home__toolbox-brand">
|
||||
<div className="omni-home__toolbox-brand-icon">
|
||||
<ToolOutlined />
|
||||
</div>
|
||||
<div className="omni-home__toolbox-brand-text">工具箱</div>
|
||||
</div>
|
||||
<div className="omni-home__toolbox-title">
|
||||
专业工具
|
||||
<br />
|
||||
精准创作
|
||||
</div>
|
||||
<div className="omni-home__toolbox-subtitle">
|
||||
四大AI工具覆盖图片加工、镜头变换、数字人制作、水印处理全流程
|
||||
</div>
|
||||
<div className="omni-home__toolbox-list">
|
||||
{TOOLS.map((tool) => (
|
||||
<div
|
||||
key={tool.key}
|
||||
className="omni-home__toolbox-item"
|
||||
onClick={() => {
|
||||
const card = CARDS.find((c) => c.key === tool.key);
|
||||
if (card) handleCardClick(card.targetView);
|
||||
}}
|
||||
>
|
||||
<div className="omni-home__toolbox-item-icon">{tool.icon}</div>
|
||||
<div className="omni-home__toolbox-item-info">
|
||||
<div className="omni-home__toolbox-item-name">{tool.name}</div>
|
||||
<div className="omni-home__toolbox-item-desc">{tool.desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="omni-home__toolbox-workflow">
|
||||
<div className="omni-home__toolbox-workflow-label">通用工作流</div>
|
||||
<div className="omni-home__toolbox-workflow-steps">
|
||||
<span className="omni-home__toolbox-workflow-step">上传素材</span>
|
||||
<span className="omni-home__toolbox-workflow-arrow">→</span>
|
||||
<span className="omni-home__toolbox-workflow-step">选择工具</span>
|
||||
<span className="omni-home__toolbox-workflow-arrow">→</span>
|
||||
<span className="omni-home__toolbox-workflow-step">AI处理</span>
|
||||
<span className="omni-home__toolbox-workflow-arrow">→</span>
|
||||
<span className="omni-home__toolbox-workflow-step">导出成果</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Grid Area */}
|
||||
<div className="omni-home__toolbox-grid">
|
||||
{CARDS.map((card) => (
|
||||
<div
|
||||
key={card.key}
|
||||
className="omni-home__toolbox-card"
|
||||
onClick={() => handleCardClick(card.targetView)}
|
||||
>
|
||||
<div className="omni-home__toolbox-card-header">
|
||||
<div className="omni-home__toolbox-card-header-left">
|
||||
<div className="omni-home__toolbox-card-icon">{card.icon}</div>
|
||||
<div className="omni-home__toolbox-card-title">{card.title}</div>
|
||||
</div>
|
||||
<div className="omni-home__toolbox-card-tag">{card.tag}</div>
|
||||
</div>
|
||||
<div className="omni-home__toolbox-card-content">
|
||||
{card.render()}
|
||||
</div>
|
||||
<div className="omni-home__toolbox-card-footer">
|
||||
{card.features.map((feat, i) => (
|
||||
<span key={feat}>
|
||||
{i > 0 && <span className="omni-home__toolbox-card-feat-sep">|</span>}
|
||||
<span className="omni-home__toolbox-card-feat">{feat}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default ToolboxSection;
|
||||
@@ -1,13 +1,20 @@
|
||||
import { CopyOutlined, DownOutlined, DownloadOutlined, FileTextOutlined, ReloadOutlined, TrophyOutlined, UploadOutlined } from "@ant-design/icons";
|
||||
import { useEffect, useMemo, useRef, useState, type ChangeEvent, type KeyboardEvent } from "react";
|
||||
import {
|
||||
CheckCircleFilled,
|
||||
CopyOutlined,
|
||||
DownloadOutlined,
|
||||
FileTextOutlined,
|
||||
UploadOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useEffect, useRef, useState, type ChangeEvent, type KeyboardEvent } from "react";
|
||||
import { evaluateScript } from "../../api/scriptEvalClient";
|
||||
import { useSessionStore } from "../../stores";
|
||||
|
||||
interface ScoreDimension {
|
||||
key: string;
|
||||
label: string;
|
||||
maxScore: number;
|
||||
weight: number;
|
||||
description: string;
|
||||
hint: string;
|
||||
detail: string;
|
||||
}
|
||||
|
||||
interface EvalResult {
|
||||
@@ -20,103 +27,156 @@ interface EvalResult {
|
||||
suggestions: string[];
|
||||
}
|
||||
|
||||
const RADAR_CENTER = 100;
|
||||
const RADAR_RADIUS = 82;
|
||||
const RADAR_ANGLES = [-90, -30, 30, 90, 150, 210];
|
||||
|
||||
const scoreDimensions: ScoreDimension[] = [
|
||||
{
|
||||
key: "hook",
|
||||
label: "钩子设计",
|
||||
maxScore: 20,
|
||||
weight: 0.2,
|
||||
description: "开篇吸引力、悬念设置、黄金三秒法则",
|
||||
},
|
||||
{
|
||||
key: "character",
|
||||
label: "角色塑造",
|
||||
maxScore: 18,
|
||||
weight: 0.18,
|
||||
description: "主角弧光、角色辨识度、动机、配角质量",
|
||||
},
|
||||
{
|
||||
key: "plot",
|
||||
label: "剧情结构",
|
||||
maxScore: 20,
|
||||
weight: 0.2,
|
||||
description: "起承转合、节奏把控、冲突设计",
|
||||
},
|
||||
{
|
||||
key: "dialogue",
|
||||
label: "台词对白",
|
||||
maxScore: 15,
|
||||
weight: 0.15,
|
||||
description: "语言质感、角色差异化、潜台词",
|
||||
},
|
||||
{
|
||||
key: "visual",
|
||||
label: "画面表现",
|
||||
maxScore: 15,
|
||||
weight: 0.15,
|
||||
description: "镜头感、空间层次、视觉冲击力",
|
||||
},
|
||||
{
|
||||
key: "content",
|
||||
label: "内容深度",
|
||||
maxScore: 12,
|
||||
weight: 0.12,
|
||||
description: "主题表达、情感共鸣、社会/人性洞察",
|
||||
},
|
||||
];
|
||||
|
||||
function radarPoint(angle: number, radius: number) {
|
||||
const radians = (angle * Math.PI) / 180;
|
||||
return {
|
||||
x: RADAR_CENTER + radius * Math.cos(radians),
|
||||
y: RADAR_CENTER + radius * Math.sin(radians),
|
||||
};
|
||||
interface HistoryEntry {
|
||||
name: string;
|
||||
date: string;
|
||||
timestamp: number;
|
||||
score: number;
|
||||
grade: string;
|
||||
}
|
||||
|
||||
function makeRadarPoints(scores: Record<string, number> | null) {
|
||||
if (!scores) return "100,100 100,100 100,100 100,100 100,100 100,100";
|
||||
|
||||
return scoreDimensions
|
||||
.map((dimension, index) => {
|
||||
const ratio = Math.max(0, Math.min(1, (scores[dimension.key] ?? 0) / dimension.maxScore));
|
||||
const point = radarPoint(RADAR_ANGLES[index] ?? 0, RADAR_RADIUS * ratio);
|
||||
return `${point.x.toFixed(1)},${point.y.toFixed(1)}`;
|
||||
})
|
||||
.join(" ");
|
||||
function getGrade(score: number): string {
|
||||
if (score >= 97) return "S+";
|
||||
if (score >= 93) return "S";
|
||||
if (score >= 88) return "A+";
|
||||
if (score >= 83) return "A";
|
||||
if (score >= 78) return "B+";
|
||||
if (score >= 70) return "B";
|
||||
return "C";
|
||||
}
|
||||
|
||||
function RadarPreview({ result }: { result: EvalResult | null }) {
|
||||
const HISTORY_KEY = "omniai:script-eval-history";
|
||||
const TEXT_FILE_EXTENSIONS = [
|
||||
".txt",
|
||||
".text",
|
||||
".md",
|
||||
".markdown",
|
||||
".fountain",
|
||||
".fdx",
|
||||
".rtf",
|
||||
".csv",
|
||||
".tsv",
|
||||
".json",
|
||||
".jsonl",
|
||||
".xml",
|
||||
".html",
|
||||
".htm",
|
||||
".yaml",
|
||||
".yml",
|
||||
".toml",
|
||||
".ini",
|
||||
".conf",
|
||||
".cfg",
|
||||
".properties",
|
||||
".log",
|
||||
".srt",
|
||||
".ass",
|
||||
".ssa",
|
||||
".vtt",
|
||||
".sql",
|
||||
".js",
|
||||
".jsx",
|
||||
".ts",
|
||||
".tsx",
|
||||
".py",
|
||||
".java",
|
||||
".c",
|
||||
".cpp",
|
||||
".h",
|
||||
".hpp",
|
||||
".cs",
|
||||
".go",
|
||||
".rs",
|
||||
".php",
|
||||
".rb",
|
||||
".sh",
|
||||
".bat",
|
||||
".ps1",
|
||||
".lua",
|
||||
".swift",
|
||||
".kt",
|
||||
".kts",
|
||||
] as const;
|
||||
const TEXT_FILE_EXTENSION_SET = new Set<string>(TEXT_FILE_EXTENSIONS);
|
||||
const TEXT_FILE_ACCEPT = TEXT_FILE_EXTENSIONS.join(",");
|
||||
const TEXT_FILE_HINT = "支持常见文本格式:TXT / MD / Fountain / FDX / RTF / JSON / CSV / XML / HTML / YAML / LOG / 字幕等";
|
||||
|
||||
function loadHistory(): HistoryEntry[] {
|
||||
try {
|
||||
const raw = localStorage.getItem(HISTORY_KEY);
|
||||
return raw ? (JSON.parse(raw) as HistoryEntry[]).sort((a, b) => b.timestamp - a.timestamp) : [];
|
||||
} catch { return []; }
|
||||
}
|
||||
|
||||
function saveHistory(entries: HistoryEntry[]) {
|
||||
try { localStorage.setItem(HISTORY_KEY, JSON.stringify(entries.sort((a, b) => b.timestamp - a.timestamp).slice(0, 20))); } catch { /* quota exceeded */ }
|
||||
}
|
||||
|
||||
function getFileExtension(filename: string): string {
|
||||
const dotIndex = filename.lastIndexOf(".");
|
||||
return dotIndex >= 0 ? filename.slice(dotIndex).toLowerCase() : "";
|
||||
}
|
||||
|
||||
function isReadableTextFile(file: File, ext: string): boolean {
|
||||
const mime = file.type.toLowerCase();
|
||||
return (
|
||||
<div className={`script-eval-v4-radar-container${result ? " has-glow" : ""}`}>
|
||||
<svg className="script-eval-v4-radar-svg" viewBox="0 0 200 200" aria-hidden="true">
|
||||
<defs>
|
||||
<linearGradient id="scriptEvalV4RadarGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stopColor="rgba(0, 255, 136, 0.34)" />
|
||||
<stop offset="100%" stopColor="rgba(123, 231, 255, 0.1)" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g className="script-eval-v4-radar-grid">
|
||||
<polygon points="100,15 173,55 173,145 100,185 27,145 27,55" />
|
||||
<polygon points="100,35 158,68 158,132 100,165 42,132 42,68" />
|
||||
<polygon points="100,55 143,81 143,119 100,145 57,119 57,81" />
|
||||
<polygon points="100,75 128,94 128,106 100,125 72,106 72,94" />
|
||||
<line x1="100" y1="15" x2="100" y2="185" />
|
||||
<line x1="27" y1="55" x2="173" y2="145" />
|
||||
<line x1="173" y1="55" x2="27" y2="145" />
|
||||
</g>
|
||||
<polygon
|
||||
className={`script-eval-v4-radar-outline${result ? " has-data" : ""}`}
|
||||
points={makeRadarPoints(result?.dimensionScores ?? null)}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
TEXT_FILE_EXTENSION_SET.has(ext) ||
|
||||
mime.startsWith("text/") ||
|
||||
mime === "application/json" ||
|
||||
mime === "application/xml" ||
|
||||
mime === "application/xhtml+xml" ||
|
||||
mime === "application/x-yaml" ||
|
||||
mime === "application/yaml"
|
||||
);
|
||||
}
|
||||
|
||||
async function decodeTextFile(file: File): Promise<string> {
|
||||
const bytes = await file.arrayBuffer();
|
||||
const utf8 = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
||||
if (!utf8.includes("\uFFFD")) return utf8;
|
||||
|
||||
try {
|
||||
return new TextDecoder("gb18030", { fatal: false }).decode(bytes);
|
||||
} catch {
|
||||
return utf8;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeUploadedText(raw: string, ext: string): string {
|
||||
if (ext === ".rtf") {
|
||||
const text = raw
|
||||
.replace(/\\par[d]?/gi, "\n")
|
||||
.replace(/\\line/gi, "\n")
|
||||
.replace(/\\'[0-9a-f]{2}/gi, "")
|
||||
.replace(/\\[a-z]+\d* ?/gi, "")
|
||||
.replace(/[{}]/g, "")
|
||||
.replace(/\n{3,}/g, "\n\n")
|
||||
.trim();
|
||||
return text || raw;
|
||||
}
|
||||
|
||||
if ([".html", ".htm", ".xml", ".fdx"].includes(ext) && typeof DOMParser !== "undefined") {
|
||||
try {
|
||||
const doc = new DOMParser().parseFromString(raw, ext === ".html" || ext === ".htm" ? "text/html" : "application/xml");
|
||||
const text = doc.documentElement.textContent?.replace(/\n{3,}/g, "\n\n").trim();
|
||||
return text || raw;
|
||||
} catch {
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
const SCORE_DIMENSIONS: ScoreDimension[] = [
|
||||
{ key: "hook", label: "钩子设计", maxScore: 20, hint: "开篇吸引力·悬念设置·黄金三秒", detail: "开篇即抛出高概念钩子,悬念设置紧凑有力。" },
|
||||
{ key: "character", label: "角色塑造", maxScore: 15, hint: "人物立体度·动机合理性·弧光设计", detail: "主角动机有铺垫,配角功能性较强,人物弧光尚可进一步深化。" },
|
||||
{ key: "plot", label: "剧情结构", maxScore: 20, hint: "起承转合·节奏把控·冲突设计", detail: "起承转合完整,节奏把控稳健,冲突设计有张力。" },
|
||||
{ key: "logic", label: "逻辑严密", maxScore: 15, hint: "世界观自洽·伏笔回收·因果链", detail: "世界观整体自洽,伏笔设置到位。" },
|
||||
{ key: "visual", label: "场景构建", maxScore: 15, hint: "空间描写·视听语言·画面想象力", detail: "视觉意象统一而强烈,场景描写极具画面感。" },
|
||||
{ key: "content", label: "内容深度", maxScore: 15, hint: "主题表达·情感共鸣·思想内核", detail: "核心设定将科技伦理与人性困境紧密结合,主题表达深刻有力。" },
|
||||
];
|
||||
|
||||
function formatReportMarkdown(result: EvalResult, script: string): string {
|
||||
const lines: string[] = [];
|
||||
lines.push(`# 剧本评测报告`);
|
||||
@@ -127,10 +187,10 @@ function formatReportMarkdown(result: EvalResult, script: string): string {
|
||||
lines.push(result.summary);
|
||||
lines.push("");
|
||||
lines.push(`## 六维评分`);
|
||||
for (const dim of scoreDimensions) {
|
||||
for (const dim of SCORE_DIMENSIONS) {
|
||||
const score = result.dimensionScores[dim.key] ?? 0;
|
||||
const pct = Math.round((score / dim.maxScore) * 100);
|
||||
lines.push(`- **${dim.label}**: ${score}/${dim.maxScore} (${pct}%) — ${dim.description}`);
|
||||
lines.push(`- **${dim.label}**: ${score}/${dim.maxScore} (${pct}%) — ${dim.hint}`);
|
||||
}
|
||||
if (result.highlights.length > 0) {
|
||||
lines.push("");
|
||||
@@ -150,13 +210,6 @@ function formatReportMarkdown(result: EvalResult, script: string): string {
|
||||
lines.push("");
|
||||
lines.push(`---`);
|
||||
lines.push(`*评测时间: ${new Date().toLocaleString("zh-CN")}*`);
|
||||
lines.push("");
|
||||
lines.push(`<details><summary>原始剧本 (${script.length} 字)</summary>`);
|
||||
lines.push("");
|
||||
lines.push("```");
|
||||
lines.push(script.slice(0, 2000) + (script.length > 2000 ? "\n...(已截断)" : ""));
|
||||
lines.push("```");
|
||||
lines.push("</details>");
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
@@ -165,79 +218,85 @@ function ScriptTokensPage() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [result, setResult] = useState<EvalResult | null>(null);
|
||||
const [evalError, setEvalError] = useState<string | null>(null);
|
||||
const [detailsExpanded, setDetailsExpanded] = useState(true);
|
||||
const [uploadedFile, setUploadedFile] = useState<{ name: string; size: number } | null>(null);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [activeDim, setActiveDim] = useState<number | null>(null);
|
||||
const [animatedScore, setAnimatedScore] = useState(0);
|
||||
const [history, setHistory] = useState<HistoryEntry[]>(loadHistory);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const scoreFrameRef = useRef<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("[剧本评分] 页面已加载,ScriptTokensPage mounted");
|
||||
}, []);
|
||||
|
||||
const session = useSessionStore((s) => s.session);
|
||||
const hasContent = Boolean(script.trim());
|
||||
const lineNumbers = useMemo(() => {
|
||||
const count = Math.min(160, Math.max(10, script.split(/\r\n|\r|\n/).length));
|
||||
return Array.from({ length: count }, (_, index) => index + 1);
|
||||
}, [script]);
|
||||
|
||||
const handleUploadKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key !== "Enter" && event.key !== " ") return;
|
||||
event.preventDefault();
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
// Score animation
|
||||
useEffect(() => {
|
||||
if (!result) return;
|
||||
const start = performance.now();
|
||||
const target = result.totalScore;
|
||||
const dur = 1400;
|
||||
function tick(now: number) {
|
||||
const t = Math.min((now - start) / dur, 1);
|
||||
const e = 1 - Math.pow(1 - t, 3);
|
||||
setAnimatedScore(Math.round(e * target));
|
||||
if (t < 1) scoreFrameRef.current = requestAnimationFrame(tick);
|
||||
}
|
||||
scoreFrameRef.current = requestAnimationFrame(tick);
|
||||
return () => { if (scoreFrameRef.current) cancelAnimationFrame(scoreFrameRef.current); };
|
||||
}, [result]);
|
||||
|
||||
const handleFileUpload = async (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
const ext = file.name.slice(file.name.lastIndexOf(".")).toLowerCase();
|
||||
const readable = [".txt", ".md"].includes(ext) || file.type === "text/plain" || file.type === "text/markdown";
|
||||
const ext = getFileExtension(file.name);
|
||||
const readable = isReadableTextFile(file, ext);
|
||||
setUploadedFile({ name: file.name, size: file.size });
|
||||
|
||||
if (readable) {
|
||||
setScript(await file.text());
|
||||
const text = normalizeUploadedText(await decodeTextFile(file), ext);
|
||||
setScript(text);
|
||||
} else {
|
||||
setScript(
|
||||
`[已上传文件:${file.name}]\n\n暂不支持解析 ${ext.toUpperCase()} 格式,请上传 TXT 或 MD 文件,或直接粘贴剧本文本后开始评测。`,
|
||||
);
|
||||
setScript(`[已上传文件:${file.name}]\n\n暂不支持解析 ${ext ? ext.toUpperCase() : "未知"} 格式,请上传常见文本类文件。`);
|
||||
}
|
||||
|
||||
event.target.value = "";
|
||||
};
|
||||
|
||||
const handleEvaluate = async () => {
|
||||
console.log("[剧本评测] 点击开始评测,hasContent:", hasContent, "script长度:", script.length);
|
||||
if (!hasContent) return;
|
||||
setLoading(true);
|
||||
setResult(null);
|
||||
setEvalError(null);
|
||||
setAnimatedScore(0);
|
||||
setActiveDim(null);
|
||||
try {
|
||||
console.log("[剧本评测] 开始评测,剧本长度:", script.length, "字符");
|
||||
const aiResult = await evaluateScript(script);
|
||||
console.log("[剧本评测] 评测完成,结果:", {
|
||||
总分: aiResult.totalScore,
|
||||
等级: aiResult.grade,
|
||||
维度得分: aiResult.dimensionScores,
|
||||
摘要: aiResult.summary,
|
||||
亮点: aiResult.highlights,
|
||||
问题: aiResult.issues,
|
||||
建议: aiResult.suggestions,
|
||||
});
|
||||
setResult(aiResult);
|
||||
const g = getGrade(aiResult.totalScore);
|
||||
const entry: HistoryEntry = {
|
||||
name: uploadedFile?.name?.replace(/\.[^.]+$/, "") ?? `剧本 ${new Date().toLocaleDateString("zh-CN")}`,
|
||||
date: new Date().toLocaleDateString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }),
|
||||
timestamp: Date.now(),
|
||||
score: aiResult.totalScore,
|
||||
grade: g,
|
||||
};
|
||||
const updated = [entry, ...loadHistory().filter((h) => h.name !== entry.name || h.score !== entry.score)].sort(
|
||||
(a, b) => b.timestamp - a.timestamp,
|
||||
);
|
||||
saveHistory(updated);
|
||||
setHistory(updated);
|
||||
} catch (err) {
|
||||
console.error("[剧本评测] 评测失败:", err);
|
||||
setEvalError(err instanceof Error ? err.message : "评测服务暂时不可用,请稍后重试");
|
||||
}
|
||||
setDetailsExpanded(true);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setScript("");
|
||||
setResult(null);
|
||||
setDetailsExpanded(true);
|
||||
setEvalError(null);
|
||||
setUploadedFile(null);
|
||||
setCopied(false);
|
||||
setAnimatedScore(0);
|
||||
setActiveDim(null);
|
||||
if (fileInputRef.current) fileInputRef.current.value = "";
|
||||
};
|
||||
|
||||
@@ -276,226 +335,314 @@ function ScriptTokensPage() {
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
const scoreStatus = loading ? "评测中" : result ? "评测完成" : "待生成评分";
|
||||
const scoreHint =
|
||||
result?.summary ??
|
||||
(hasContent ? "点击「开始评测」生成六维雷达评分和优化路径。" : "粘贴完整剧本后,点击「开始评测」生成六维雷达评分和优化路径。");
|
||||
const uploadKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.key !== "Enter" && event.key !== " ") return;
|
||||
event.preventDefault();
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
const grade = result ? getGrade(result.totalScore) : null;
|
||||
const beatPct = result ? (result.totalScore >= 95 ? 97 : result.totalScore >= 88 ? 92 : result.totalScore >= 80 ? 85 : 72) : 0;
|
||||
const compactTitle = uploadedFile?.name?.replace(/\.[^.]+$/, "") ?? "剧本评测";
|
||||
const scriptMinutes = Math.max(8, Math.round(script.length / 460));
|
||||
const reportDate = new Date().toLocaleDateString("zh-CN", { month: "2-digit", day: "2-digit" });
|
||||
|
||||
return (
|
||||
<section className="script-token-page script-eval-v4 page-motion">
|
||||
<main className="script-token-page__scroll script-eval-v4-stage">
|
||||
<section className="script-eval-v4-app" aria-label="剧本评测工具">
|
||||
<div className="script-eval-v4-panel-left">
|
||||
<section className="script-eval-v4-glass script-eval-v4-input-card">
|
||||
<div
|
||||
className="script-eval-v4-upload-area"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
onKeyDown={handleUploadKeyDown}
|
||||
>
|
||||
<UploadOutlined />
|
||||
<div className="upload-text">
|
||||
{uploadedFile ? uploadedFile.name : "粘贴文本或上传文档"}
|
||||
<div className="hint">
|
||||
{uploadedFile ? `${(uploadedFile.size / 1024).toFixed(1)}KB,已载入文件信息` : "建议包含场景、角色、动作和台词"}
|
||||
</div>
|
||||
<section className="script-eval-v5 page-motion">
|
||||
<div className="script-eval-v5-page">
|
||||
{/* Left Panel */}
|
||||
<aside className="script-eval-v5-left">
|
||||
<div className="script-eval-v5-lp-section">
|
||||
<div className="script-eval-v5-lp-label">上传剧本</div>
|
||||
<div
|
||||
className="script-eval-v5-upload-zone"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
onKeyDown={uploadKeyDown}
|
||||
>
|
||||
{uploadedFile ? (
|
||||
<div className="script-eval-v5-upload-done is-show">
|
||||
<CheckCircleFilled />
|
||||
<span className="script-eval-v5-uf-name">{uploadedFile.name}</span>
|
||||
<span className="script-eval-v5-uf-re" onClick={(e) => { e.stopPropagation(); handleReset(); }}>
|
||||
重新上传
|
||||
</span>
|
||||
</div>
|
||||
<input ref={fileInputRef} type="file" accept=".txt,.md,.pdf,.doc,.docx" onChange={handleFileUpload} />
|
||||
<button
|
||||
type="button"
|
||||
className="script-eval-v4-upload-btn"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
fileInputRef.current?.click();
|
||||
}}
|
||||
>
|
||||
上传
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="script-eval-v4-text-shell">
|
||||
<div className="script-eval-v4-line-numbers" aria-hidden="true">
|
||||
{lineNumbers.map((line) => (
|
||||
<span key={line}>{line}</span>
|
||||
))}
|
||||
</div>
|
||||
<textarea
|
||||
className="script-eval-v4-text-input"
|
||||
value={script}
|
||||
onChange={(event) => setScript(event.target.value)}
|
||||
placeholder={
|
||||
"在此粘贴你的剧本内容...\n\n【第一幕】夜晚,城市天台。霓虹灯映照着雨后的地面。\n小凯独自站在天台边缘,手中握着一张皱巴巴的纸条..."
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="script-eval-v4-button-group">
|
||||
<button
|
||||
type="button"
|
||||
className="script-eval-v4-btn-primary"
|
||||
disabled={loading || !hasContent}
|
||||
onClick={() => void handleEvaluate()}
|
||||
>
|
||||
<span>{loading ? "评测中..." : "开始评测"}</span>
|
||||
</button>
|
||||
<button type="button" className="script-eval-v4-btn-secondary" onClick={handleReset}>
|
||||
<ReloadOutlined />
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
) : (
|
||||
<>
|
||||
<div className="script-eval-v5-upload-icon"><UploadOutlined /></div>
|
||||
<div className="script-eval-v5-upload-text">拖拽或点击上传</div>
|
||||
<button type="button" className="script-eval-v5-upload-btn" onClick={(e) => { e.stopPropagation(); fileInputRef.current?.click(); }}>
|
||||
+ 上传剧本
|
||||
</button>
|
||||
<div className="script-eval-v5-upload-hint">{TEXT_FILE_HINT}</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<input ref={fileInputRef} type="file" accept={TEXT_FILE_ACCEPT} style={{ display: "none" }} onChange={handleFileUpload} />
|
||||
</div>
|
||||
|
||||
<aside className="script-eval-v4-panel-right" aria-label="评分结果">
|
||||
{evalError ? (
|
||||
<div className="script-eval-v4-error" role="alert">
|
||||
<span className="script-eval-v4-error__icon">⚠</span>
|
||||
<span>{evalError}</span>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<section className={`script-eval-v4-glass script-eval-v4-score-card${loading ? " loading" : ""}${result ? " ready" : ""}`}>
|
||||
<div className="script-eval-v4-score-header">
|
||||
<div className="score-title">SCORE BOARD</div>
|
||||
<span className={`score-status${result ? " ready" : ""}`}>{scoreStatus}</span>
|
||||
</div>
|
||||
|
||||
<div className="script-eval-v4-score-main">
|
||||
<RadarPreview result={result} />
|
||||
|
||||
<div className="script-eval-v4-score-display">
|
||||
<div className={`score-number${result ? " has-data" : ""}`}>
|
||||
{result ? (
|
||||
<>
|
||||
{result.totalScore} <span>/ 100</span>
|
||||
</>
|
||||
) : (
|
||||
"— / 100"
|
||||
)}
|
||||
<div className="script-eval-v5-lp-section">
|
||||
<div className="script-eval-v5-lp-label">AI 识别信息</div>
|
||||
<div className="script-eval-v5-info-grid">
|
||||
{!result ? (
|
||||
<div className="script-eval-v5-info-empty">上传剧本并点击评测后识别</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="script-eval-v5-info-item">
|
||||
<span className="script-eval-v5-info-key">综合评分</span>
|
||||
<span className="script-eval-v5-info-val"><span className="script-eval-v5-info-tag">{result.totalScore}分 · {grade}级</span></span>
|
||||
</div>
|
||||
<div className="score-label">综合评分 {result ? `· ${result.grade} 级` : ""}</div>
|
||||
<div className="score-hint">{scoreHint}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="script-eval-v5-info-item">
|
||||
<span className="script-eval-v5-info-key">文本长度</span>
|
||||
<span className="script-eval-v5-info-val">{script.length} 字</span>
|
||||
</div>
|
||||
<div className="script-eval-v5-info-item">
|
||||
<span className="script-eval-v5-info-key">评测时间</span>
|
||||
<span className="script-eval-v5-info-val">{new Date().toLocaleDateString("zh-CN")}</span>
|
||||
</div>
|
||||
<div className="script-eval-v5-info-item">
|
||||
<span className="script-eval-v5-info-key">击败比例</span>
|
||||
<span className="script-eval-v5-info-val">{beatPct}%</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="script-eval-v4-dimensions-tags">
|
||||
{scoreDimensions.map((dimension) => (
|
||||
<span className="tag" key={dimension.key}>
|
||||
{dimension.label}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="script-eval-v4-glass script-eval-v4-details-card">
|
||||
<button
|
||||
type="button"
|
||||
className="script-eval-v4-details-header"
|
||||
onClick={() => setDetailsExpanded((expanded) => !expanded)}
|
||||
aria-expanded={detailsExpanded}
|
||||
>
|
||||
<span className="details-title">
|
||||
<TrophyOutlined />
|
||||
DIMENSIONS 六维详情
|
||||
</span>
|
||||
<DownOutlined className={`expand-icon${detailsExpanded ? " expanded" : ""}`} />
|
||||
</button>
|
||||
|
||||
<div className={`script-eval-v4-details-content${detailsExpanded ? " expanded" : ""}`}>
|
||||
<div className="script-eval-v4-details-list">
|
||||
{scoreDimensions.map((dimension) => {
|
||||
const score = result?.dimensionScores[dimension.key] ?? 0;
|
||||
const pct = result ? Math.round((score / dimension.maxScore) * 100) : 0;
|
||||
return (
|
||||
<article className="script-eval-v4-detail-row" key={dimension.key}>
|
||||
<div className="detail-row-main">
|
||||
<span className="dimension-name">{dimension.label}</span>
|
||||
<div className="dimension-bar" aria-hidden="true">
|
||||
<span className="dimension-bar-fill" style={{ width: `${pct}%` }} />
|
||||
</div>
|
||||
<span className="dimension-score">{result ? `${score}/${dimension.maxScore}` : `${dimension.maxScore}分`}</span>
|
||||
</div>
|
||||
<div className="dimension-desc">{dimension.description}</div>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{result && (result.highlights.length > 0 || result.issues.length > 0) && (
|
||||
<section className="script-eval-v4-glass script-eval-v4-insights-card">
|
||||
{result.highlights.length > 0 && (
|
||||
<div className="script-eval-v4-insight-group highlights">
|
||||
<div className="insight-group-title">
|
||||
<span className="insight-icon">✦</span>
|
||||
HIGHLIGHTS 亮点
|
||||
<div className="script-eval-v5-lp-section is-fill">
|
||||
<div className="script-eval-v5-lp-label">历史评测</div>
|
||||
<div className="script-eval-v5-history-list">
|
||||
{!session ? (
|
||||
<div className="script-eval-v5-history-empty">登录后查看云端评测记录</div>
|
||||
) : history.length === 0 ? (
|
||||
<div className="script-eval-v5-history-empty">暂无评测记录</div>
|
||||
) : (
|
||||
history.map((item, i) => (
|
||||
<div key={i} className={`script-eval-v5-history-item${i === 0 ? " is-active" : ""}`}>
|
||||
<div className="script-eval-v5-hi-left">
|
||||
<div className="script-eval-v5-hi-name">{item.name}</div>
|
||||
<div className="script-eval-v5-hi-date">{item.date}</div>
|
||||
<div className="script-eval-v5-hi-bar">
|
||||
<div className="script-eval-v5-hi-bar-fill" style={{ width: `${Math.min(92, (item.score / 100) * 100)}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
<ul className="insight-list">
|
||||
{result.highlights.map((h, i) => (
|
||||
<li key={i} className="insight-item highlight-item">{h}</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="script-eval-v5-hi-right">
|
||||
<div className={`script-eval-v5-hi-score${item.score >= 90 ? " is-green" : ""}`}>{item.score}</div>
|
||||
<div className="script-eval-v5-hi-grade">{item.grade}</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="script-eval-v5-lp-bottom">
|
||||
<button
|
||||
type="button"
|
||||
className="script-eval-v5-eval-btn"
|
||||
disabled={loading || !hasContent}
|
||||
onClick={() => void handleEvaluate()}
|
||||
>
|
||||
{loading ? "◆ 评测中..." : "◆ 开始评测"}
|
||||
</button>
|
||||
<button type="button" className="script-eval-v5-export-btn" disabled={!result} onClick={handleExportMarkdown}>
|
||||
导出评测报告
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Right Area */}
|
||||
<div className="script-eval-v5-right">
|
||||
<div className="script-eval-v5-right-topbar">
|
||||
<div className="script-eval-v5-right-title">
|
||||
<span className="script-eval-v5-rt-green">剧本评测</span>
|
||||
{uploadedFile && <> · {compactTitle}</>}
|
||||
</div>
|
||||
<div className="script-eval-v5-right-actions">
|
||||
{result && (
|
||||
<>
|
||||
<button type="button" className="script-eval-v5-action-btn" onClick={() => void handleCopyReport()}>
|
||||
<CopyOutlined />{copied ? "已复制" : "复制"}
|
||||
</button>
|
||||
<button type="button" className="script-eval-v5-action-btn" onClick={handleExportMarkdown}>
|
||||
<DownloadOutlined />导出
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`script-eval-v5-right-content${result ? " is-report" : ""}`}>
|
||||
{!result && (
|
||||
<div className="script-eval-v5-input-section">
|
||||
<div className="script-eval-v5-illustration" aria-label="上传剧本并开始评测">
|
||||
<div
|
||||
className="script-eval-v5-illustration-hit"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
onKeyDown={uploadKeyDown}
|
||||
>
|
||||
<div className="script-eval-v5-upload-card-icon">
|
||||
<FileTextOutlined />
|
||||
</div>
|
||||
<div className="script-eval-v5-upload-card-title">
|
||||
{uploadedFile ? "剧本已导入" : "上传剧本文件"}
|
||||
</div>
|
||||
<div className="script-eval-v5-upload-card-desc">
|
||||
{uploadedFile
|
||||
? "如需更换,点击此处重新上传;完成后点击左侧开始评测。"
|
||||
: `${TEXT_FILE_HINT},上传后点击开始评测,AI 将识别剧本信息。`}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{evalError && (
|
||||
<div className="script-eval-v5-error" role="alert">
|
||||
<span>⚠</span><span>{evalError}</span>
|
||||
</div>
|
||||
)}
|
||||
{result.issues.length > 0 && (
|
||||
<div className="script-eval-v4-insight-group issues">
|
||||
<div className="insight-group-title">
|
||||
<span className="insight-icon">△</span>
|
||||
ISSUES 扣分点
|
||||
</div>
|
||||
<ul className="insight-list">
|
||||
{result.issues.map((issue, i) => (
|
||||
<li key={i} className="insight-item issue-item">{issue}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
{result && result.suggestions.length > 0 && (
|
||||
<section className="script-eval-v4-glass script-eval-v4-suggestions-card">
|
||||
<div className="suggestions-header">
|
||||
<span className="suggestions-title">
|
||||
<span className="insight-icon">→</span>
|
||||
SUGGESTIONS 优化路径
|
||||
</span>
|
||||
</div>
|
||||
<ul className="suggestion-list">
|
||||
{result.suggestions.map((s, i) => (
|
||||
<li key={i} className="suggestion-item">
|
||||
<span className="suggestion-index">{i + 1}</span>
|
||||
<span className="suggestion-text">{s}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{result && (
|
||||
<div className="script-eval-v4-report-actions">
|
||||
<button type="button" className="script-eval-v4-report-btn" onClick={() => void handleCopyReport()}>
|
||||
<CopyOutlined />
|
||||
<span>{copied ? "已复制" : "复制报告"}</span>
|
||||
</button>
|
||||
<button type="button" className="script-eval-v4-report-btn" onClick={handleExportMarkdown}>
|
||||
<DownloadOutlined />
|
||||
<span>导出 Markdown</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<section className="script-eval-report script-eval-report--inside">
|
||||
<div className="script-eval-report__body">
|
||||
<header className="script-eval-report__hero">
|
||||
<div className="script-eval-report__score-block">
|
||||
<div className="script-eval-report__score-row">
|
||||
<span className="script-eval-report__score">{animatedScore}</span>
|
||||
<span className="script-eval-report__score-total">/ 100</span>
|
||||
<span className="script-eval-report__grade">
|
||||
<i />
|
||||
{grade}级
|
||||
</span>
|
||||
</div>
|
||||
<div className="script-eval-report__score-line">
|
||||
<span style={{ width: `${animatedScore}%` }} />
|
||||
</div>
|
||||
<div className="script-eval-report__beat">击败全国 <b>{beatPct}%</b> 剧本</div>
|
||||
</div>
|
||||
|
||||
{!result && (
|
||||
<section className="script-eval-v4-note">
|
||||
<FileTextOutlined />
|
||||
<span>评分后会展示当前短板、改写方向和可执行优化路径。</span>
|
||||
<div className="script-eval-report__summary">
|
||||
<div className="script-eval-report__title-line">
|
||||
<div>
|
||||
<h1>{compactTitle}</h1>
|
||||
<p>{`剧本评测 · ${scriptMinutes} min · ${reportDate}`}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="script-eval-report__desc">{result.summary}</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section className="script-eval-report__chart-card" aria-label="维度拆解">
|
||||
<div className="script-eval-report__card-head">
|
||||
<span><i />维度拆解</span>
|
||||
<div className="script-eval-report__legend">
|
||||
<span><i className="is-score" />得分</span>
|
||||
<span><i className="is-loss" />扣分</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="script-eval-report__chart">
|
||||
<div className="script-eval-report__axis">
|
||||
<span>100%</span>
|
||||
<span>80%</span>
|
||||
<span>60%</span>
|
||||
<span>40%</span>
|
||||
<span>20%</span>
|
||||
<span>0%</span>
|
||||
</div>
|
||||
<div className="script-eval-report__chart-grid">
|
||||
{SCORE_DIMENSIONS.map((dim) => {
|
||||
const score = result.dimensionScores[dim.key] ?? 0;
|
||||
const pct = Math.max(0, Math.min(1, score / dim.maxScore));
|
||||
const lossPct = 1 - pct;
|
||||
const isPerfect = score === dim.maxScore;
|
||||
return (
|
||||
<button key={dim.key} type="button" className="script-eval-report__bar-col">
|
||||
<div className="script-eval-report__bar-score">
|
||||
<b>{score}</b><small>/{dim.maxScore}</small>{isPerfect ? <em>*</em> : null}
|
||||
</div>
|
||||
<div className="script-eval-report__bar-box">
|
||||
{lossPct > 0 ? <div className="script-eval-report__bar-loss" style={{ height: `${lossPct * 100}%` }} /> : null}
|
||||
<div className="script-eval-report__bar-fill" style={{ height: `${pct * 100}%` }} />
|
||||
</div>
|
||||
<strong>{dim.label}</strong>
|
||||
<span>{dim.hint}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="script-eval-report__findings">
|
||||
{result.highlights.length > 0 ? (
|
||||
<section className="script-eval-report__finding-group is-highlight">
|
||||
<h2>亮点 <span>{result.highlights.length}</span></h2>
|
||||
<div>
|
||||
{result.highlights.map((item, index) => (
|
||||
<p key={index}>{item}</p>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
{result.issues.length > 0 ? (
|
||||
<section className="script-eval-report__finding-group is-issue">
|
||||
<h2>扣分点 <span>{result.issues.length}</span></h2>
|
||||
<div>
|
||||
{result.issues.map((item, index) => (
|
||||
<p key={index}>{item}</p>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{result.suggestions.length > 0 ? (
|
||||
<section className="script-eval-report__path-card">
|
||||
<div className="script-eval-report__card-head">
|
||||
<span><i />优化路径</span>
|
||||
</div>
|
||||
<table className="script-eval-report__path-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>类型</th>
|
||||
<th>优化路径</th>
|
||||
<th>优先级</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{result.suggestions.map((item, index) => {
|
||||
const high = index < 2;
|
||||
return (
|
||||
<tr key={index}>
|
||||
<td>{String(index + 1).padStart(2, "0")}</td>
|
||||
<td>{high ? "核心" : "增强"}</td>
|
||||
<td>{item}</td>
|
||||
<td><span className={high ? "is-high" : "is-mid"}>{high ? "HIGH" : "MID"}</span></td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</aside>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div className="script-eval-v5-statusbar">
|
||||
<div className="script-eval-v5-status-dot" />
|
||||
<span>{loading ? "评测中..." : result ? "评测完成" : hasContent ? "待评测" : "等待上传"}</span>
|
||||
<span className="script-eval-v5-sb-right">{result ? `六维标准 · ${result.totalScore}分` : "六维标准"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
@import "./components/legacy-components.css";
|
||||
@import "./pages/home.css";
|
||||
@import "./pages/welcome-splash.css";
|
||||
@import "./pages/toolbox.css";
|
||||
@import "./pages/script-review-visual.css";
|
||||
@import "./pages/script-review-showcase.css";
|
||||
@import "./pages/model-generation-showcase.css";
|
||||
@import "./pages/workbench.css";
|
||||
@import "./pages/ecommerce.css";
|
||||
@import "./pages/ecommerce-video.css";
|
||||
@@ -17,6 +21,7 @@
|
||||
@import "./pages/image-workbench.css";
|
||||
@import "./pages/subtitle-removal.css";
|
||||
@import "./pages/size-template.css";
|
||||
@import "./pages/script-tokens-v5.css";
|
||||
@import "./pages/script-tokens.css";
|
||||
@import "./pages/profile.css";
|
||||
@import "./pages/canvas.css";
|
||||
|
||||
+310
-1
@@ -468,7 +468,7 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.omni-home__feature-page.is-token::before {
|
||||
.omni-home__feature-page.is-model::before {
|
||||
background:
|
||||
linear-gradient(90deg, rgb(5 8 13 / 96%) 0%, rgb(5 8 13 / 80%) 42%, rgb(5 8 13 / 48%) 100%),
|
||||
linear-gradient(180deg, rgb(255 255 255 / 6%), transparent 34%);
|
||||
@@ -594,6 +594,258 @@
|
||||
filter: saturate(1.1) contrast(1.06) brightness(1.04);
|
||||
}
|
||||
|
||||
.omni-home__feature-page.is-model .omni-home__feature-visual {
|
||||
aspect-ratio: 16 / 9;
|
||||
min-height: clamp(360px, 36vw, 620px);
|
||||
max-height: min(68vh, 680px);
|
||||
border-color: rgb(255 255 255 / 14%);
|
||||
border-radius: 8px;
|
||||
background: #0a0b12;
|
||||
box-shadow:
|
||||
0 34px 90px rgb(0 0 0 / 42%),
|
||||
inset 0 1px 0 rgb(255 255 255 / 7%);
|
||||
}
|
||||
|
||||
.omni-home__feature-page.is-ecommerce .omni-home__feature-visual {
|
||||
aspect-ratio: 16 / 9;
|
||||
min-height: clamp(360px, 36vw, 620px);
|
||||
max-height: min(68vh, 680px);
|
||||
border-color: rgb(255 255 255 / 14%);
|
||||
border-radius: 8px;
|
||||
background: #101214;
|
||||
box-shadow:
|
||||
0 34px 90px rgb(0 0 0 / 42%),
|
||||
inset 0 1px 0 rgb(255 255 255 / 7%);
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(240px, 0.86fr) minmax(360px, 1.14fr);
|
||||
grid-template-rows: auto 1fr;
|
||||
gap: clamp(16px, 2vw, 28px);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding: clamp(22px, 3vw, 38px);
|
||||
background:
|
||||
linear-gradient(145deg, rgb(255 255 255 / 5%), transparent 28%),
|
||||
linear-gradient(180deg, #181b1d 0%, #0d0f10 100%);
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__depth,
|
||||
.omni-home-ecommerce-showcase__grain {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__depth {
|
||||
z-index: -2;
|
||||
background:
|
||||
linear-gradient(90deg, rgb(255 255 255 / 5%) 1px, transparent 1px),
|
||||
linear-gradient(180deg, rgb(255 255 255 / 4%) 1px, transparent 1px),
|
||||
radial-gradient(circle at 50% 50%, transparent 0 42%, rgb(0 0 0 / 36%) 78%),
|
||||
linear-gradient(140deg, rgb(0 255 136 / 10%), transparent 40%);
|
||||
background-size: 32px 32px, 32px 32px, 100% 100%, 100% 100%;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__grain {
|
||||
z-index: -1;
|
||||
opacity: 0.22;
|
||||
background:
|
||||
repeating-linear-gradient(0deg, rgb(255 255 255 / 4%) 0 1px, transparent 1px 4px),
|
||||
repeating-linear-gradient(90deg, rgb(255 255 255 / 3%) 0 1px, transparent 1px 5px);
|
||||
mix-blend-mode: soft-light;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__prompt {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
gap: 8px;
|
||||
min-height: 152px;
|
||||
border: 1px solid rgb(0 255 136 / 42%);
|
||||
border-radius: 8px;
|
||||
background:
|
||||
linear-gradient(135deg, rgb(0 255 136 / 12%), rgb(255 255 255 / 7%)),
|
||||
rgb(18 21 22 / 84%);
|
||||
padding: clamp(18px, 2.2vw, 26px);
|
||||
box-shadow:
|
||||
0 0 0 1px rgb(0 255 136 / 10%),
|
||||
0 20px 58px rgb(0 255 136 / 10%),
|
||||
inset 0 1px 0 rgb(255 255 255 / 10%);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__prompt span {
|
||||
color: rgb(0 255 136 / 86%);
|
||||
font-size: clamp(13px, 1vw, 16px);
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__prompt strong {
|
||||
color: #f7faf8;
|
||||
font-size: clamp(26px, 2.5vw, 44px);
|
||||
font-weight: 950;
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__prompt p {
|
||||
max-width: 420px;
|
||||
margin: 0;
|
||||
color: rgb(232 238 236 / 76%);
|
||||
font-size: clamp(14px, 1.18vw, 18px);
|
||||
font-weight: 700;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__tools {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
align-self: end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__tool {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
min-height: 92px;
|
||||
align-content: center;
|
||||
border: 1px solid rgb(255 255 255 / 12%);
|
||||
border-radius: 8px;
|
||||
background: rgb(255 255 255 / 7%);
|
||||
padding: 15px 16px;
|
||||
box-shadow: 0 18px 42px rgb(0 0 0 / 20%);
|
||||
backdrop-filter: blur(20px);
|
||||
transition:
|
||||
transform 220ms cubic-bezier(0.16, 1, 0.3, 1),
|
||||
border-color 220ms ease,
|
||||
background 220ms ease;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__tool:hover {
|
||||
border-color: rgb(0 255 136 / 36%);
|
||||
background: rgb(0 255 136 / 9%);
|
||||
transform: translateY(-3px) scale(1.01);
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__tool b {
|
||||
color: #ffffff;
|
||||
font-size: clamp(20px, 1.7vw, 28px);
|
||||
font-weight: 950;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__tool small {
|
||||
color: rgb(232 238 236 / 58%);
|
||||
font-size: clamp(12px, 1vw, 15px);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__gallery {
|
||||
position: relative;
|
||||
grid-row: 1 / 3;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot {
|
||||
position: absolute;
|
||||
display: grid;
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgb(255 255 255 / 14%);
|
||||
border-radius: 8px;
|
||||
background: rgb(255 255 255 / 8%);
|
||||
box-shadow:
|
||||
0 24px 62px rgb(0 0 0 / 32%),
|
||||
inset 0 1px 0 rgb(255 255 255 / 8%);
|
||||
backdrop-filter: blur(20px);
|
||||
transition:
|
||||
transform 240ms cubic-bezier(0.16, 1, 0.3, 1),
|
||||
border-color 220ms ease,
|
||||
box-shadow 220ms ease;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot:hover {
|
||||
border-color: rgb(0 255 136 / 32%);
|
||||
box-shadow: 0 30px 76px rgb(0 0 0 / 40%);
|
||||
transform: translateY(-4px) scale(1.015);
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot.is-1 {
|
||||
z-index: 3;
|
||||
top: 3%;
|
||||
left: 18%;
|
||||
width: 61%;
|
||||
height: 65%;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot.is-2 {
|
||||
z-index: 2;
|
||||
bottom: 2%;
|
||||
left: 0;
|
||||
width: 47%;
|
||||
height: 43%;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot.is-3 {
|
||||
z-index: 2;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 48%;
|
||||
height: 45%;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
filter: saturate(0.96) contrast(1.02);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot:hover img {
|
||||
transform: none;
|
||||
filter: saturate(1.02) contrast(1.04);
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot > div {
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
border-top: 1px solid rgb(255 255 255 / 10%);
|
||||
background: rgb(12 14 15 / 88%);
|
||||
padding: 12px 14px 13px;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot span {
|
||||
justify-self: start;
|
||||
border: 1px solid rgb(0 255 136 / 30%);
|
||||
border-radius: 999px;
|
||||
background: rgb(0 255 136 / 10%);
|
||||
color: rgb(0 255 136 / 92%);
|
||||
padding: 3px 9px;
|
||||
font-size: 12px;
|
||||
font-weight: 950;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot strong {
|
||||
color: #f7faf8;
|
||||
font-size: clamp(15px, 1.15vw, 20px);
|
||||
font-weight: 950;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot small {
|
||||
color: rgb(232 238 236 / 58%);
|
||||
font-size: clamp(11px, 0.92vw, 14px);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
|
||||
.omni-home__feature-stats {
|
||||
position: absolute;
|
||||
right: clamp(22px, 7vw, 92px);
|
||||
@@ -841,6 +1093,21 @@
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.omni-home__feature-page.is-model .omni-home__feature-visual,
|
||||
.omni-home__feature-page.is-ecommerce .omni-home__feature-visual {
|
||||
min-height: 620px;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto auto minmax(360px, 1fr);
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__gallery {
|
||||
grid-row: auto;
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
.omni-home__feature-stats {
|
||||
right: 22px;
|
||||
left: 22px;
|
||||
@@ -906,6 +1173,48 @@
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.omni-home__feature-page.is-ecommerce .omni-home__feature-visual {
|
||||
min-height: 0;
|
||||
aspect-ratio: auto;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase {
|
||||
gap: 14px;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__prompt {
|
||||
min-height: 130px;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__tools {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__tool {
|
||||
min-height: 78px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__gallery {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.omni-home-ecommerce-showcase__shot,
|
||||
.omni-home-ecommerce-showcase__shot.is-1,
|
||||
.omni-home-ecommerce-showcase__shot.is-2,
|
||||
.omni-home-ecommerce-showcase__shot.is-3 {
|
||||
position: relative;
|
||||
inset: auto;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: 4 / 3;
|
||||
}
|
||||
|
||||
.omni-home__feature-stats {
|
||||
bottom: 34px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,787 @@
|
||||
/* ===== Model Generation Showcase ===== */
|
||||
.omni-model-gen-showcase {
|
||||
--mgs-green: #00ff88;
|
||||
--mgs-blue: #4fc3f7;
|
||||
--mgs-purple: #a855f7;
|
||||
--mgs-bg: #0a0b12;
|
||||
--mgs-surface: rgba(14, 16, 38, 0.75);
|
||||
--mgs-elevated: rgba(20, 22, 52, 0.85);
|
||||
--mgs-border-subtle: rgba(0, 255, 136, 0.08);
|
||||
--mgs-border-default: rgba(0, 255, 136, 0.14);
|
||||
--mgs-border-hover: rgba(0, 255, 136, 0.28);
|
||||
--mgs-text-primary: #eef0f6;
|
||||
--mgs-text-secondary: #8b8fa7;
|
||||
--mgs-text-muted: #5a5e78;
|
||||
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 8px 10px;
|
||||
background: var(--mgs-bg);
|
||||
color: var(--mgs-text-primary);
|
||||
font-family: "Inter", "Noto Sans SC", sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ===== Left Panel ===== */
|
||||
.mgs-left-panel {
|
||||
width: 166px;
|
||||
min-width: 166px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mgs-brand-section {
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.mgs-brand-section h1 {
|
||||
font-size: 13px;
|
||||
font-weight: 900;
|
||||
line-height: 1.25;
|
||||
background: linear-gradient(135deg, var(--mgs-green), var(--mgs-blue));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.mgs-subtitle {
|
||||
font-size: 8px;
|
||||
line-height: 1.5;
|
||||
color: var(--mgs-text-secondary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Mode Tabs */
|
||||
.mgs-mode-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.mgs-mode-tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 7px 9px;
|
||||
border-radius: 8px;
|
||||
background: var(--mgs-surface);
|
||||
border: 1px solid var(--mgs-border-subtle);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mgs-mode-tab::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, rgba(0, 255, 136, 0.05), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.mgs-mode-tab:hover {
|
||||
border-color: var(--mgs-border-hover);
|
||||
transform: translateX(3px);
|
||||
box-shadow: 0 3px 14px rgba(0, 255, 136, 0.08);
|
||||
}
|
||||
|
||||
.mgs-mode-tab:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.mgs-mode-tab.is-active {
|
||||
border-color: rgba(0, 255, 136, 0.25);
|
||||
background: var(--mgs-elevated);
|
||||
box-shadow: 0 0 16px rgba(0, 255, 136, 0.1), inset 0 0 14px rgba(0, 255, 136, 0.03);
|
||||
}
|
||||
|
||||
.mgs-mode-tab.is-active::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.mgs-mode-icon {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
flex-shrink: 0;
|
||||
background: rgba(0, 255, 136, 0.08);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.mgs-mode-tab.is-active .mgs-mode-icon {
|
||||
background: rgba(0, 255, 136, 0.15);
|
||||
box-shadow: 0 0 8px rgba(0, 255, 136, 0.2);
|
||||
}
|
||||
|
||||
.mgs-mode-info h3 {
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
margin: 0 0 1px;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.mgs-mode-tab.is-active .mgs-mode-info h3 {
|
||||
color: var(--mgs-green);
|
||||
}
|
||||
|
||||
.mgs-mode-info p {
|
||||
font-size: 7px;
|
||||
color: var(--mgs-text-muted);
|
||||
line-height: 1.3;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Workflow */
|
||||
.mgs-workflow {
|
||||
margin-top: auto;
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
background: var(--mgs-surface);
|
||||
border: 1px solid var(--mgs-border-subtle);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mgs-workflow::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(0, 255, 136, 0.06), transparent);
|
||||
animation: mgs-flow 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes mgs-flow {
|
||||
0% { left: -100%; }
|
||||
100% { left: 200%; }
|
||||
}
|
||||
|
||||
.mgs-workflow-title {
|
||||
font-size: 7px;
|
||||
color: var(--mgs-text-muted);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.mgs-workflow-steps {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
font-size: 7px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mgs-wf-step {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 4px 2px;
|
||||
border-radius: 4px;
|
||||
color: var(--mgs-text-secondary);
|
||||
background: rgba(0, 255, 136, 0.04);
|
||||
border: 1px solid var(--mgs-border-subtle);
|
||||
transition: all 0.3s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mgs-wf-step.is-active {
|
||||
color: var(--mgs-green);
|
||||
border-color: rgba(0, 255, 136, 0.2);
|
||||
background: rgba(0, 255, 136, 0.08);
|
||||
animation: mgs-step-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes mgs-step-pulse {
|
||||
0%, 100% { box-shadow: 0 0 0 rgba(0, 255, 136, 0); }
|
||||
50% { box-shadow: 0 0 6px rgba(0, 255, 136, 0.15); }
|
||||
}
|
||||
|
||||
.mgs-wf-arrow {
|
||||
color: var(--mgs-text-muted);
|
||||
font-size: 7px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ===== Center Area ===== */
|
||||
.mgs-center-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mgs-input-card {
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
border-radius: 12px;
|
||||
background: var(--mgs-elevated);
|
||||
border: 1px solid var(--mgs-border-default);
|
||||
padding: 14px;
|
||||
position: relative;
|
||||
box-shadow: 0 0 24px rgba(0, 255, 136, 0.04), 0 4px 16px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.mgs-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mgs-card-mode-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 8px;
|
||||
font-weight: 700;
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
color: var(--mgs-green);
|
||||
border: 1px solid rgba(0, 255, 136, 0.15);
|
||||
}
|
||||
|
||||
.mgs-dot {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 50%;
|
||||
background: var(--mgs-green);
|
||||
animation: mgs-pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes mgs-pulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.5; transform: scale(0.8); }
|
||||
}
|
||||
|
||||
.mgs-card-status {
|
||||
font-size: 7px;
|
||||
color: var(--mgs-text-muted);
|
||||
}
|
||||
|
||||
/* Prompt Area */
|
||||
.mgs-prompt-area {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mgs-prompt-input {
|
||||
width: 100%;
|
||||
padding: 6px 8px;
|
||||
border-radius: 6px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid var(--mgs-border-default);
|
||||
color: var(--mgs-text-primary);
|
||||
font-size: 8px;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
resize: none;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.mgs-prompt-input::placeholder {
|
||||
color: var(--mgs-text-muted);
|
||||
}
|
||||
|
||||
.mgs-prompt-input:focus {
|
||||
border-color: rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
/* Options */
|
||||
.mgs-options {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-bottom: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.mgs-opt {
|
||||
padding: 3px 7px;
|
||||
border-radius: 5px;
|
||||
font-size: 7px;
|
||||
font-weight: 600;
|
||||
background: rgba(0, 255, 136, 0.06);
|
||||
border: 1px solid var(--mgs-border-subtle);
|
||||
color: var(--mgs-text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.mgs-opt:hover {
|
||||
border-color: var(--mgs-border-hover);
|
||||
color: var(--mgs-green);
|
||||
}
|
||||
|
||||
.mgs-opt.is-selected {
|
||||
border-color: rgba(0, 255, 136, 0.25);
|
||||
color: var(--mgs-green);
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
}
|
||||
|
||||
/* Agent Result */
|
||||
.mgs-agent-output-area {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mgs-agent-result {
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 255, 136, 0.04);
|
||||
border: 1px solid var(--mgs-border-subtle);
|
||||
}
|
||||
|
||||
.mgs-agent-result-label {
|
||||
font-size: 7px;
|
||||
font-weight: 700;
|
||||
color: var(--mgs-green);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.mgs-agent-result-label::before {
|
||||
content: "";
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 50%;
|
||||
background: var(--mgs-green);
|
||||
animation: mgs-pulse 2s infinite;
|
||||
}
|
||||
|
||||
.mgs-agent-result-text {
|
||||
font-size: 8px;
|
||||
line-height: 1.5;
|
||||
color: var(--mgs-text-secondary);
|
||||
}
|
||||
|
||||
/* Chat Input */
|
||||
.mgs-chat-input-row {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mgs-chat-input {
|
||||
flex: 1;
|
||||
padding: 5px 8px;
|
||||
border-radius: 6px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid var(--mgs-border-default);
|
||||
color: var(--mgs-text-primary);
|
||||
font-size: 8px;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.mgs-chat-input::placeholder {
|
||||
color: var(--mgs-text-muted);
|
||||
}
|
||||
|
||||
.mgs-chat-send {
|
||||
padding: 5px 12px;
|
||||
border-radius: 6px;
|
||||
border: none;
|
||||
background: var(--mgs-green);
|
||||
color: #0a0b12;
|
||||
cursor: pointer;
|
||||
font-size: 8px;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 0 8px rgba(0, 255, 136, 0.2);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Image Grid */
|
||||
.mgs-img-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.mgs-img-cell {
|
||||
aspect-ratio: 4 / 3;
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(135deg, rgba(0, 255, 136, 0.05), rgba(79, 195, 247, 0.03));
|
||||
border: 1px solid var(--mgs-border-subtle);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
color: var(--mgs-text-muted);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.mgs-img-cell:hover {
|
||||
border-color: var(--mgs-border-hover);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
/* Video Config */
|
||||
.mgs-video-config {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mgs-config-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid var(--mgs-border-subtle);
|
||||
font-size: 7px;
|
||||
}
|
||||
|
||||
.mgs-config-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.mgs-config-label {
|
||||
color: var(--mgs-text-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mgs-config-value {
|
||||
color: var(--mgs-green);
|
||||
font-weight: 600;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 255, 136, 0.08);
|
||||
}
|
||||
|
||||
/* Video Preview */
|
||||
.mgs-video-preview {
|
||||
aspect-ratio: 16 / 9;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, rgba(0, 255, 136, 0.04), rgba(168, 85, 247, 0.04));
|
||||
border: 1px solid var(--mgs-border-subtle);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mgs-play-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 255, 136, 0.15);
|
||||
border: 2px solid rgba(0, 255, 136, 0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.mgs-play-btn:hover {
|
||||
background: rgba(0, 255, 136, 0.25);
|
||||
transform: scale(1.08);
|
||||
box-shadow: 0 0 14px rgba(0, 255, 136, 0.2);
|
||||
}
|
||||
|
||||
.mgs-play-btn svg {
|
||||
fill: var(--mgs-green);
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.mgs-video-timeline {
|
||||
margin-top: 6px;
|
||||
height: 3px;
|
||||
border-radius: 2px;
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mgs-video-timeline-fill {
|
||||
height: 100%;
|
||||
width: 35%;
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(90deg, var(--mgs-green), var(--mgs-blue));
|
||||
animation: mgs-timeline 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes mgs-timeline {
|
||||
0%, 100% { width: 30%; }
|
||||
50% { width: 45%; }
|
||||
}
|
||||
|
||||
/* ===== Right Panel ===== */
|
||||
.mgs-right-panel {
|
||||
width: 168px;
|
||||
min-width: 168px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.mgs-right-panel::-webkit-scrollbar {
|
||||
width: 2px;
|
||||
}
|
||||
|
||||
.mgs-right-panel::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 255, 136, 0.12);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.mgs-output-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.mgs-section-label {
|
||||
font-size: 7px;
|
||||
font-weight: 700;
|
||||
color: var(--mgs-text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.mgs-section-dot {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 50%;
|
||||
animation: mgs-pulse 2s infinite;
|
||||
}
|
||||
|
||||
.mgs-section-dot.is-green { background: var(--mgs-green); }
|
||||
.mgs-section-dot.is-blue { background: var(--mgs-blue); }
|
||||
.mgs-section-dot.is-purple { background: var(--mgs-purple); }
|
||||
|
||||
.mgs-output-cards {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mgs-out-card {
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
background: var(--mgs-surface);
|
||||
border: 1px solid var(--mgs-border-subtle);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mgs-out-card::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, rgba(0, 255, 136, 0.03), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.mgs-out-card:hover {
|
||||
border-color: var(--mgs-border-hover);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 16px rgba(0, 255, 136, 0.06);
|
||||
}
|
||||
|
||||
.mgs-out-card:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.mgs-out-card-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.mgs-out-tag {
|
||||
font-size: 7px;
|
||||
font-weight: 700;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
color: var(--mgs-green);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.mgs-out-tag.is-img {
|
||||
background: rgba(79, 195, 247, 0.1);
|
||||
color: var(--mgs-blue);
|
||||
}
|
||||
|
||||
.mgs-out-tag.is-video {
|
||||
background: rgba(168, 85, 247, 0.1);
|
||||
color: var(--mgs-purple);
|
||||
}
|
||||
|
||||
.mgs-out-title {
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mgs-out-preview {
|
||||
font-size: 8px;
|
||||
color: var(--mgs-text-secondary);
|
||||
line-height: 1.45;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mgs-out-img-placeholder {
|
||||
height: 40px;
|
||||
border-radius: 5px;
|
||||
margin-top: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mgs-out-img-placeholder.is-realistic {
|
||||
background: linear-gradient(135deg, rgba(0, 255, 136, 0.06), rgba(0, 255, 136, 0.02));
|
||||
border: 1px dashed rgba(0, 255, 136, 0.12);
|
||||
}
|
||||
|
||||
.mgs-out-img-placeholder.is-illustration {
|
||||
background: linear-gradient(135deg, rgba(79, 195, 247, 0.06), rgba(168, 85, 247, 0.04));
|
||||
border: 1px dashed rgba(79, 195, 247, 0.12);
|
||||
}
|
||||
|
||||
.mgs-out-img-placeholder.is-ecommerce {
|
||||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.06), rgba(0, 255, 136, 0.03));
|
||||
border: 1px dashed rgba(168, 85, 247, 0.12);
|
||||
}
|
||||
|
||||
.mgs-out-video-placeholder {
|
||||
height: 36px;
|
||||
border-radius: 5px;
|
||||
margin-top: 4px;
|
||||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.06), rgba(0, 255, 136, 0.03));
|
||||
border: 1px dashed rgba(168, 85, 247, 0.12);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.mgs-mini-play {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
background: rgba(168, 85, 247, 0.2);
|
||||
border: 1.5px solid rgba(168, 85, 247, 0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mgs-mini-play svg {
|
||||
fill: var(--mgs-purple);
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.mgs-video-duration {
|
||||
font-size: 7px;
|
||||
color: var(--mgs-text-muted);
|
||||
}
|
||||
|
||||
/* ===== Responsive ===== */
|
||||
@media (max-width: 980px) {
|
||||
.omni-model-gen-showcase {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 6px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.mgs-left-panel {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mgs-mode-tabs {
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.mgs-mode-tab {
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
gap: 3px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.mgs-mode-tab:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.mgs-mode-icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.mgs-mode-info h3 {
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.mgs-mode-info p {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mgs-workflow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mgs-center-area {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.mgs-right-panel {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
flex-direction: row;
|
||||
gap: 6px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.mgs-output-cards {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.mgs-out-card {
|
||||
min-width: 150px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.mgs-input-card {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
/* ===== Script Review Showcase ===== */
|
||||
.omni-script-review-showcase {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px 12px;
|
||||
background: #0a0b12;
|
||||
color: #eeeef6;
|
||||
font-family: "Inter", "Noto Sans SC", sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Score Hero */
|
||||
.srs-score-hero {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 8px 14px;
|
||||
border-radius: 12px;
|
||||
background: rgba(20, 22, 52, 0.85);
|
||||
border: 1px solid rgba(0, 255, 136, 0.14);
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.srs-score-hero::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
border-radius: 13px;
|
||||
background: linear-gradient(135deg, rgba(0, 255, 136, 0.2), rgba(79, 195, 247, 0.12));
|
||||
z-index: -1;
|
||||
opacity: 0.5;
|
||||
filter: blur(1px);
|
||||
}
|
||||
|
||||
.srs-score-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.srs-score-circle {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 50%;
|
||||
background: conic-gradient(#00ff88 0deg, #00ff88 calc(77 * 3.6deg), rgba(255, 255, 255, 0.06) calc(77 * 3.6deg));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 0 20px rgba(0, 255, 136, 0.15);
|
||||
animation: srs-glow 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes srs-glow {
|
||||
0%, 100% { box-shadow: 0 0 20px rgba(0, 255, 136, 0.15); }
|
||||
50% { box-shadow: 0 0 30px rgba(0, 255, 136, 0.25); }
|
||||
}
|
||||
|
||||
.srs-score-circle-inner {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 50%;
|
||||
background: rgba(20, 22, 52, 0.95);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.srs-score-num {
|
||||
font-size: 18px;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.srs-score-den {
|
||||
font-size: 8px;
|
||||
color: rgba(150, 155, 185, 0.45);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.srs-score-meta {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.srs-score-grade {
|
||||
font-size: 16px;
|
||||
font-weight: 900;
|
||||
color: #00ff88;
|
||||
line-height: 1;
|
||||
text-shadow: 0 0 16px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
.srs-score-tags {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.srs-score-tag {
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 7px;
|
||||
font-weight: 600;
|
||||
background: rgba(0, 255, 136, 0.08);
|
||||
color: #00ff88;
|
||||
border: 1px solid rgba(0, 255, 136, 0.12);
|
||||
}
|
||||
|
||||
.srs-score-divider {
|
||||
width: 1px;
|
||||
height: 40px;
|
||||
background: linear-gradient(transparent, rgba(255, 255, 255, 0.08), transparent);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.srs-score-right {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.srs-score-proj {
|
||||
font-size: 9px;
|
||||
color: rgba(150, 155, 185, 0.45);
|
||||
margin-bottom: 3px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.srs-score-summary {
|
||||
font-size: 10px;
|
||||
line-height: 1.55;
|
||||
color: rgba(190, 195, 220, 0.72);
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Chart Card */
|
||||
.srs-chart-card {
|
||||
padding: 8px 12px;
|
||||
border-radius: 12px;
|
||||
background: rgba(20, 22, 52, 0.85);
|
||||
border: 1px solid rgba(0, 255, 136, 0.14);
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.srs-chart-title {
|
||||
font-size: 8px;
|
||||
font-weight: 700;
|
||||
color: rgba(190, 195, 220, 0.72);
|
||||
margin-bottom: 6px;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.srs-chart-body {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.srs-chart-col {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.srs-chart-bar-wrap {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.srs-chart-bar-bg {
|
||||
width: 100%;
|
||||
max-width: 42px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
border-radius: 6px 6px 0 0;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.srs-chart-bar-fill {
|
||||
width: 100%;
|
||||
max-width: 42px;
|
||||
border-radius: 6px 6px 0 0;
|
||||
background: linear-gradient(180deg, rgba(0, 255, 136, 0.75), #00ff88);
|
||||
box-shadow: 0 0 10px rgba(0, 255, 136, 0.12);
|
||||
height: 0;
|
||||
transition: height 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.srs-chart-bar-fill.is-perfect {
|
||||
background: linear-gradient(180deg, #00ff88, rgba(0, 255, 136, 0.85));
|
||||
box-shadow: 0 0 14px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
.srs-chart-bar-fill.is-low {
|
||||
background: linear-gradient(180deg, rgba(255, 165, 2, 0.65), rgba(255, 165, 2, 0.35));
|
||||
box-shadow: 0 0 10px rgba(255, 165, 2, 0.12);
|
||||
}
|
||||
|
||||
.srs-chart-bar-score {
|
||||
position: absolute;
|
||||
top: -16px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 10px;
|
||||
font-weight: 900;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.srs-chart-bar-sub {
|
||||
font-size: 7px;
|
||||
color: rgba(150, 155, 185, 0.45);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.srs-chart-bar-star {
|
||||
color: #00ff88;
|
||||
font-size: 8px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.srs-chart-col-label {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.srs-chart-col-name {
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
color: #eeeef6;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.srs-chart-col-desc {
|
||||
font-size: 7px;
|
||||
color: rgba(150, 155, 185, 0.45);
|
||||
margin-top: 1px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* Triple Section */
|
||||
.srs-triple-section {
|
||||
display: flex;
|
||||
gap: 7px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.srs-section-card {
|
||||
flex: 1;
|
||||
padding: 8px 10px;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.srs-section-card:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.srs-section-card.is-highlight {
|
||||
background: rgba(0, 255, 136, 0.03);
|
||||
border: 1px solid rgba(0, 255, 136, 0.1);
|
||||
}
|
||||
|
||||
.srs-section-card.is-highlight::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, #00ff88, rgba(0, 255, 136, 0.3));
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
.srs-section-card.is-highlight:hover {
|
||||
border-color: rgba(0, 255, 136, 0.22);
|
||||
box-shadow: 0 6px 24px rgba(0, 255, 136, 0.08);
|
||||
}
|
||||
|
||||
.srs-section-card.is-weakness {
|
||||
background: rgba(255, 71, 87, 0.02);
|
||||
border: 1px solid rgba(255, 71, 87, 0.08);
|
||||
}
|
||||
|
||||
.srs-section-card.is-weakness::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, #ff4757, rgba(255, 71, 87, 0.3));
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
.srs-section-card.is-weakness:hover {
|
||||
border-color: rgba(255, 71, 87, 0.2);
|
||||
box-shadow: 0 6px 24px rgba(255, 71, 87, 0.06);
|
||||
}
|
||||
|
||||
.srs-section-card.is-optimize {
|
||||
background: rgba(79, 195, 247, 0.02);
|
||||
border: 1px solid rgba(79, 195, 247, 0.08);
|
||||
}
|
||||
|
||||
.srs-section-card.is-optimize::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, #4fc3f7, rgba(79, 195, 247, 0.3));
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
.srs-section-card.is-optimize:hover {
|
||||
border-color: rgba(79, 195, 247, 0.2);
|
||||
box-shadow: 0 6px 24px rgba(79, 195, 247, 0.06);
|
||||
}
|
||||
|
||||
.srs-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.srs-section-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.is-highlight .srs-section-icon { background: rgba(0, 255, 136, 0.12); }
|
||||
.is-weakness .srs-section-icon { background: rgba(255, 71, 87, 0.12); }
|
||||
.is-optimize .srs-section-icon { background: rgba(79, 195, 247, 0.12); }
|
||||
|
||||
.srs-section-label {
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.is-highlight .srs-section-label { color: #00ff88; }
|
||||
.is-weakness .srs-section-label { color: #ff4757; }
|
||||
.is-optimize .srs-section-label { color: #4fc3f7; }
|
||||
|
||||
.srs-section-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.srs-section-item {
|
||||
padding: 4px 6px;
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border: 1px solid rgba(255, 255, 255, 0.03);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.srs-section-item:hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.srs-section-item-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.srs-section-item-dim {
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
color: #eeeef6;
|
||||
}
|
||||
|
||||
.srs-section-item-score {
|
||||
font-size: 8px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.srs-section-item-score.is-green { color: #00ff88; }
|
||||
.srs-section-item-score.is-red { color: #ff4757; }
|
||||
|
||||
.srs-section-item-text {
|
||||
font-size: 8px;
|
||||
line-height: 1.4;
|
||||
color: rgba(150, 155, 185, 0.45);
|
||||
}
|
||||
|
||||
.srs-section-item-badge {
|
||||
font-size: 7px;
|
||||
font-weight: 700;
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.srs-section-item-badge.badge-red {
|
||||
background: rgba(255, 71, 87, 0.1);
|
||||
color: #ff4757;
|
||||
}
|
||||
|
||||
.srs-section-item-badge.badge-orange {
|
||||
background: rgba(255, 165, 2, 0.1);
|
||||
color: #ffa502;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.omni-script-review-showcase {
|
||||
padding: 8px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.srs-score-hero {
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.srs-score-divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.srs-triple-section {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.srs-score-circle {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.srs-score-circle-inner {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.srs-score-num {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.srs-chart-body {
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/* ===== 剧本评测展示 ===== */
|
||||
.omni-script-review-visual {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: clamp(14px, 2vw, 24px);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Hero */
|
||||
.omni-script-review-hero {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.omni-script-review-score-row {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.omni-script-review-num {
|
||||
font-size: clamp(36px, 5vw, 56px);
|
||||
font-weight: 800;
|
||||
color: var(--accent);
|
||||
line-height: 1;
|
||||
letter-spacing: -2px;
|
||||
}
|
||||
|
||||
.omni-script-review-total {
|
||||
font-size: 14px;
|
||||
color: rgb(255 255 255 / 30%);
|
||||
font-weight: 400;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.omni-script-review-grade {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin-left: 12px;
|
||||
margin-bottom: 8px;
|
||||
padding: 2px 10px;
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 255, 136, 0.08);
|
||||
border: 1px solid rgba(0, 255, 136, 0.2);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.omni-script-review-grade-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
animation: omni-sr-pulse 2s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes omni-sr-pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
|
||||
.omni-script-review-bar {
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
height: 3px;
|
||||
border-radius: 2px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.omni-script-review-bar-fill {
|
||||
height: 100%;
|
||||
border-radius: 2px;
|
||||
background: var(--accent);
|
||||
transition: width 1.4s ease;
|
||||
}
|
||||
|
||||
.omni-script-review-beat {
|
||||
font-size: 11px;
|
||||
color: rgb(255 255 255 / 30%);
|
||||
}
|
||||
|
||||
.omni-script-review-beat b {
|
||||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Chart */
|
||||
.omni-script-review-chart {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.omni-script-review-chart-bars {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
align-items: flex-end;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.omni-script-review-bcol {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.omni-script-review-bbar-area {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.omni-script-review-bseg {
|
||||
width: 70%;
|
||||
min-height: 0;
|
||||
transition: height 1s cubic-bezier(0.4, 0, 0.2, 1), filter 0.25s, opacity 0.25s;
|
||||
}
|
||||
|
||||
.omni-script-review-bseg.is-score {
|
||||
background: linear-gradient(180deg, #33ffaa, var(--accent) 40%, #00cc6a);
|
||||
border-radius: 5px 5px 2px 2px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.omni-script-review-bseg.is-score::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 50%;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.1), transparent);
|
||||
border-radius: 5px 0 0 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.omni-script-review-bseg.is-perfect {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.omni-script-review-bseg.is-loss {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px dashed rgba(255, 255, 255, 0.1);
|
||||
border-bottom: none;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
|
||||
.omni-script-review-bcol:hover .omni-script-review-bseg.is-score {
|
||||
filter: brightness(1.15);
|
||||
box-shadow: 0 0 10px rgba(0, 255, 136, 0.15);
|
||||
}
|
||||
|
||||
.omni-script-review-bcol.is-active .omni-script-review-bseg.is-score {
|
||||
filter: brightness(1.25);
|
||||
box-shadow: 0 0 14px rgba(0, 255, 136, 0.25);
|
||||
}
|
||||
|
||||
.omni-script-review-bcol.is-dimmed .omni-script-review-bseg {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.omni-script-review-blabel {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.omni-script-review-blabel span {
|
||||
font-size: clamp(8px, 0.9vw, 10px);
|
||||
font-weight: 600;
|
||||
color: rgb(255 255 255 / 55%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.omni-script-review-bcol:hover .omni-script-review-blabel span,
|
||||
.omni-script-review-bcol.is-active .omni-script-review-blabel span {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.omni-script-review-diminfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px 0;
|
||||
animation: omni-sr-fadeUp 0.25s ease;
|
||||
}
|
||||
|
||||
@keyframes omni-sr-fadeUp {
|
||||
from { opacity: 0; transform: translateY(4px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.omni-script-review-diminfo-name {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.omni-script-review-diminfo-score {
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
color: var(--accent);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.omni-script-review-diminfo-score small {
|
||||
font-size: 12px;
|
||||
color: rgb(255 255 255 / 30%);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.omni-script-review-legend {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
justify-content: flex-end;
|
||||
font-size: 9px;
|
||||
color: rgb(255 255 255 / 30%);
|
||||
}
|
||||
|
||||
.omni-script-review-legend-dot {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 2px;
|
||||
vertical-align: middle;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.omni-script-review-legend-dot.is-score {
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.omni-script-review-legend-dot.is-loss {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px dashed rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.omni-script-review-chart-bars {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.omni-script-review-bseg {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,884 @@
|
||||
/* ===== 工具箱功能页 ===== */
|
||||
.omni-home__toolbox-page {
|
||||
--toolbox-green: #00ff88;
|
||||
--toolbox-blue: #4fc3f7;
|
||||
--toolbox-purple: #a855f7;
|
||||
--toolbox-surface: rgba(14, 16, 38, 0.75);
|
||||
--toolbox-elevated: rgba(20, 22, 52, 0.85);
|
||||
--toolbox-highlight: rgba(28, 31, 68, 0.9);
|
||||
--toolbox-border-subtle: rgba(0, 255, 136, 0.08);
|
||||
--toolbox-border-default: rgba(0, 255, 136, 0.14);
|
||||
--toolbox-border-hover: rgba(0, 255, 136, 0.28);
|
||||
--toolbox-text-primary: #f0f0f5;
|
||||
--toolbox-text-secondary: rgba(240, 240, 245, 0.6);
|
||||
--toolbox-text-tertiary: rgba(240, 240, 245, 0.4);
|
||||
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
min-height: var(--home-section-min-height);
|
||||
border-top: 1px solid rgb(255 255 255 / 8%);
|
||||
background:
|
||||
linear-gradient(180deg, #070b10 0%, #05080d 100%),
|
||||
radial-gradient(ellipse 80% 60% at 50% 40%, rgba(0, 255, 136, 0.04) 0%, transparent 70%),
|
||||
radial-gradient(ellipse 60% 50% at 80% 70%, rgba(79, 195, 247, 0.03) 0%, transparent 60%),
|
||||
radial-gradient(ellipse 50% 40% at 20% 80%, rgba(168, 85, 247, 0.03) 0%, transparent 60%);
|
||||
scroll-snap-align: start;
|
||||
scroll-snap-stop: normal;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-shell {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
gap: clamp(20px, 3vw, 40px);
|
||||
padding: clamp(42px, 6vw, 82px) clamp(22px, 7vw, 92px);
|
||||
min-height: var(--home-section-min-height);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ===== Left Panel ===== */
|
||||
.omni-home__toolbox-left {
|
||||
width: clamp(340px, 30vw, 440px);
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
justify-content: flex-start;
|
||||
padding-top: clamp(40px, 8vh, 100px);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-brand-icon {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
background: var(--toolbox-green);
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #0a0b12;
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-brand-icon .anticon {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-brand-text {
|
||||
font-weight: 900;
|
||||
font-size: 30px;
|
||||
color: #fff;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-title {
|
||||
font-weight: 900;
|
||||
font-size: clamp(34px, 3.6vw, 46px);
|
||||
line-height: 1.15;
|
||||
background: linear-gradient(135deg, var(--toolbox-green), var(--toolbox-blue));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-subtitle {
|
||||
font-size: 17px;
|
||||
line-height: 1.6;
|
||||
color: var(--toolbox-text-secondary);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
padding: 18px 22px;
|
||||
border-radius: 16px;
|
||||
background: var(--toolbox-surface);
|
||||
border: 1px solid var(--toolbox-border-subtle);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
cursor: pointer;
|
||||
animation: omni-toolbox-fadeSlideIn 0.6s ease both;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-item:nth-child(1) { animation-delay: 0.1s; }
|
||||
.omni-home__toolbox-item:nth-child(2) { animation-delay: 0.2s; }
|
||||
.omni-home__toolbox-item:nth-child(3) { animation-delay: 0.3s; }
|
||||
.omni-home__toolbox-item:nth-child(4) { animation-delay: 0.4s; }
|
||||
|
||||
.omni-home__toolbox-item:hover {
|
||||
border-color: var(--toolbox-border-hover);
|
||||
transform: translateX(4px);
|
||||
background: var(--toolbox-elevated);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-item-icon {
|
||||
font-size: 28px;
|
||||
flex-shrink: 0;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
background: rgba(0, 255, 136, 0.08);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-item-name {
|
||||
font-weight: 700;
|
||||
font-size: 17px;
|
||||
color: var(--toolbox-text-primary);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-item-desc {
|
||||
font-size: 14px;
|
||||
color: var(--toolbox-text-tertiary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@keyframes omni-toolbox-fadeSlideIn {
|
||||
from { opacity: 0; transform: translateX(-12px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
.omni-home__toolbox-workflow {
|
||||
margin-top: auto;
|
||||
padding: 20px 24px;
|
||||
border-radius: 16px;
|
||||
background: var(--toolbox-surface);
|
||||
border: 1px solid var(--toolbox-border-subtle);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-workflow-label {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: var(--toolbox-green);
|
||||
margin-bottom: 12px;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-workflow-steps {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
color: var(--toolbox-text-tertiary);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-workflow-step {
|
||||
color: var(--toolbox-text-secondary);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-workflow-arrow {
|
||||
color: var(--toolbox-green);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* ===== Grid Area ===== */
|
||||
.omni-home__toolbox-grid {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
gap: 16px;
|
||||
min-height: clamp(360px, 40vw, 520px);
|
||||
}
|
||||
|
||||
/* ===== Tool Cards ===== */
|
||||
.omni-home__toolbox-card {
|
||||
position: relative;
|
||||
border-radius: 18px;
|
||||
background: var(--toolbox-elevated);
|
||||
border: 1px solid var(--toolbox-border-default);
|
||||
backdrop-filter: blur(20px);
|
||||
overflow: hidden;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
animation: omni-toolbox-cardIn 0.7s ease both;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card:nth-child(1) { animation-delay: 0.15s; }
|
||||
.omni-home__toolbox-card:nth-child(2) { animation-delay: 0.25s; }
|
||||
.omni-home__toolbox-card:nth-child(3) { animation-delay: 0.35s; }
|
||||
.omni-home__toolbox-card:nth-child(4) { animation-delay: 0.45s; }
|
||||
|
||||
.omni-home__toolbox-card:hover {
|
||||
transform: translateY(-6px) scale(1.01);
|
||||
border-color: var(--toolbox-border-hover);
|
||||
box-shadow:
|
||||
0 12px 40px rgba(0, 255, 136, 0.08),
|
||||
0 0 60px rgba(0, 255, 136, 0.04);
|
||||
}
|
||||
|
||||
@keyframes omni-toolbox-cardIn {
|
||||
from { opacity: 0; transform: translateY(20px) scale(0.97); }
|
||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 18px 0;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card-header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 9px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
border: 1px solid rgba(0, 255, 136, 0.12);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card-title {
|
||||
font-weight: 900;
|
||||
font-size: 14px;
|
||||
color: var(--toolbox-text-primary);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card-tag {
|
||||
padding: 3px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
color: var(--toolbox-green);
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
border: 1px solid rgba(0, 255, 136, 0.2);
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px 18px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card-footer {
|
||||
padding: 8px 18px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card-feat {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
color: var(--toolbox-text-tertiary);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card-feat-sep {
|
||||
color: rgba(0, 255, 136, 0.2);
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* === Card 1: 图片工作室 === */
|
||||
.toolbox-card1-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
gap: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toolbox-card1-side {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.toolbox-card1-left {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.toolbox-card1-right {
|
||||
background: rgba(0, 255, 136, 0.02);
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.toolbox-card1-img {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toolbox-card1-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.toolbox-card1-left .toolbox-card1-img {
|
||||
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.toolbox-card1-right .toolbox-card1-img {
|
||||
box-shadow: 0 0 12px rgba(0, 255, 136, 0.06);
|
||||
}
|
||||
|
||||
.toolbox-card1-label {
|
||||
font-size: 10px;
|
||||
color: var(--toolbox-text-tertiary);
|
||||
margin-top: 6px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.toolbox-card1-left .toolbox-card1-label {
|
||||
color: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
.toolbox-card1-right .toolbox-card1-label {
|
||||
color: rgba(0, 255, 136, 0.5);
|
||||
}
|
||||
|
||||
.toolbox-card1-divider {
|
||||
width: 1px;
|
||||
background: linear-gradient(to bottom, transparent, rgba(0, 255, 136, 0.3), transparent);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 8%;
|
||||
height: 84%;
|
||||
}
|
||||
|
||||
/* === Card 2: 镜头实验室 === */
|
||||
.toolbox-card2-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.toolbox-card2-frame {
|
||||
flex: 1;
|
||||
height: 85%;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
background: linear-gradient(180deg, #1a1d42 0%, #141230 100%);
|
||||
border: 1px solid rgba(0, 255, 136, 0.06);
|
||||
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.toolbox-card2-frame:hover {
|
||||
border-color: rgba(0, 255, 136, 0.2);
|
||||
box-shadow: 0 0 16px rgba(0, 255, 136, 0.08);
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
.toolbox-card2-product {
|
||||
position: absolute;
|
||||
top: 14%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 55%;
|
||||
height: 50%;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.toolbox-card2-frame:nth-child(1) .toolbox-card2-product {
|
||||
background: repeating-linear-gradient(0deg, #6b9b7a 0px, #6b9b7a 2px, #d4dfc8 2px, #d4dfc8 4px);
|
||||
}
|
||||
|
||||
.toolbox-card2-frame:nth-child(2) .toolbox-card2-product {
|
||||
background: repeating-linear-gradient(0deg, #6b9b7a 0px, #6b9b7a 2px, #d4dfc8 2px, #d4dfc8 4px);
|
||||
transform: translateX(-50%) perspective(200px) rotateY(25deg);
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.toolbox-card2-frame:nth-child(3) .toolbox-card2-product {
|
||||
background: repeating-linear-gradient(90deg, #6b9b7a 0px, #6b9b7a 2px, #d4dfc8 2px, #d4dfc8 4px);
|
||||
width: 50%;
|
||||
height: 40%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.toolbox-card2-frame:nth-child(4) .toolbox-card2-product {
|
||||
background: repeating-linear-gradient(0deg, #6b9b7a 0px, #6b9b7a 2px, #d4dfc8 2px, #d4dfc8 4px);
|
||||
width: 58%;
|
||||
transform: translateX(-50%) perspective(200px) rotateX(-15deg);
|
||||
}
|
||||
|
||||
.toolbox-card2-frame:nth-child(5) .toolbox-card2-product {
|
||||
background: repeating-linear-gradient(0deg, #5a7a4e 0px, #5a7a4e 2px, #b8c8a8 2px, #b8c8a8 4px);
|
||||
width: 50%;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.toolbox-card2-shadow {
|
||||
position: absolute;
|
||||
top: 66%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40%;
|
||||
height: 4px;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 255, 136, 0.06);
|
||||
filter: blur(3px);
|
||||
}
|
||||
|
||||
.toolbox-card2-angle-label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
color: var(--toolbox-text-tertiary);
|
||||
margin-bottom: 10%;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 255, 136, 0.06);
|
||||
border: 1px solid rgba(0, 255, 136, 0.08);
|
||||
}
|
||||
|
||||
.toolbox-card2-frame:nth-child(1) .toolbox-card2-angle-label {
|
||||
color: var(--toolbox-green);
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
border-color: rgba(0, 255, 136, 0.2);
|
||||
}
|
||||
|
||||
/* === Card 3: 一键数字人 === */
|
||||
.toolbox-card3-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
gap: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toolbox-card3-side {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toolbox-card3-left {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.toolbox-card3-right {
|
||||
background: rgba(0, 255, 136, 0.02);
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.toolbox-card3-portrait {
|
||||
width: 70%;
|
||||
aspect-ratio: 3/4;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toolbox-card3-left .toolbox-card3-portrait {
|
||||
background: linear-gradient(180deg, #2a2d5e, #1e2050);
|
||||
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.toolbox-card3-left .toolbox-card3-portrait::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 14%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 32%;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 50%;
|
||||
background: rgba(200, 190, 220, 0.1);
|
||||
}
|
||||
|
||||
.toolbox-card3-left .toolbox-card3-portrait::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 42%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 50%;
|
||||
height: 40%;
|
||||
border-radius: 20% 20% 5% 5%;
|
||||
background: rgba(200, 190, 220, 0.06);
|
||||
}
|
||||
|
||||
.toolbox-card3-portrait-mark {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
left: 8px;
|
||||
font-size: 8px;
|
||||
font-weight: 700;
|
||||
color: rgba(255, 255, 255, 0.25);
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.toolbox-card3-right .toolbox-card3-portrait {
|
||||
background: linear-gradient(180deg, #1a3a2e, #0d2a20);
|
||||
border: 1px solid rgba(0, 255, 136, 0.12);
|
||||
box-shadow:
|
||||
0 0 30px rgba(0, 255, 136, 0.08),
|
||||
inset 0 0 20px rgba(0, 255, 136, 0.04);
|
||||
}
|
||||
|
||||
.toolbox-card3-right .toolbox-card3-portrait::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 14%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 32%;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
box-shadow: 0 0 20px rgba(0, 255, 136, 0.15);
|
||||
}
|
||||
|
||||
.toolbox-card3-right .toolbox-card3-portrait::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 42%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 50%;
|
||||
height: 40%;
|
||||
border-radius: 20% 20% 5% 5%;
|
||||
background: rgba(0, 255, 136, 0.06);
|
||||
box-shadow: 0 0 15px rgba(0, 255, 136, 0.08);
|
||||
}
|
||||
|
||||
.toolbox-card3-glow-ring {
|
||||
position: absolute;
|
||||
inset: -4px;
|
||||
border-radius: 14px;
|
||||
border: 1.5px solid rgba(0, 255, 136, 0.2);
|
||||
animation: omni-toolbox-glowPulse 2.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes omni-toolbox-glowPulse {
|
||||
0%, 100% { opacity: 0.3; box-shadow: 0 0 10px rgba(0, 255, 136, 0.05); }
|
||||
50% { opacity: 1; box-shadow: 0 0 25px rgba(0, 255, 136, 0.15); }
|
||||
}
|
||||
|
||||
.toolbox-card3-lipsync {
|
||||
position: absolute;
|
||||
top: 32%;
|
||||
left: 62%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5px;
|
||||
}
|
||||
|
||||
.toolbox-card3-lipsync span {
|
||||
width: 2px;
|
||||
border-radius: 1px;
|
||||
background: var(--toolbox-green);
|
||||
animation: omni-toolbox-lipsync 0.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.toolbox-card3-lipsync span:nth-child(1) { height: 4px; animation-delay: 0s; }
|
||||
.toolbox-card3-lipsync span:nth-child(2) { height: 8px; animation-delay: 0.1s; }
|
||||
.toolbox-card3-lipsync span:nth-child(3) { height: 5px; animation-delay: 0.2s; }
|
||||
.toolbox-card3-lipsync span:nth-child(4) { height: 10px; animation-delay: 0.3s; }
|
||||
.toolbox-card3-lipsync span:nth-child(5) { height: 4px; animation-delay: 0.4s; }
|
||||
|
||||
@keyframes omni-toolbox-lipsync {
|
||||
0%, 100% { transform: scaleY(1); opacity: 0.6; }
|
||||
50% { transform: scaleY(0.3); opacity: 1; }
|
||||
}
|
||||
|
||||
.toolbox-card3-gesture {
|
||||
position: absolute;
|
||||
top: 55%;
|
||||
left: 20%;
|
||||
width: 24px;
|
||||
height: 2px;
|
||||
border-radius: 1px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.toolbox-card3-gesture::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(0, 255, 136, 0.5), transparent);
|
||||
animation: omni-toolbox-gestureMove 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.toolbox-card3-gesture::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -4px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid rgba(0, 255, 136, 0.3);
|
||||
animation: omni-toolbox-gestureMove 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes omni-toolbox-gestureMove {
|
||||
0%, 100% { opacity: 0.2; transform: translateX(0); }
|
||||
50% { opacity: 0.8; transform: translateX(6px); }
|
||||
}
|
||||
|
||||
.toolbox-card3-live {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
font-size: 8px;
|
||||
font-weight: 900;
|
||||
color: #0a0b12;
|
||||
background: var(--toolbox-green);
|
||||
padding: 2px 7px;
|
||||
border-radius: 4px;
|
||||
letter-spacing: 1px;
|
||||
animation: omni-toolbox-livePulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes omni-toolbox-livePulse {
|
||||
0%, 100% { box-shadow: 0 0 6px rgba(0, 255, 136, 0.3); }
|
||||
50% { box-shadow: 0 0 16px rgba(0, 255, 136, 0.6); }
|
||||
}
|
||||
|
||||
.toolbox-card3-transform {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 2;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--toolbox-green);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
color: #0a0b12;
|
||||
box-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
|
||||
animation: omni-toolbox-transformSpin 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes omni-toolbox-transformSpin {
|
||||
0%, 100% { box-shadow: 0 0 20px rgba(0, 255, 136, 0.3); }
|
||||
50% { box-shadow: 0 0 30px rgba(0, 255, 136, 0.5); }
|
||||
}
|
||||
|
||||
.toolbox-card3-label {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.toolbox-card3-left .toolbox-card3-label {
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.toolbox-card3-right .toolbox-card3-label {
|
||||
color: rgba(0, 255, 136, 0.5);
|
||||
}
|
||||
|
||||
.toolbox-card3-divider {
|
||||
width: 1px;
|
||||
background: linear-gradient(to bottom, transparent, rgba(0, 255, 136, 0.25), transparent);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 8%;
|
||||
height: 84%;
|
||||
}
|
||||
|
||||
/* === Card 4: 去除水印 === */
|
||||
.toolbox-card4-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
gap: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toolbox-card4-side {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toolbox-card4-left {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.toolbox-card4-right {
|
||||
background: rgba(0, 255, 136, 0.02);
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.toolbox-card4-img {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toolbox-card4-img img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.toolbox-card4-left .toolbox-card4-img {
|
||||
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.toolbox-card4-right .toolbox-card4-img {
|
||||
box-shadow: 0 0 12px rgba(0, 255, 136, 0.06);
|
||||
}
|
||||
|
||||
.toolbox-card4-label {
|
||||
font-size: 10px;
|
||||
color: var(--toolbox-text-tertiary);
|
||||
margin-top: 8px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.toolbox-card4-left .toolbox-card4-label {
|
||||
color: rgba(255, 200, 200, 0.5);
|
||||
}
|
||||
|
||||
.toolbox-card4-right .toolbox-card4-label {
|
||||
color: rgba(0, 255, 136, 0.5);
|
||||
}
|
||||
|
||||
.toolbox-card4-divider {
|
||||
width: 1px;
|
||||
background: linear-gradient(to bottom, transparent, rgba(0, 255, 136, 0.3), transparent);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 8%;
|
||||
height: 84%;
|
||||
}
|
||||
|
||||
/* ===== Responsive ===== */
|
||||
@media (max-width: 980px) {
|
||||
.omni-home__toolbox-shell {
|
||||
flex-direction: column;
|
||||
padding: 48px 22px 64px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-left {
|
||||
width: 100%;
|
||||
flex-shrink: unset;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-grid {
|
||||
width: 100%;
|
||||
min-height: clamp(480px, 70vw, 700px);
|
||||
}
|
||||
|
||||
.omni-home__toolbox-workflow {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.omni-home__toolbox-shell {
|
||||
padding: 36px 18px 48px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-grid {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.omni-home__toolbox-card {
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.omni-home__toolbox-item,
|
||||
.omni-home__toolbox-card {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.toolbox-card3-glow-ring,
|
||||
.toolbox-card3-lipsync span,
|
||||
.toolbox-card3-gesture::before,
|
||||
.toolbox-card3-gesture::after,
|
||||
.toolbox-card3-live,
|
||||
.toolbox-card3-transform {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user