mirror of
https://github.com/potato47/ccc-devtools.git
synced 2026-04-06 13:22:32 +00:00
feat: 整体重构
This commit is contained in:
1
packages/cccdev-template-3x/.gitignore
vendored
Normal file
1
packages/cccdev-template-3x/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
template/devtools/
|
||||
281
packages/cccdev-template-3x/bun.lock
Normal file
281
packages/cccdev-template-3x/bun.lock
Normal file
@@ -0,0 +1,281 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "cccdev-template-3x",
|
||||
"devDependencies": {
|
||||
"@preact/preset-vite": "^2.9.1",
|
||||
"@preact/signals": "^1.3.0",
|
||||
"preact": "^10.24.3",
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^5.4.11",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@babel/code-frame": ["@babel/code-frame@7.29.0", "https://mirrors.tencent.com/npm/@babel/code-frame/-/code-frame-7.29.0.tgz", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
|
||||
|
||||
"@babel/compat-data": ["@babel/compat-data@7.29.0", "https://mirrors.tencent.com/npm/@babel/compat-data/-/compat-data-7.29.0.tgz", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="],
|
||||
|
||||
"@babel/core": ["@babel/core@7.29.0", "https://mirrors.tencent.com/npm/@babel/core/-/core-7.29.0.tgz", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="],
|
||||
|
||||
"@babel/generator": ["@babel/generator@7.29.1", "https://mirrors.tencent.com/npm/@babel/generator/-/generator-7.29.1.tgz", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
|
||||
|
||||
"@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "https://mirrors.tencent.com/npm/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
|
||||
|
||||
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "https://mirrors.tencent.com/npm/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
|
||||
|
||||
"@babel/helper-globals": ["@babel/helper-globals@7.28.0", "https://mirrors.tencent.com/npm/@babel/helper-globals/-/helper-globals-7.28.0.tgz", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
|
||||
|
||||
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "https://mirrors.tencent.com/npm/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="],
|
||||
|
||||
"@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "https://mirrors.tencent.com/npm/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="],
|
||||
|
||||
"@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "https://mirrors.tencent.com/npm/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="],
|
||||
|
||||
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "https://mirrors.tencent.com/npm/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "https://mirrors.tencent.com/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
||||
|
||||
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "https://mirrors.tencent.com/npm/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||
|
||||
"@babel/helpers": ["@babel/helpers@7.28.6", "https://mirrors.tencent.com/npm/@babel/helpers/-/helpers-7.28.6.tgz", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="],
|
||||
|
||||
"@babel/parser": ["@babel/parser@7.29.0", "https://mirrors.tencent.com/npm/@babel/parser/-/parser-7.29.0.tgz", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="],
|
||||
|
||||
"@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "https://mirrors.tencent.com/npm/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.28.6", "https://mirrors.tencent.com/npm/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-module-imports": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-syntax-jsx": "^7.28.6", "@babel/types": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "https://mirrors.tencent.com/npm/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="],
|
||||
|
||||
"@babel/template": ["@babel/template@7.28.6", "https://mirrors.tencent.com/npm/@babel/template/-/template-7.28.6.tgz", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
|
||||
|
||||
"@babel/traverse": ["@babel/traverse@7.29.0", "https://mirrors.tencent.com/npm/@babel/traverse/-/traverse-7.29.0.tgz", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
|
||||
|
||||
"@babel/types": ["@babel/types@7.29.0", "https://mirrors.tencent.com/npm/@babel/types/-/types-7.29.0.tgz", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "https://mirrors.tencent.com/npm/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "https://mirrors.tencent.com/npm/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||
|
||||
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "https://mirrors.tencent.com/npm/@jridgewell/remapping/-/remapping-2.3.5.tgz", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "https://mirrors.tencent.com/npm/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "https://mirrors.tencent.com/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "https://mirrors.tencent.com/npm/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@preact/preset-vite": ["@preact/preset-vite@2.10.3", "https://mirrors.tencent.com/npm/@preact/preset-vite/-/preset-vite-2.10.3.tgz", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@prefresh/vite": "^2.4.11", "@rollup/pluginutils": "^5.0.0", "babel-plugin-transform-hook-names": "^1.0.2", "debug": "^4.4.3", "picocolors": "^1.1.1", "vite-prerender-plugin": "^0.5.8" }, "peerDependencies": { "@babel/core": "7.x", "vite": "2.x || 3.x || 4.x || 5.x || 6.x || 7.x" } }, "sha512-1SiS+vFItpkNdBs7q585PSAIln0wBeBdcpJYbzPs1qipsb/FssnkUioNXuRsb8ZnU8YEQHr+3v8+/mzWSnTQmg=="],
|
||||
|
||||
"@preact/signals": ["@preact/signals@1.3.4", "https://mirrors.tencent.com/npm/@preact/signals/-/signals-1.3.4.tgz", { "dependencies": { "@preact/signals-core": "^1.7.0" }, "peerDependencies": { "preact": "10.x" } }, "sha512-TPMkStdT0QpSc8FpB63aOwXoSiZyIrPsP9Uj347KopdS6olZdAYeeird/5FZv/M1Yc1ge5qstub2o8VDbvkT4g=="],
|
||||
|
||||
"@preact/signals-core": ["@preact/signals-core@1.14.0", "https://mirrors.tencent.com/npm/@preact/signals-core/-/signals-core-1.14.0.tgz", {}, "sha512-AowtCcCU/33lFlh1zRFf/u+12rfrhtNakj7UpaGEsmMwUKpKWMVvcktOGcwBBNiB4lWrZWc01LhiyyzVklJyaQ=="],
|
||||
|
||||
"@prefresh/babel-plugin": ["@prefresh/babel-plugin@0.5.3", "https://mirrors.tencent.com/npm/@prefresh/babel-plugin/-/babel-plugin-0.5.3.tgz", {}, "sha512-57LX2SHs4BX2s1IwCjNzTE2OJeEepRCNf1VTEpbNcUyHfMO68eeOWGDIt4ob9aYlW6PEWZ1SuwNikuoIXANDtQ=="],
|
||||
|
||||
"@prefresh/core": ["@prefresh/core@1.5.9", "https://mirrors.tencent.com/npm/@prefresh/core/-/core-1.5.9.tgz", { "peerDependencies": { "preact": "^10.0.0 || ^11.0.0-0" } }, "sha512-IKBKCPaz34OFVC+adiQ2qaTF5qdztO2/4ZPf4KsRTgjKosWqxVXmEbxCiUydYZRY8GVie+DQlKzQr9gt6HQ+EQ=="],
|
||||
|
||||
"@prefresh/utils": ["@prefresh/utils@1.2.1", "https://mirrors.tencent.com/npm/@prefresh/utils/-/utils-1.2.1.tgz", {}, "sha512-vq/sIuN5nYfYzvyayXI4C2QkprfNaHUQ9ZX+3xLD8nL3rWyzpxOm1+K7RtMbhd+66QcaISViK7amjnheQ/4WZw=="],
|
||||
|
||||
"@prefresh/vite": ["@prefresh/vite@2.4.12", "https://mirrors.tencent.com/npm/@prefresh/vite/-/vite-2.4.12.tgz", { "dependencies": { "@babel/core": "^7.22.1", "@prefresh/babel-plugin": "^0.5.2", "@prefresh/core": "^1.5.0", "@prefresh/utils": "^1.2.0", "@rollup/pluginutils": "^4.2.1" }, "peerDependencies": { "preact": "^10.4.0 || ^11.0.0-0", "vite": ">=2.0.0" } }, "sha512-FY1fzXpUjiuosznMV0YM7XAOPZjB5FIdWS0W24+XnlxYkt9hNAwwsiKYn+cuTEoMtD/ZVazS5QVssBr9YhpCQA=="],
|
||||
|
||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "https://mirrors.tencent.com/npm/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", { "os": "android", "cpu": "arm64" }, "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", { "os": "linux", "cpu": "arm" }, "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", { "os": "linux", "cpu": "none" }, "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", { "os": "linux", "cpu": "x64" }, "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg=="],
|
||||
|
||||
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ=="],
|
||||
|
||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", { "os": "none", "cpu": "arm64" }, "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "https://mirrors.tencent.com/npm/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "https://mirrors.tencent.com/npm/@types/estree/-/estree-1.0.8.tgz", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"babel-plugin-transform-hook-names": ["babel-plugin-transform-hook-names@1.0.2", "https://mirrors.tencent.com/npm/babel-plugin-transform-hook-names/-/babel-plugin-transform-hook-names-1.0.2.tgz", { "peerDependencies": { "@babel/core": "^7.12.10" } }, "sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw=="],
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.8", "https://mirrors.tencent.com/npm/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ=="],
|
||||
|
||||
"boolbase": ["boolbase@1.0.0", "https://mirrors.tencent.com/npm/boolbase/-/boolbase-1.0.0.tgz", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
||||
|
||||
"browserslist": ["browserslist@4.28.1", "https://mirrors.tencent.com/npm/browserslist/-/browserslist-4.28.1.tgz", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001779", "https://mirrors.tencent.com/npm/caniuse-lite/-/caniuse-lite-1.0.30001779.tgz", {}, "sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA=="],
|
||||
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "https://mirrors.tencent.com/npm/convert-source-map/-/convert-source-map-2.0.0.tgz", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"css-select": ["css-select@5.2.2", "https://mirrors.tencent.com/npm/css-select/-/css-select-5.2.2.tgz", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
|
||||
|
||||
"css-what": ["css-what@6.2.2", "https://mirrors.tencent.com/npm/css-what/-/css-what-6.2.2.tgz", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "https://mirrors.tencent.com/npm/debug/-/debug-4.4.3.tgz", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "https://mirrors.tencent.com/npm/dom-serializer/-/dom-serializer-2.0.0.tgz", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
|
||||
"domelementtype": ["domelementtype@2.3.0", "https://mirrors.tencent.com/npm/domelementtype/-/domelementtype-2.3.0.tgz", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
|
||||
"domhandler": ["domhandler@5.0.3", "https://mirrors.tencent.com/npm/domhandler/-/domhandler-5.0.3.tgz", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||
|
||||
"domutils": ["domutils@3.2.2", "https://mirrors.tencent.com/npm/domutils/-/domutils-3.2.2.tgz", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.313", "https://mirrors.tencent.com/npm/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", {}, "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA=="],
|
||||
|
||||
"entities": ["entities@4.5.0", "https://mirrors.tencent.com/npm/entities/-/entities-4.5.0.tgz", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"esbuild": ["esbuild@0.21.5", "https://mirrors.tencent.com/npm/esbuild/-/esbuild-0.21.5.tgz", { "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" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "https://mirrors.tencent.com/npm/escalade/-/escalade-3.2.0.tgz", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"estree-walker": ["estree-walker@2.0.2", "https://mirrors.tencent.com/npm/estree-walker/-/estree-walker-2.0.2.tgz", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "https://mirrors.tencent.com/npm/fsevents/-/fsevents-2.3.3.tgz", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "https://mirrors.tencent.com/npm/gensync/-/gensync-1.0.0-beta.2.tgz", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"he": ["he@1.2.0", "https://mirrors.tencent.com/npm/he/-/he-1.2.0.tgz", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "https://mirrors.tencent.com/npm/js-tokens/-/js-tokens-4.0.0.tgz", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "https://mirrors.tencent.com/npm/jsesc/-/jsesc-3.1.0.tgz", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
"json5": ["json5@2.2.3", "https://mirrors.tencent.com/npm/json5/-/json5-2.2.3.tgz", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"kolorist": ["kolorist@1.8.0", "https://mirrors.tencent.com/npm/kolorist/-/kolorist-1.8.0.tgz", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "https://mirrors.tencent.com/npm/lru-cache/-/lru-cache-5.1.1.tgz", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "https://mirrors.tencent.com/npm/magic-string/-/magic-string-0.30.21.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "https://mirrors.tencent.com/npm/ms/-/ms-2.1.3.tgz", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "https://mirrors.tencent.com/npm/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"node-html-parser": ["node-html-parser@6.1.13", "https://mirrors.tencent.com/npm/node-html-parser/-/node-html-parser-6.1.13.tgz", { "dependencies": { "css-select": "^5.1.0", "he": "1.2.0" } }, "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg=="],
|
||||
|
||||
"node-releases": ["node-releases@2.0.36", "https://mirrors.tencent.com/npm/node-releases/-/node-releases-2.0.36.tgz", {}, "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA=="],
|
||||
|
||||
"nth-check": ["nth-check@2.1.1", "https://mirrors.tencent.com/npm/nth-check/-/nth-check-2.1.1.tgz", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "https://mirrors.tencent.com/npm/picocolors/-/picocolors-1.1.1.tgz", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "https://mirrors.tencent.com/npm/picomatch/-/picomatch-4.0.3.tgz", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"postcss": ["postcss@8.5.8", "https://mirrors.tencent.com/npm/postcss/-/postcss-8.5.8.tgz", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
|
||||
|
||||
"preact": ["preact@10.29.0", "https://mirrors.tencent.com/npm/preact/-/preact-10.29.0.tgz", {}, "sha512-wSAGyk2bYR1c7t3SZ3jHcM6xy0lcBcDel6lODcs9ME6Th++Dx2KU+6D3HD8wMMKGA8Wpw7OMd3/4RGzYRpzwRg=="],
|
||||
|
||||
"rollup": ["rollup@4.59.0", "https://mirrors.tencent.com/npm/rollup/-/rollup-4.59.0.tgz", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="],
|
||||
|
||||
"semver": ["semver@6.3.1", "https://mirrors.tencent.com/npm/semver/-/semver-6.3.1.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"simple-code-frame": ["simple-code-frame@1.3.0", "https://mirrors.tencent.com/npm/simple-code-frame/-/simple-code-frame-1.3.0.tgz", { "dependencies": { "kolorist": "^1.6.0" } }, "sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w=="],
|
||||
|
||||
"source-map": ["source-map@0.7.6", "https://mirrors.tencent.com/npm/source-map/-/source-map-0.7.6.tgz", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "https://mirrors.tencent.com/npm/source-map-js/-/source-map-js-1.2.1.tgz", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"stack-trace": ["stack-trace@1.0.0-pre2", "https://mirrors.tencent.com/npm/stack-trace/-/stack-trace-1.0.0-pre2.tgz", {}, "sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "https://mirrors.tencent.com/npm/typescript/-/typescript-5.9.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"update-browserslist-db": ["update-browserslist-db@1.2.3", "https://mirrors.tencent.com/npm/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
||||
|
||||
"vite": ["vite@5.4.21", "https://mirrors.tencent.com/npm/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
|
||||
|
||||
"vite-prerender-plugin": ["vite-prerender-plugin@0.5.13", "https://mirrors.tencent.com/npm/vite-prerender-plugin/-/vite-prerender-plugin-0.5.13.tgz", { "dependencies": { "kolorist": "^1.8.0", "magic-string": "0.x >= 0.26.0", "node-html-parser": "^6.1.12", "simple-code-frame": "^1.3.0", "source-map": "^0.7.4", "stack-trace": "^1.0.0-pre2" }, "peerDependencies": { "vite": "5.x || 6.x || 7.x || 8.x" } }, "sha512-IKSpYkzDBsKAxa05naRbj7GvNVMSdww/Z/E89oO3xndz+gWnOBOKOAbEXv7qDhktY/j3vHgJmoV1pPzqU2tx9g=="],
|
||||
|
||||
"yallist": ["yallist@3.1.1", "https://mirrors.tencent.com/npm/yallist/-/yallist-3.1.1.tgz", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"@prefresh/vite/@rollup/pluginutils": ["@rollup/pluginutils@4.2.1", "https://mirrors.tencent.com/npm/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", { "dependencies": { "estree-walker": "^2.0.1", "picomatch": "^2.2.2" } }, "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ=="],
|
||||
|
||||
"@prefresh/vite/@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "https://mirrors.tencent.com/npm/picomatch/-/picomatch-2.3.1.tgz", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
}
|
||||
}
|
||||
16
packages/cccdev-template-3x/package.json
Normal file
16
packages/cccdev-template-3x/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "cccdev-template-3x",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc --noEmit && vite build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preact/preset-vite": "^2.9.1",
|
||||
"@preact/signals": "^1.3.0",
|
||||
"preact": "^10.24.3",
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^5.4.11"
|
||||
}
|
||||
}
|
||||
208
packages/cccdev-template-3x/src/components/App.tsx
Normal file
208
packages/cccdev-template-3x/src/components/App.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
import { useEffect, useRef } from 'preact/hooks';
|
||||
import { useComputed, useSignal } from '@preact/signals';
|
||||
import { devtoolsOpen, profilerOpen } from '../store';
|
||||
import { TreePanel } from './TreePanel';
|
||||
import { PropPanel } from './PropPanel';
|
||||
import { ProfilerPanel } from './ProfilerPanel';
|
||||
|
||||
const MIN_WIDTH = 240;
|
||||
const MAX_WIDTH = 600;
|
||||
const STORAGE_KEY = 'cc_devtools_width';
|
||||
|
||||
function getSavedWidth(): number {
|
||||
const v = parseInt(localStorage.getItem(STORAGE_KEY) ?? '', 10);
|
||||
return isNaN(v) ? 320 : Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, v));
|
||||
}
|
||||
|
||||
function getToolbarHeight(): number {
|
||||
const toolbar = document.querySelector('.toolbar') as HTMLElement | null;
|
||||
return toolbar ? toolbar.getBoundingClientRect().height : 42;
|
||||
}
|
||||
|
||||
/** 将游戏内容区向左推开,避免面板遮挡 */
|
||||
function pushGameCanvas(width: number) {
|
||||
const content = document.getElementById('content');
|
||||
if (content) content.style.paddingRight = `${width + 16}px`; // 面板宽 + 两侧间距
|
||||
}
|
||||
|
||||
function restoreGameCanvas() {
|
||||
const content = document.getElementById('content');
|
||||
if (content) content.style.paddingRight = '';
|
||||
}
|
||||
|
||||
function ToggleButton({ toolbarH }: { toolbarH: number }) {
|
||||
const active = devtoolsOpen.value;
|
||||
return (
|
||||
<button
|
||||
class={`ccdev-toggle${active ? ' active' : ''}`}
|
||||
style={{ top: `${Math.round(toolbarH / 2)}px` }}
|
||||
onClick={() => {
|
||||
devtoolsOpen.value = !devtoolsOpen.value;
|
||||
}}
|
||||
title={active ? '关闭 DevTools' : '打开 DevTools'}
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M8 2l1.88 1.88" />
|
||||
<path d="M14.12 3.88L16 2" />
|
||||
<path d="M9 7.13v-1a3.003 3.003 0 1 1 6 0v1" />
|
||||
<path d="M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6" />
|
||||
<path d="M12 20v-9" />
|
||||
<path d="M6.53 9C4.6 8.8 3 7.1 3 5" />
|
||||
<path d="M6 13H2" />
|
||||
<path d="M3 21c0-2.1 1.7-3.9 3.8-4" />
|
||||
<path d="M20.97 5c0 2.1-1.6 3.8-3.5 4" />
|
||||
<path d="M22 13h-4" />
|
||||
<path d="M17.2 17c2.1.1 3.8 1.9 3.8 4" />
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function App() {
|
||||
const open = useComputed(() => devtoolsOpen.value);
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const handleRef = useRef<HTMLDivElement>(null);
|
||||
const toolbarH = useSignal(getToolbarHeight());
|
||||
|
||||
// 向外暴露 toggle,供 toolbar 按钮调用
|
||||
(window as any).__ccDevToolsToggle = () => {
|
||||
devtoolsOpen.value = !devtoolsOpen.value;
|
||||
};
|
||||
(window as any).__ccProfilerToggle = () => {
|
||||
profilerOpen.value = !profilerOpen.value;
|
||||
};
|
||||
|
||||
// 持续监听 toolbar 实际高度,同步到 signal
|
||||
useEffect(() => {
|
||||
let ro: ResizeObserver | null = null;
|
||||
|
||||
function attach() {
|
||||
const toolbar = document.querySelector('.toolbar') as HTMLElement | null;
|
||||
if (!toolbar) return false;
|
||||
// 用 getBoundingClientRect 拿含 border 的完整高度
|
||||
toolbarH.value = Math.round(toolbar.getBoundingClientRect().bottom);
|
||||
ro = new ResizeObserver(() => {
|
||||
toolbarH.value = Math.round(toolbar.getBoundingClientRect().bottom);
|
||||
});
|
||||
ro.observe(toolbar);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!attach()) {
|
||||
// toolbar 尚未注入(由 cocosToolBar include 动态插入),轮询等待
|
||||
const id = setInterval(() => {
|
||||
if (attach()) clearInterval(id);
|
||||
}, 100);
|
||||
return () => clearInterval(id);
|
||||
}
|
||||
|
||||
return () => ro?.disconnect();
|
||||
}, []);
|
||||
|
||||
// 面板展开/收起时同步推开画布
|
||||
useEffect(() => {
|
||||
if (open.value) {
|
||||
const w = getSavedWidth();
|
||||
document.documentElement.style.setProperty('--devtools-width', `${w}px`);
|
||||
pushGameCanvas(w);
|
||||
} else {
|
||||
restoreGameCanvas();
|
||||
}
|
||||
return () => {
|
||||
restoreGameCanvas();
|
||||
};
|
||||
}, [open.value]);
|
||||
|
||||
// 左侧拖拽调整宽度
|
||||
useEffect(() => {
|
||||
const handle = handleRef.current;
|
||||
const panel = panelRef.current;
|
||||
if (!handle || !panel) return;
|
||||
|
||||
let startX = 0;
|
||||
let startW = 0;
|
||||
|
||||
function onMouseMove(e: MouseEvent) {
|
||||
const delta = startX - e.clientX; // 向左拖 = 变宽
|
||||
const newW = Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, startW + delta));
|
||||
document.documentElement.style.setProperty('--devtools-width', `${newW}px`);
|
||||
pushGameCanvas(newW);
|
||||
}
|
||||
|
||||
function onMouseUp() {
|
||||
if (!handle) return;
|
||||
handle.classList.remove('dragging');
|
||||
const content = document.getElementById('content');
|
||||
if (content) content.style.pointerEvents = '';
|
||||
const w = parseInt(
|
||||
getComputedStyle(document.documentElement).getPropertyValue('--devtools-width'),
|
||||
10,
|
||||
);
|
||||
localStorage.setItem(STORAGE_KEY, String(w));
|
||||
document.removeEventListener('mousemove', onMouseMove);
|
||||
document.removeEventListener('mouseup', onMouseUp);
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
}
|
||||
|
||||
function onMouseDown(e: MouseEvent) {
|
||||
if (!handle || !panel) return;
|
||||
e.preventDefault();
|
||||
startX = e.clientX;
|
||||
startW = panel.getBoundingClientRect().width;
|
||||
handle.classList.add('dragging');
|
||||
const content = document.getElementById('content');
|
||||
if (content) content.style.pointerEvents = 'none';
|
||||
document.body.style.cursor = 'ew-resize';
|
||||
document.body.style.userSelect = 'none';
|
||||
document.addEventListener('mousemove', onMouseMove);
|
||||
document.addEventListener('mouseup', onMouseUp);
|
||||
}
|
||||
|
||||
handle.addEventListener('mousedown', onMouseDown);
|
||||
return () => handle.removeEventListener('mousedown', onMouseDown);
|
||||
}, [open.value]);
|
||||
|
||||
const GAP = 8; // 上下各留 8px 空隙
|
||||
const top = `${toolbarH.value + GAP}px`;
|
||||
const height = `calc(100vh - ${toolbarH.value + GAP * 2}px)`;
|
||||
|
||||
if (!open.value)
|
||||
return (
|
||||
<>
|
||||
<ToggleButton toolbarH={toolbarH.value} />
|
||||
<ProfilerPanel />
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToggleButton toolbarH={toolbarH.value} />
|
||||
<div id="cc-devtools" ref={panelRef} style={{ top, height }}>
|
||||
<div id="cc-devtools-resize" ref={handleRef} title="拖拽调整面板宽度" />
|
||||
<a
|
||||
class="ccdev-github"
|
||||
href="https://github.com/potato47/ccc-devtools"
|
||||
target="_blank"
|
||||
title="GitHub"
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z" />
|
||||
</svg>
|
||||
</a>
|
||||
<TreePanel />
|
||||
<PropPanel />
|
||||
</div>
|
||||
<ProfilerPanel />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { useSignal } from '@preact/signals';
|
||||
import { PropItem } from './PropItem';
|
||||
import { getComponentViewModel } from '../models/ComponentModels';
|
||||
import { outputToConsole } from '../engine';
|
||||
|
||||
interface ComponentPanelProps {
|
||||
name: string;
|
||||
component: any;
|
||||
updateKey: number;
|
||||
}
|
||||
|
||||
export function ComponentPanel({ name, component, updateKey: _updateKey }: ComponentPanelProps) {
|
||||
const collapsed = useSignal(false);
|
||||
const model = getComponentViewModel(name, () => component);
|
||||
|
||||
return (
|
||||
<div class="comp-panel">
|
||||
<div
|
||||
class="comp-header"
|
||||
onClick={() => {
|
||||
collapsed.value = !collapsed.value;
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="comp-enabled"
|
||||
checked={component?.enabled}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onChange={(e) => {
|
||||
if (component) component.enabled = (e.target as HTMLInputElement).checked;
|
||||
}}
|
||||
/>
|
||||
<span class="comp-name">{name}</span>
|
||||
<span class="comp-arrow">{collapsed.value ? '›' : '⌄'}</span>
|
||||
<button
|
||||
class="icon-btn"
|
||||
title="输出到控制台"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
outputToConsole(component);
|
||||
}}
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
{!collapsed.value && model && (
|
||||
<div class="comp-props">
|
||||
{model.props.map((prop) => (
|
||||
<PropItem key={prop.key} model={model} propName={prop.name} propKey={prop.key} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{!collapsed.value && !model && <div class="comp-empty">(无可编辑属性)</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
120
packages/cccdev-template-3x/src/components/ProfilerPanel.tsx
Normal file
120
packages/cccdev-template-3x/src/components/ProfilerPanel.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import { useEffect, useRef } from 'preact/hooks';
|
||||
import { useSignal } from '@preact/signals';
|
||||
import { profilerOpen } from '../store';
|
||||
import { cc } from '../engine';
|
||||
|
||||
interface StatItem {
|
||||
key: string;
|
||||
desc: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const STAT_KEYS = [
|
||||
'fps',
|
||||
'draws',
|
||||
'frame',
|
||||
'instances',
|
||||
'tricount',
|
||||
'logic',
|
||||
'physics',
|
||||
'render',
|
||||
'textureMemory',
|
||||
'bufferMemory',
|
||||
];
|
||||
|
||||
export function ProfilerPanel() {
|
||||
const items = useSignal<StatItem[]>(STAT_KEYS.map((key) => ({ key, desc: key, value: '—' })));
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const posX = useSignal(window.innerWidth - 260);
|
||||
const posY = useSignal(60);
|
||||
|
||||
// 轮询 cc.profiler.stats
|
||||
useEffect(() => {
|
||||
if (!profilerOpen.value) return;
|
||||
|
||||
function refresh() {
|
||||
const c = cc();
|
||||
if (!c?.profiler?.stats) return;
|
||||
const stats = c.profiler.stats;
|
||||
items.value = STAT_KEYS.map((key) => {
|
||||
const data = stats[key];
|
||||
if (!data) return { key, desc: key, value: '—' };
|
||||
const val = data.isInteger
|
||||
? String(data.counter._value | 0)
|
||||
: data.counter._value.toFixed(2);
|
||||
return { key, desc: data.desc ?? key, value: val };
|
||||
});
|
||||
}
|
||||
|
||||
refresh();
|
||||
const id = setInterval(refresh, 1000);
|
||||
return () => clearInterval(id);
|
||||
}, [profilerOpen.value]);
|
||||
|
||||
// 原生拖拽
|
||||
useEffect(() => {
|
||||
const header = panelRef.current?.querySelector('.profiler-drag') as HTMLElement;
|
||||
if (!header) return;
|
||||
|
||||
let startX = 0,
|
||||
startY = 0,
|
||||
startPX = 0,
|
||||
startPY = 0;
|
||||
let dragging = false;
|
||||
|
||||
function onMouseMove(e: MouseEvent) {
|
||||
if (!dragging) return;
|
||||
posX.value = startPX + (e.clientX - startX);
|
||||
posY.value = startPY + (e.clientY - startY);
|
||||
}
|
||||
|
||||
function onMouseUp() {
|
||||
dragging = false;
|
||||
document.removeEventListener('mousemove', onMouseMove);
|
||||
document.removeEventListener('mouseup', onMouseUp);
|
||||
}
|
||||
|
||||
function onMouseDown(e: MouseEvent) {
|
||||
dragging = true;
|
||||
startX = e.clientX;
|
||||
startY = e.clientY;
|
||||
startPX = posX.value;
|
||||
startPY = posY.value;
|
||||
document.addEventListener('mousemove', onMouseMove);
|
||||
document.addEventListener('mouseup', onMouseUp);
|
||||
}
|
||||
|
||||
header.addEventListener('mousedown', onMouseDown);
|
||||
return () => header.removeEventListener('mousedown', onMouseDown);
|
||||
}, [profilerOpen.value]);
|
||||
|
||||
if (!profilerOpen.value) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={panelRef}
|
||||
class="profiler-float"
|
||||
style={{ left: `${posX.value}px`, top: `${posY.value}px` }}
|
||||
>
|
||||
<div class="profiler-drag">
|
||||
<span>Profiler</span>
|
||||
<button
|
||||
class="icon-btn"
|
||||
onClick={() => {
|
||||
profilerOpen.value = false;
|
||||
}}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div class="profiler-body">
|
||||
{items.value.map((item) => (
|
||||
<div class="profiler-row" key={item.key}>
|
||||
<span class="profiler-desc">{item.desc}</span>
|
||||
<span class="profiler-val">{item.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
120
packages/cccdev-template-3x/src/components/PropItem.tsx
Normal file
120
packages/cccdev-template-3x/src/components/PropItem.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import { useRef, useEffect } from 'preact/hooks';
|
||||
import { cc } from '../engine';
|
||||
|
||||
interface PropItemProps {
|
||||
model: any;
|
||||
propName: string;
|
||||
propKey: string;
|
||||
}
|
||||
|
||||
function getPropType(value: any): string {
|
||||
if (value === null || value === undefined) return 'unknown';
|
||||
if (typeof value === 'object' && value.__classname__) return value.__classname__;
|
||||
return typeof value;
|
||||
}
|
||||
|
||||
function colorToHex(color: any): string {
|
||||
const hex = color.toHEX() as string;
|
||||
return `#${hex}`;
|
||||
}
|
||||
|
||||
function hexToColor(hex: string): any {
|
||||
return new (cc().Color)().fromHEX(hex);
|
||||
}
|
||||
|
||||
function formatNum(v: number): string {
|
||||
return Number.isInteger(v) ? String(v) : parseFloat(v.toFixed(3)).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字输入框:非受控 + onInput 实时写入。
|
||||
* 用 ref 在外部 tick 变化时手动同步显示值(仅在未聚焦时同步,避免打断输入)。
|
||||
*/
|
||||
function NumberInput({ model, propKey }: { model: any; propKey: string }) {
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
|
||||
// 同步外部值到输入框(未聚焦时才更新,避免覆盖正在输入的内容)
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el || document.activeElement === el) return;
|
||||
const external = model[propKey];
|
||||
if (parseFloat(el.value) !== external) {
|
||||
el.value = formatNum(external);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
type="number"
|
||||
class="prop-input"
|
||||
defaultValue={formatNum(model[propKey])}
|
||||
step="0.1"
|
||||
onInput={(e) => {
|
||||
const v = parseFloat((e.target as HTMLInputElement).value);
|
||||
if (!isNaN(v)) model[propKey] = v;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function StringInput({ model, propKey }: { model: any; propKey: string }) {
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el || document.activeElement === el) return;
|
||||
const external = String(model[propKey] ?? '');
|
||||
if (el.value !== external) el.value = external;
|
||||
});
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
type="text"
|
||||
class="prop-input"
|
||||
defaultValue={model[propKey]}
|
||||
onInput={(e) => {
|
||||
model[propKey] = (e.target as HTMLInputElement).value;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function PropItem({ model, propName, propKey }: PropItemProps) {
|
||||
const value = model[propKey];
|
||||
const type = getPropType(value);
|
||||
|
||||
return (
|
||||
<div class="prop-row">
|
||||
<span class="prop-name">{propName}</span>
|
||||
<div class="prop-value">
|
||||
{type === 'number' && <NumberInput model={model} propKey={propKey} />}
|
||||
{type === 'string' && <StringInput model={model} propKey={propKey} />}
|
||||
{type === 'boolean' && (
|
||||
<input
|
||||
type="checkbox"
|
||||
class="prop-checkbox"
|
||||
checked={value}
|
||||
onChange={(e) => {
|
||||
model[propKey] = (e.target as HTMLInputElement).checked;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{type === 'cc.Color' && (
|
||||
<input
|
||||
type="color"
|
||||
class="prop-color"
|
||||
value={colorToHex(value)}
|
||||
onChange={(e) => {
|
||||
model[propKey] = hexToColor((e.target as HTMLInputElement).value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!['number', 'string', 'boolean', 'cc.Color'].includes(type) && (
|
||||
<span class="prop-unknown">{String(value)}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
64
packages/cccdev-template-3x/src/components/PropPanel.tsx
Normal file
64
packages/cccdev-template-3x/src/components/PropPanel.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useComputed } from '@preact/signals';
|
||||
import { selectedNode, updateTick } from '../store';
|
||||
import { isValid, getComponents, outputToConsole, drawNodeRect } from '../engine';
|
||||
import { NodeModel } from '../models/NodeModel';
|
||||
import { PropItem } from './PropItem';
|
||||
import { ComponentPanel } from './ComponentPanel';
|
||||
|
||||
export function PropPanel() {
|
||||
const node = useComputed(() => selectedNode.value);
|
||||
const tick = useComputed(() => updateTick.value);
|
||||
|
||||
if (!node.value || !isValid(node.value)) {
|
||||
return (
|
||||
<div class="prop-panel">
|
||||
<div class="panel-header">属性</div>
|
||||
<div class="prop-empty">未选中节点</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const ccNode = node.value;
|
||||
const components = getComponents(ccNode);
|
||||
|
||||
return (
|
||||
<div class="prop-panel">
|
||||
<div class="panel-header">属性</div>
|
||||
<div class="prop-scroll">
|
||||
{/* 节点基础属性 */}
|
||||
<div class="comp-panel">
|
||||
<div class="comp-header">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="comp-enabled"
|
||||
checked={ccNode.active}
|
||||
onChange={(e) => {
|
||||
ccNode.active = (e.target as HTMLInputElement).checked;
|
||||
}}
|
||||
/>
|
||||
<span class="comp-name">Node</span>
|
||||
<div style="flex:1" />
|
||||
<button class="icon-btn" title="高亮节点" onClick={() => drawNodeRect(ccNode)}>
|
||||
⊡
|
||||
</button>
|
||||
<button class="icon-btn" title="输出到控制台" onClick={() => outputToConsole(ccNode)}>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<div class="comp-props">
|
||||
{NodeModel.props.map((prop) => (
|
||||
<PropItem key={prop.key} model={NodeModel} propName={prop.name} propKey={prop.key} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider" />
|
||||
|
||||
{/* 组件列表 */}
|
||||
{components.map(({ name, target }) => (
|
||||
<ComponentPanel key={name} name={name} component={target} updateKey={tick.value} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
275
packages/cccdev-template-3x/src/components/TreePanel.tsx
Normal file
275
packages/cccdev-template-3x/src/components/TreePanel.tsx
Normal file
@@ -0,0 +1,275 @@
|
||||
import { useEffect, useCallback, useRef } from 'preact/hooks';
|
||||
import { useSignal, useComputed } from '@preact/signals';
|
||||
import {
|
||||
treeData,
|
||||
expandedUuids,
|
||||
selectedNode,
|
||||
searchQuery,
|
||||
updateTick,
|
||||
type TreeNode,
|
||||
} from '../store';
|
||||
import { isReady, getScene, resolveNodeByPath } from '../engine';
|
||||
|
||||
// ── 构建树数据 ─────────────────────────────────────────────
|
||||
function buildTree(children: any[], path: string[]): TreeNode[] {
|
||||
const result: TreeNode[] = [];
|
||||
for (const ccNode of children) {
|
||||
const childPath = [...path, ccNode.uuid];
|
||||
const node: TreeNode = {
|
||||
uuid: ccNode.uuid,
|
||||
name: ccNode.name,
|
||||
active: ccNode.activeInHierarchy,
|
||||
children: ccNode.children?.length > 0 ? buildTree(ccNode.children, childPath) : [],
|
||||
path: childPath,
|
||||
};
|
||||
result.push(node);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── 搜索:收集所有匹配节点(扁平列表)─────────────────────
|
||||
interface FlatMatch {
|
||||
node: TreeNode;
|
||||
depth: number;
|
||||
}
|
||||
|
||||
function collectMatches(nodes: TreeNode[], query: string, depth = 0): FlatMatch[] {
|
||||
const q = query.toLowerCase();
|
||||
const result: FlatMatch[] = [];
|
||||
for (const node of nodes) {
|
||||
if (node.name.toLowerCase().includes(q)) {
|
||||
result.push({ node, depth });
|
||||
}
|
||||
result.push(...collectMatches(node.children, query, depth + 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── 搜索时自动展开匹配节点的所有祖先 ─────────────────────
|
||||
function expandAncestors(nodes: TreeNode[], query: string): Set<string> {
|
||||
const q = query.toLowerCase();
|
||||
const toExpand = new Set<string>();
|
||||
|
||||
function walk(node: TreeNode, ancestors: string[]): boolean {
|
||||
const matched = node.name.toLowerCase().includes(q);
|
||||
let childMatched = false;
|
||||
for (const child of node.children) {
|
||||
if (walk(child, [...ancestors, node.uuid])) childMatched = true;
|
||||
}
|
||||
if (matched || childMatched) {
|
||||
for (const uuid of ancestors) toExpand.add(uuid);
|
||||
}
|
||||
return matched || childMatched;
|
||||
}
|
||||
|
||||
for (const node of nodes) walk(node, []);
|
||||
return toExpand;
|
||||
}
|
||||
|
||||
// ── 高亮关键词 ────────────────────────────────────────────
|
||||
function HighlightText({ text, query }: { text: string; query: string }) {
|
||||
if (!query) return <span class="tree-label">{text}</span>;
|
||||
const idx = text.toLowerCase().indexOf(query.toLowerCase());
|
||||
if (idx === -1) return <span class="tree-label">{text}</span>;
|
||||
return (
|
||||
<span class="tree-label">
|
||||
{text.slice(0, idx)}
|
||||
<mark class="tree-highlight">{text.slice(idx, idx + query.length)}</mark>
|
||||
{text.slice(idx + query.length)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// ── 搜索结果行 ────────────────────────────────────────────
|
||||
function SearchResultItem({ node, query }: { node: TreeNode; query: string }) {
|
||||
const isSelected = useComputed(() => selectedNode.value?.uuid === node.uuid);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
const ccNode = resolveNodeByPath(node.path);
|
||||
selectedNode.value = ccNode ?? null;
|
||||
}, [node.path]);
|
||||
|
||||
return (
|
||||
<div
|
||||
class={`tree-row${isSelected.value ? ' selected' : ''}${!node.active ? ' inactive' : ''}`}
|
||||
style={{ paddingLeft: '8px' }}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<HighlightText text={node.name} query={query} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── 单个树节点组件 ─────────────────────────────────────────
|
||||
interface TreeNodeItemProps {
|
||||
node: TreeNode;
|
||||
depth: number;
|
||||
}
|
||||
|
||||
function TreeNodeItem({ node, depth }: TreeNodeItemProps) {
|
||||
const expanded = useComputed(() => expandedUuids.value.has(node.uuid));
|
||||
const isSelected = useComputed(() => selectedNode.value?.uuid === node.uuid);
|
||||
const hasChildren = node.children.length > 0;
|
||||
|
||||
const handleClick = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const ccNode = resolveNodeByPath(node.path);
|
||||
selectedNode.value = ccNode ?? null;
|
||||
},
|
||||
[node.path],
|
||||
);
|
||||
|
||||
const handleToggle = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const next = new Set(expandedUuids.value);
|
||||
if (next.has(node.uuid)) {
|
||||
next.delete(node.uuid);
|
||||
} else {
|
||||
next.add(node.uuid);
|
||||
}
|
||||
expandedUuids.value = next;
|
||||
},
|
||||
[node.uuid],
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="tree-node">
|
||||
<div
|
||||
class={`tree-row${isSelected.value ? ' selected' : ''}${!node.active ? ' inactive' : ''}`}
|
||||
style={{ paddingLeft: `${depth * 14 + 6}px` }}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<span
|
||||
class={`tree-arrow${hasChildren ? '' : ' invisible'}${expanded.value ? ' expanded' : ''}`}
|
||||
onClick={handleToggle}
|
||||
>
|
||||
›
|
||||
</span>
|
||||
<span class="tree-label">{node.name}</span>
|
||||
</div>
|
||||
{hasChildren && expanded.value && (
|
||||
<div class="tree-children">
|
||||
{node.children.map((child) => (
|
||||
<TreeNodeItem key={child.uuid} node={child} depth={depth + 1} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── TreePanel 主组件 ───────────────────────────────────────
|
||||
export function TreePanel() {
|
||||
const initialized = useSignal(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const query = useComputed(() => searchQuery.value.trim());
|
||||
|
||||
// 搜索结果(扁平列表)
|
||||
const searchResults = useComputed(() => {
|
||||
if (!query.value) return null;
|
||||
return collectMatches(treeData.value, query.value);
|
||||
});
|
||||
|
||||
// 搜索关键词变化时,自动展开匹配节点的祖先
|
||||
useEffect(() => {
|
||||
if (!query.value) return;
|
||||
const ancestors = expandAncestors(treeData.value, query.value);
|
||||
if (ancestors.size === 0) return;
|
||||
expandedUuids.value = new Set([...expandedUuids.value, ...ancestors]);
|
||||
}, [query.value]);
|
||||
|
||||
useEffect(() => {
|
||||
let rafId: number;
|
||||
let started = false;
|
||||
|
||||
function refreshTree() {
|
||||
if (isReady()) {
|
||||
if (!started) {
|
||||
started = true;
|
||||
initialized.value = true;
|
||||
}
|
||||
treeData.value = buildTree(getScene().children, []);
|
||||
updateTick.value = -updateTick.value;
|
||||
}
|
||||
rafId = requestAnimationFrame(refreshTree);
|
||||
}
|
||||
|
||||
const pollId = setInterval(() => {
|
||||
if (isReady()) {
|
||||
clearInterval(pollId);
|
||||
rafId = requestAnimationFrame(refreshTree);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
clearInterval(pollId);
|
||||
cancelAnimationFrame(rafId);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleSearchInput = useCallback((e: Event) => {
|
||||
searchQuery.value = (e.target as HTMLInputElement).value;
|
||||
}, []);
|
||||
|
||||
const handleClear = useCallback(() => {
|
||||
searchQuery.value = '';
|
||||
inputRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
// Esc 清空搜索
|
||||
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
searchQuery.value = '';
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div class="tree-panel">
|
||||
<div class="panel-header">节点树</div>
|
||||
|
||||
{/* 搜索栏 */}
|
||||
<div class="tree-search-bar">
|
||||
<span class="tree-search-icon">⌕</span>
|
||||
<input
|
||||
ref={inputRef}
|
||||
class="tree-search-input"
|
||||
type="text"
|
||||
placeholder="搜索节点…"
|
||||
value={searchQuery.value}
|
||||
onInput={handleSearchInput}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
{query.value && (
|
||||
<button class="tree-search-clear" onClick={handleClear} title="清空">
|
||||
✕
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div class="tree-scroll">
|
||||
{!initialized.value ? (
|
||||
<div class="tree-empty">等待引擎初始化…</div>
|
||||
) : searchResults.value !== null ? (
|
||||
// 搜索模式:扁平结果列表
|
||||
searchResults.value.length === 0 ? (
|
||||
<div class="tree-empty">未找到匹配节点</div>
|
||||
) : (
|
||||
<>
|
||||
<div class="tree-search-count">{searchResults.value.length} 个结果</div>
|
||||
{searchResults.value.map(({ node }) => (
|
||||
<SearchResultItem key={node.uuid} node={node} query={query.value} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
) : treeData.value.length === 0 ? (
|
||||
<div class="tree-empty">场景为空</div>
|
||||
) : (
|
||||
// 正常树模式
|
||||
treeData.value.map((node) => <TreeNodeItem key={node.uuid} node={node} depth={0} />)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
108
packages/cccdev-template-3x/src/engine.ts
Normal file
108
packages/cccdev-template-3x/src/engine.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/** 获取 Cocos 引擎全局对象 */
|
||||
export const cc = (): any => (window as any)['cc'];
|
||||
|
||||
export const getScene = (): any => cc()?.director?.getScene();
|
||||
|
||||
export const isReady = (): boolean => !!cc() && !!getScene();
|
||||
|
||||
export const isValid = (node: any): boolean => !!node && cc()?.isValid(node);
|
||||
|
||||
export const getComponents = (ccNode: any): Array<{ name: string; target: any }> =>
|
||||
(ccNode?.components ?? []).map((c: any) => ({ name: c.__classname__ as string, target: c }));
|
||||
|
||||
export const getSceneChildren = (): any[] => getScene()?.children ?? [];
|
||||
|
||||
export const getChildByUuid = (node: any, uuid: string): any => node?.getChildByUuid(uuid) ?? null;
|
||||
|
||||
/** 沿 uuid 路径从场景根查找节点 */
|
||||
export const resolveNodeByPath = (path: string[]): any => {
|
||||
let node: any = getScene();
|
||||
for (const uuid of path) {
|
||||
node = getChildByUuid(node, uuid);
|
||||
if (!node) return null;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
/** 将节点/组件输出到 window.temp1、temp2... 方便控制台操作 */
|
||||
export const outputToConsole = (target: any): void => {
|
||||
let i = 1;
|
||||
while ((window as any)['temp' + i] !== undefined) i++;
|
||||
(window as any)['temp' + i] = target;
|
||||
console.log('temp' + i, target);
|
||||
};
|
||||
|
||||
/** 在场景中高亮绘制节点包围盒,2s 后自动销毁 */
|
||||
export const drawNodeRect = (target: any): void => {
|
||||
const c = cc();
|
||||
if (!c) return;
|
||||
let rect: any;
|
||||
const transform = target.getComponent(c.UITransformComponent);
|
||||
if (transform) {
|
||||
rect = getSelfBoundingBoxToWorld(transform, c);
|
||||
} else {
|
||||
const worldPos = c.v3();
|
||||
target.getWorldPosition(worldPos);
|
||||
rect = c.rect(worldPos.x, worldPos.y, 0, 0);
|
||||
}
|
||||
const canvasNode = new c.Node('__DevTools_Highlight__');
|
||||
const scene = getScene();
|
||||
scene.addChild(canvasNode);
|
||||
canvasNode.addComponent(c.Canvas);
|
||||
const bgNode = new c.Node();
|
||||
const graphics = bgNode.addComponent(c.GraphicsComponent);
|
||||
const bgTransform = bgNode.addComponent(c.UITransformComponent);
|
||||
canvasNode.addChild(bgNode);
|
||||
const centerPos = c.v3(rect.center.x, rect.center.y, 0);
|
||||
const localPos = c.v3();
|
||||
canvasNode.getComponent(c.UITransformComponent).convertToNodeSpaceAR(centerPos, localPos);
|
||||
bgNode.setPosition(localPos);
|
||||
bgNode.layer = target.layer;
|
||||
const isZeroSize = rect.width === 0 || rect.height === 0;
|
||||
if (isZeroSize) {
|
||||
graphics.circle(0, 0, 100);
|
||||
graphics.fillColor = c.Color.GREEN;
|
||||
graphics.fill();
|
||||
} else {
|
||||
bgTransform.width = rect.width;
|
||||
bgTransform.height = rect.height;
|
||||
graphics.rect(
|
||||
-bgTransform.width / 2,
|
||||
-bgTransform.height / 2,
|
||||
bgTransform.width,
|
||||
bgTransform.height,
|
||||
);
|
||||
graphics.fillColor = new c.Color().fromHEX('#E91E6390');
|
||||
graphics.fill();
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (c.isValid(canvasNode)) canvasNode.destroy();
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
function getSelfBoundingBoxToWorld(transform: any, c: any): any {
|
||||
const _worldMatrix = c.mat4();
|
||||
if (transform.node.parent) {
|
||||
transform.node.parent.getWorldMatrix(_worldMatrix);
|
||||
const parentMat = _worldMatrix;
|
||||
const _matrix = c.mat4();
|
||||
c.Mat4.fromRTS(
|
||||
_matrix,
|
||||
transform.node.getRotation(),
|
||||
transform.node.getPosition(),
|
||||
transform.node.getScale(),
|
||||
);
|
||||
const width = transform._contentSize.width;
|
||||
const height = transform._contentSize.height;
|
||||
const rect = c.rect(
|
||||
-transform._anchorPoint.x * width,
|
||||
-transform._anchorPoint.y * height,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
c.Mat4.multiply(_worldMatrix, parentMat, _matrix);
|
||||
rect.transformMat4(_worldMatrix);
|
||||
return rect;
|
||||
}
|
||||
return transform.getBoundingBox();
|
||||
}
|
||||
17
packages/cccdev-template-3x/src/main.tsx
Normal file
17
packages/cccdev-template-3x/src/main.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { render } from 'preact';
|
||||
import { App } from './components/App';
|
||||
import './style.css';
|
||||
|
||||
// 等待 DOM 就绪后挂载
|
||||
function mount() {
|
||||
const container = document.getElementById('cc-devtools-root');
|
||||
if (container) {
|
||||
render(<App />, container);
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', mount);
|
||||
} else {
|
||||
mount();
|
||||
}
|
||||
119
packages/cccdev-template-3x/src/models/ComponentModels.ts
Normal file
119
packages/cccdev-template-3x/src/models/ComponentModels.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { PropDef } from './NodeModel';
|
||||
|
||||
export interface ComponentViewModel {
|
||||
props: PropDef[];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export function getComponentViewModel(
|
||||
name: string,
|
||||
componentGetter: () => any,
|
||||
): ComponentViewModel | null {
|
||||
switch (name) {
|
||||
case 'cc.UITransform':
|
||||
return new CCUITransformModel(componentGetter);
|
||||
case 'cc.Label':
|
||||
return new CCLabelModel(componentGetter);
|
||||
case 'cc.Sprite':
|
||||
return new CCSpriteModel(componentGetter);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class CCUITransformModel implements ComponentViewModel {
|
||||
props: PropDef[] = [
|
||||
{ name: 'Width', key: 'width' },
|
||||
{ name: 'Height', key: 'height' },
|
||||
{ name: 'Anchor X', key: 'anchorX' },
|
||||
{ name: 'Anchor Y', key: 'anchorY' },
|
||||
];
|
||||
|
||||
constructor(private getter: () => any) {}
|
||||
|
||||
get width(): number {
|
||||
return this.getter()?.contentSize.width ?? 0;
|
||||
}
|
||||
set width(v: number) {
|
||||
const c = this.getter();
|
||||
if (!c) return;
|
||||
c.setContentSize(v, c.contentSize.height);
|
||||
}
|
||||
|
||||
get height(): number {
|
||||
return this.getter()?.contentSize.height ?? 0;
|
||||
}
|
||||
set height(v: number) {
|
||||
const c = this.getter();
|
||||
if (!c) return;
|
||||
c.setContentSize(c.contentSize.width, v);
|
||||
}
|
||||
|
||||
get anchorX(): number {
|
||||
return this.getter()?.anchorPoint.x ?? 0;
|
||||
}
|
||||
set anchorX(v: number) {
|
||||
const c = this.getter();
|
||||
if (!c) return;
|
||||
c.setAnchorPoint(v, c.anchorPoint.y);
|
||||
}
|
||||
|
||||
get anchorY(): number {
|
||||
return this.getter()?.anchorPoint.y ?? 0;
|
||||
}
|
||||
set anchorY(v: number) {
|
||||
const c = this.getter();
|
||||
if (!c) return;
|
||||
c.setAnchorPoint(c.anchorPoint.x, v);
|
||||
}
|
||||
}
|
||||
|
||||
class CCLabelModel implements ComponentViewModel {
|
||||
props: PropDef[] = [
|
||||
{ name: 'String', key: 'string' },
|
||||
{ name: 'Color', key: 'color' },
|
||||
{ name: 'Font Size', key: 'fontSize' },
|
||||
{ name: 'Line Height', key: 'lineHeight' },
|
||||
];
|
||||
constructor(private getter: () => any) {}
|
||||
get string(): string {
|
||||
return this.getter()?.string ?? '';
|
||||
}
|
||||
set string(v: string) {
|
||||
const c = this.getter();
|
||||
if (c) c.string = v;
|
||||
}
|
||||
get color(): any {
|
||||
return this.getter()?.color;
|
||||
}
|
||||
set color(v: any) {
|
||||
const c = this.getter();
|
||||
if (c) c.color = v;
|
||||
}
|
||||
get fontSize(): number {
|
||||
return this.getter()?.fontSize ?? 0;
|
||||
}
|
||||
set fontSize(v: number) {
|
||||
const c = this.getter();
|
||||
if (c) c.fontSize = v;
|
||||
}
|
||||
get lineHeight(): number {
|
||||
return this.getter()?.lineHeight ?? 0;
|
||||
}
|
||||
set lineHeight(v: number) {
|
||||
const c = this.getter();
|
||||
if (c) c.lineHeight = v;
|
||||
}
|
||||
}
|
||||
|
||||
class CCSpriteModel implements ComponentViewModel {
|
||||
props: PropDef[] = [{ name: 'Color', key: 'color' }];
|
||||
constructor(private getter: () => any) {}
|
||||
get color(): any {
|
||||
return this.getter()?.color;
|
||||
}
|
||||
set color(v: any) {
|
||||
const c = this.getter();
|
||||
if (c) c.color = v;
|
||||
}
|
||||
}
|
||||
83
packages/cccdev-template-3x/src/models/NodeModel.ts
Normal file
83
packages/cccdev-template-3x/src/models/NodeModel.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { selectedNode } from '../store';
|
||||
|
||||
export interface PropDef {
|
||||
name: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export class NodeModel {
|
||||
static readonly props: PropDef[] = [
|
||||
{ name: 'Name', key: 'nodeName' },
|
||||
{ name: 'X', key: 'x' },
|
||||
{ name: 'Y', key: 'y' },
|
||||
{ name: 'Z', key: 'z' },
|
||||
{ name: 'Scale X', key: 'scaleX' },
|
||||
{ name: 'Scale Y', key: 'scaleY' },
|
||||
{ name: 'Scale Z', key: 'scaleZ' },
|
||||
];
|
||||
|
||||
private static get node(): any {
|
||||
return selectedNode.value;
|
||||
}
|
||||
|
||||
static get nodeName(): string {
|
||||
return this.node?.name ?? '';
|
||||
}
|
||||
static set nodeName(v: string) {
|
||||
if (this.node) this.node.name = v;
|
||||
}
|
||||
|
||||
static get x(): number {
|
||||
return this.node?.getPosition().x ?? 0;
|
||||
}
|
||||
static set x(v: number) {
|
||||
if (!this.node) return;
|
||||
const p = this.node.getPosition();
|
||||
this.node.setPosition(v, p.y, p.z);
|
||||
}
|
||||
|
||||
static get y(): number {
|
||||
return this.node?.getPosition().y ?? 0;
|
||||
}
|
||||
static set y(v: number) {
|
||||
if (!this.node) return;
|
||||
const p = this.node.getPosition();
|
||||
this.node.setPosition(p.x, v, p.z);
|
||||
}
|
||||
|
||||
static get z(): number {
|
||||
return this.node?.getPosition().z ?? 0;
|
||||
}
|
||||
static set z(v: number) {
|
||||
if (!this.node) return;
|
||||
const p = this.node.getPosition();
|
||||
this.node.setPosition(p.x, p.y, v);
|
||||
}
|
||||
|
||||
static get scaleX(): number {
|
||||
return this.node?.getScale().x ?? 1;
|
||||
}
|
||||
static set scaleX(v: number) {
|
||||
if (!this.node) return;
|
||||
const s = this.node.getScale();
|
||||
this.node.setScale(v, s.y, s.z);
|
||||
}
|
||||
|
||||
static get scaleY(): number {
|
||||
return this.node?.getScale().y ?? 1;
|
||||
}
|
||||
static set scaleY(v: number) {
|
||||
if (!this.node) return;
|
||||
const s = this.node.getScale();
|
||||
this.node.setScale(s.x, v, s.z);
|
||||
}
|
||||
|
||||
static get scaleZ(): number {
|
||||
return this.node?.getScale().z ?? 1;
|
||||
}
|
||||
static set scaleZ(v: number) {
|
||||
if (!this.node) return;
|
||||
const s = this.node.getScale();
|
||||
this.node.setScale(s.x, s.y, v);
|
||||
}
|
||||
}
|
||||
40
packages/cccdev-template-3x/src/store.ts
Normal file
40
packages/cccdev-template-3x/src/store.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { signal, computed } from '@preact/signals';
|
||||
|
||||
/** 当前选中的 cc.Node */
|
||||
export const selectedNode = signal<any>(null);
|
||||
|
||||
/** 每帧 toggle(1 / -1),驱动属性面板重渲 */
|
||||
export const updateTick = signal<number>(1);
|
||||
|
||||
/** DevTools 面板是否展开 */
|
||||
export const devtoolsOpen = signal<boolean>(!!localStorage.getItem('cc_devtools_show'));
|
||||
|
||||
/** Profiler 浮窗是否展开 */
|
||||
export const profilerOpen = signal<boolean>(false);
|
||||
|
||||
/** 节点树数据(每帧重建) */
|
||||
export interface TreeNode {
|
||||
uuid: string;
|
||||
name: string;
|
||||
active: boolean;
|
||||
children: TreeNode[];
|
||||
path: string[];
|
||||
}
|
||||
export const treeData = signal<TreeNode[]>([]);
|
||||
|
||||
/** 已展开节点的 uuid 集合 */
|
||||
export const expandedUuids = signal<Set<string>>(new Set());
|
||||
|
||||
/** 是否有节点被选中且有效 */
|
||||
export const hasSelection = computed(() => selectedNode.value !== null);
|
||||
|
||||
/** 节点搜索关键词 */
|
||||
export const searchQuery = signal<string>('');
|
||||
|
||||
devtoolsOpen.subscribe((val) => {
|
||||
if (val) {
|
||||
localStorage.setItem('cc_devtools_show', '1');
|
||||
} else {
|
||||
localStorage.removeItem('cc_devtools_show');
|
||||
}
|
||||
});
|
||||
519
packages/cccdev-template-3x/src/style.css
Normal file
519
packages/cccdev-template-3x/src/style.css
Normal file
@@ -0,0 +1,519 @@
|
||||
/* ── CSS 变量(与 preview-template 主题保持一致) ── */
|
||||
:root {
|
||||
--bg-base: #16161e;
|
||||
--bg-panel: #1c1c28;
|
||||
--bg-control: #252535;
|
||||
--bg-hover: #2e2e48;
|
||||
--bg-selected: rgba(108, 99, 255, 0.22);
|
||||
--border: rgba(255, 255, 255, 0.08);
|
||||
--border-focus: rgba(108, 99, 255, 0.6);
|
||||
--accent: #6c63ff;
|
||||
--accent-light: #a89dff;
|
||||
--text-primary: #d0d0f0;
|
||||
--text-muted: #7878a0;
|
||||
--text-dim: rgba(208, 208, 240, 0.35);
|
||||
--radius: 5px;
|
||||
--font: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
/* ── Toolbar 开关按钮 ── */
|
||||
.ccdev-toggle {
|
||||
position: fixed;
|
||||
right: 12px;
|
||||
transform: translateY(-50%);
|
||||
z-index: 9999;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-control);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 0.15s,
|
||||
border-color 0.15s,
|
||||
color 0.15s;
|
||||
}
|
||||
.ccdev-toggle:hover {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--border-focus);
|
||||
color: var(--accent-light);
|
||||
}
|
||||
.ccdev-toggle.active {
|
||||
background: rgba(108, 99, 255, 0.18);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-light);
|
||||
}
|
||||
|
||||
/* ── 浮层主容器 ── */
|
||||
#cc-devtools {
|
||||
position: fixed;
|
||||
/* top / height 由 App.tsx 内联 style 动态注入,跟随 toolbar 实际高度 */
|
||||
top: 42px;
|
||||
right: 8px;
|
||||
width: var(--devtools-width, 320px);
|
||||
height: calc(100vh - 42px);
|
||||
background: var(--bg-base);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 8888;
|
||||
font-family: var(--font);
|
||||
font-size: 12px;
|
||||
color: var(--text-primary);
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
transition: width 0s;
|
||||
}
|
||||
|
||||
/* ── GitHub 链接 ── */
|
||||
.ccdev-github {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
z-index: 2;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-muted);
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
color 0.15s,
|
||||
background 0.15s;
|
||||
}
|
||||
.ccdev-github:hover {
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
/* ── 拖拽调整宽度手柄 ── */
|
||||
#cc-devtools-resize {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
cursor: ew-resize;
|
||||
z-index: 1;
|
||||
background: transparent;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
#cc-devtools-resize:hover,
|
||||
#cc-devtools-resize.dragging {
|
||||
background: rgba(108, 99, 255, 0.4);
|
||||
}
|
||||
|
||||
#cc-devtools * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ── Panel 标题 ── */
|
||||
.panel-header {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 10px;
|
||||
background: var(--bg-panel);
|
||||
border-bottom: 1px solid var(--border);
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.8px;
|
||||
text-transform: uppercase;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ── 节点树面板 ── */
|
||||
.tree-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
border-bottom: 2px solid var(--border);
|
||||
}
|
||||
|
||||
/* ── 搜索栏 ── */
|
||||
.tree-search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
padding: 0 8px;
|
||||
gap: 6px;
|
||||
background: var(--bg-panel);
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tree-search-icon {
|
||||
color: var(--text-muted);
|
||||
font-size: 15px;
|
||||
flex-shrink: 0;
|
||||
line-height: 1;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.tree-search-input {
|
||||
flex: 1;
|
||||
height: 22px;
|
||||
background: var(--bg-control);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-primary);
|
||||
font-size: 11px;
|
||||
padding: 0 6px;
|
||||
outline: none;
|
||||
transition: border-color 0.15s;
|
||||
min-width: 0;
|
||||
}
|
||||
.tree-search-input::placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.tree-search-input:focus {
|
||||
border-color: var(--border-focus);
|
||||
}
|
||||
|
||||
.tree-search-clear {
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
color: var(--text-muted);
|
||||
font-size: 10px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition:
|
||||
background 0.12s,
|
||||
color 0.12s;
|
||||
}
|
||||
.tree-search-clear:hover {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tree-search-count {
|
||||
padding: 4px 10px;
|
||||
font-size: 10px;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.tree-highlight {
|
||||
background: rgba(108, 99, 255, 0.45);
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
padding: 0 1px;
|
||||
}
|
||||
|
||||
.tree-scroll {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.tree-scroll::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
.tree-scroll::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
.tree-scroll::-webkit-scrollbar-thumb {
|
||||
background: rgba(108, 99, 255, 0.3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.tree-empty {
|
||||
padding: 16px 12px;
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tree-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
margin: 1px 4px;
|
||||
transition: background 0.12s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tree-row:hover {
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
.tree-row.selected {
|
||||
background: var(--bg-selected);
|
||||
}
|
||||
.tree-row.inactive .tree-label {
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
.tree-arrow {
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.15s;
|
||||
display: inline-block;
|
||||
}
|
||||
.tree-arrow.expanded {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.tree-arrow.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.tree-label {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* ── 属性面板 ── */
|
||||
.prop-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.prop-scroll {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.prop-scroll::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
.prop-scroll::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
.prop-scroll::-webkit-scrollbar-thumb {
|
||||
background: rgba(108, 99, 255, 0.3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.prop-empty {
|
||||
padding: 16px 12px;
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
/* ── 组件折叠块 ── */
|
||||
.comp-panel {
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.comp-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
background: var(--bg-panel);
|
||||
gap: 6px;
|
||||
user-select: none;
|
||||
}
|
||||
.comp-header:hover {
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.comp-name {
|
||||
flex: 1;
|
||||
color: var(--accent-light);
|
||||
font-size: 11px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.comp-arrow {
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
width: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.comp-props {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.comp-empty {
|
||||
padding: 6px 12px;
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.comp-enabled {
|
||||
accent-color: var(--accent);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ── 属性行 ── */
|
||||
.prop-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 26px;
|
||||
padding: 0 8px;
|
||||
gap: 8px;
|
||||
}
|
||||
.prop-row:hover {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
|
||||
.prop-name {
|
||||
width: 72px;
|
||||
flex-shrink: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.prop-value {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.prop-input {
|
||||
width: 100%;
|
||||
height: 22px;
|
||||
padding: 0 6px;
|
||||
background: var(--bg-control);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-primary);
|
||||
font-size: 11px;
|
||||
outline: none;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
.prop-input:focus {
|
||||
border-color: var(--border-focus);
|
||||
}
|
||||
|
||||
.prop-checkbox {
|
||||
accent-color: var(--accent);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.prop-color {
|
||||
width: 100%;
|
||||
height: 22px;
|
||||
padding: 1px 3px;
|
||||
background: var(--bg-control);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.prop-unknown {
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* ── 通用图标按钮 ── */
|
||||
.icon-btn {
|
||||
height: 20px;
|
||||
min-width: 20px;
|
||||
padding: 0 5px;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 0.12s,
|
||||
color 0.12s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.icon-btn:hover {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--border-focus);
|
||||
color: var(--accent-light);
|
||||
}
|
||||
|
||||
/* ── Profiler 浮层 ── */
|
||||
.profiler-float {
|
||||
position: fixed;
|
||||
width: 220px;
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid rgba(108, 99, 255, 0.4);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.profiler-drag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 30px;
|
||||
padding: 0 8px;
|
||||
background: var(--bg-hover);
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.profiler-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.profiler-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 3px 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.profiler-row:hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.profiler-desc {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.profiler-val {
|
||||
color: var(--accent-light);
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ── 数字输入框去掉 spin ── */
|
||||
input[type='number']::-webkit-outer-spin-button,
|
||||
input[type='number']::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
49
packages/cccdev-template-3x/template/index.ejs
Normal file
49
packages/cccdev-template-3x/template/index.ejs
Normal file
@@ -0,0 +1,49 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="icon" href="./favicon.ico" />
|
||||
<meta charset="utf-8" />
|
||||
<title><%=title%></title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,user-scalable=no,initial-scale=1,minimum-scale=1,maximum-scale=1,minimal-ui=true"
|
||||
/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="full-screen" content="yes" />
|
||||
<meta name="screen-orientation" content="portrait" />
|
||||
<meta name="x5-fullscreen" content="true" />
|
||||
<meta name="360-fullscreen" content="true" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="force-rendering" content="webkit" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="./index.css" />
|
||||
<link rel="stylesheet" href="./devtools/assets/style.css" />
|
||||
</head>
|
||||
<body style="overflow: hidden;">
|
||||
<%- include(cocosToolBar, {config: config}) %>
|
||||
<div id="content" class="content" style="overflow: hidden;">
|
||||
<div class="contentWrap">
|
||||
<div id="GameDiv" class="wrapper">
|
||||
<div id="Cocos3dGameContainer">
|
||||
<canvas id="GameCanvas" tabindex="-1" style="background-color: '';"></canvas>
|
||||
</div>
|
||||
<div id="splash">
|
||||
<div class="progress-bar stripes"><span></span></div>
|
||||
</div>
|
||||
<div id="bulletin">
|
||||
<div id="sceneIsEmpty" class="inner"><%=tip_sceneIsEmpty%></div>
|
||||
</div>
|
||||
<div class="error" id="error">
|
||||
<div class="title">Error <i>(Please open the console to see detailed errors)</i></div>
|
||||
<div class="error-main"></div>
|
||||
<div class="error-stack"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cc-devtools-root"></div>
|
||||
<%- include(cocosTemplate, {}) %>
|
||||
<script src="./devtools/assets/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
9
packages/cccdev-template-3x/tsconfig.json
Normal file
9
packages/cccdev-template-3x/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
24
packages/cccdev-template-3x/vite.config.ts
Normal file
24
packages/cccdev-template-3x/vite.config.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import preact from '@preact/preset-vite';
|
||||
import { resolve } from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [preact()],
|
||||
base: '/devtools/',
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'src/main.tsx'),
|
||||
name: 'CCDevTools',
|
||||
formats: ['iife'],
|
||||
fileName: () => 'assets/index.js',
|
||||
},
|
||||
outDir: 'template/devtools',
|
||||
emptyOutDir: true,
|
||||
cssCodeSplit: false,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
assetFileNames: 'assets/[name].[ext]',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user