🎨 Update

This commit is contained in:
2025-06-12 00:58:35 +08:00
parent 4844ccdf58
commit 0927b921c3
15 changed files with 559 additions and 76 deletions

View File

@@ -41,8 +41,10 @@
"@types/uuid": "^10.0.0",
"@vueuse/core": "^13.3.0",
"codemirror": "^6.0.1",
"colors-named": "^1.0.2",
"colors-named-hex": "^1.0.2",
"hsl-matcher": "^1.2.4",
"pinia": "^3.0.2",
"pinia-plugin-persistedstate": "^4.3.0",
"sass": "^1.89.1",
"uuid": "^11.1.0",
"vue": "^3.5.16",
@@ -1415,7 +1417,10 @@
"version": "3.17.4",
"resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-3.17.4.tgz",
"integrity": "sha512-l+hY8sy2XFfg3PigZj+PTu6+KIJzmbACTRimn1ew/gtCz+F38f6KTF4sMRTN5CUxiB8TRENgEonASmkAWfpO9Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"c12": "^3.0.4",
"consola": "^3.4.2",
@@ -1448,7 +1453,10 @@
"version": "7.0.4",
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.4.tgz",
"integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 4"
}
@@ -2033,6 +2041,7 @@
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.7.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
@@ -2608,6 +2617,7 @@
"version": "8.14.1",
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -2751,7 +2761,10 @@
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/c12/-/c12-3.0.4.tgz",
"integrity": "sha512-t5FaZTYbbCtvxuZq9xxIruYydrAGsJ+8UdP0pZzMiK2xl/gNiSOy0OxhLzHUEEb0m1QXYqfzfvyIFEmz/g9lqg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"chokidar": "^4.0.3",
"confbox": "^0.2.2",
@@ -2821,7 +2834,10 @@
"version": "0.1.6",
"resolved": "https://registry.npmmirror.com/citty/-/citty-0.1.6.tgz",
"integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"consola": "^3.2.3"
}
@@ -2861,6 +2877,30 @@
"dev": true,
"license": "MIT"
},
"node_modules/colors-named": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/colors-named/-/colors-named-1.0.2.tgz",
"integrity": "sha512-2ANq2r393PV9njYUD66UdfBcxR1slMqRA3QRTWgCx49JoCJ+kOhyfbQYxKJbPZQIhZUcNjVOs5AlyY1WwXec3w==",
"license": "MIT",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/colors-named-hex": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/colors-named-hex/-/colors-named-hex-1.0.2.tgz",
"integrity": "sha512-k6kq1e1pUCQvSVwIaGFq2l0LrkAPQZWyeuZn1Z8nOiYSEZiKoFj4qx690h2Kd34DFl9Me0gKS6MUwAMBJj8nuA==",
"license": "MIT",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
@@ -2872,13 +2912,17 @@
"version": "0.2.2",
"resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz",
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
"dev": true,
"license": "MIT"
},
"node_modules/consola": {
"version": "3.4.2",
"resolved": "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz",
"integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": "^14.18.0 || >=16.10.0"
}
@@ -2970,23 +3014,23 @@
"dev": true,
"license": "MIT"
},
"node_modules/deep-pick-omit": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/deep-pick-omit/-/deep-pick-omit-1.2.1.tgz",
"integrity": "sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==",
"license": "MIT"
},
"node_modules/defu": {
"version": "6.1.4",
"resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz",
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
"license": "MIT"
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/destr": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz",
"integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
"license": "MIT"
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/detect-libc": {
"version": "1.0.3",
@@ -3005,7 +3049,10 @@
"version": "16.5.0",
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.5.0.tgz",
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
"dev": true,
"license": "BSD-2-Clause",
"optional": true,
"peer": true,
"engines": {
"node": ">=12"
},
@@ -3029,7 +3076,10 @@
"version": "0.1.0",
"resolved": "https://registry.npmmirror.com/errx/-/errx-0.1.0.tgz",
"integrity": "sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==",
"license": "MIT"
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/esbuild": {
"version": "0.25.2",
@@ -3272,6 +3322,7 @@
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.5.tgz",
"integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-deep-equal": {
@@ -3418,7 +3469,10 @@
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/giget/-/giget-2.0.0.tgz",
"integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"citty": "^0.1.6",
"consola": "^3.4.0",
@@ -3490,6 +3544,18 @@
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT"
},
"node_modules/hsl-matcher": {
"version": "1.2.4",
"resolved": "https://registry.npmmirror.com/hsl-matcher/-/hsl-matcher-1.2.4.tgz",
"integrity": "sha512-tS7XnJS33Egirm+6cI+Z/kH/aVZt94uxGlJxOZlGql2/yqbAzPg3zHHnTnVN4cVpoJnEYEGq+LE3iXbuUIe8BA==",
"license": "MIT",
"engines": {
"node": ">=16.0.0"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz",
@@ -3602,7 +3668,10 @@
"version": "2.4.2",
"resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.4.2.tgz",
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"bin": {
"jiti": "lib/jiti-cli.mjs"
}
@@ -3611,7 +3680,10 @@
"version": "9.0.1",
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz",
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
"license": "MIT"
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/js-yaml": {
"version": "4.1.0",
@@ -3661,7 +3733,10 @@
"version": "2.0.6",
"resolved": "https://registry.npmmirror.com/klona/-/klona-2.0.6.tgz",
"integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 8"
}
@@ -3670,7 +3745,10 @@
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/knitwork/-/knitwork-1.2.0.tgz",
"integrity": "sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==",
"license": "MIT"
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/levn": {
"version": "0.4.1",
@@ -3690,6 +3768,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.1.tgz",
"integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"mlly": "^1.7.4",
@@ -3789,6 +3868,7 @@
"version": "1.7.4",
"resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.7.4.tgz",
"integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"acorn": "^8.14.0",
@@ -3801,12 +3881,14 @@
"version": "0.1.8",
"resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz",
"integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
"dev": true,
"license": "MIT"
},
"node_modules/mlly/node_modules/pkg-types": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz",
"integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"confbox": "^0.1.8",
@@ -3864,7 +3946,10 @@
"version": "1.6.6",
"resolved": "https://registry.npmmirror.com/node-fetch-native/-/node-fetch-native-1.6.6.tgz",
"integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==",
"license": "MIT"
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/normalize-path": {
"version": "3.0.0",
@@ -3893,7 +3978,10 @@
"version": "0.6.0",
"resolved": "https://registry.npmmirror.com/nypm/-/nypm-0.6.0.tgz",
"integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"citty": "^0.1.6",
"consola": "^3.4.0",
@@ -3912,7 +4000,10 @@
"version": "2.0.11",
"resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz",
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
"license": "MIT"
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/optionator": {
"version": "0.9.4",
@@ -4008,6 +4099,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"dev": true,
"license": "MIT"
},
"node_modules/perfect-debounce": {
@@ -4056,34 +4148,11 @@
}
}
},
"node_modules/pinia-plugin-persistedstate": {
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.3.0.tgz",
"integrity": "sha512-x9wxpHj6iFDj5ITQJ3rj6+KesEqyRk/vqcE3WE+VGfetleV9Zufqwa9qJ6AkA5wmRSQEp7BTA1us/MDVTRHFFw==",
"license": "MIT",
"dependencies": {
"@nuxt/kit": "^3.17.2",
"deep-pick-omit": "^1.2.1",
"defu": "^6.1.4",
"destr": "^2.0.5"
},
"peerDependencies": {
"@pinia/nuxt": ">=0.10.0",
"pinia": ">=3.0.0"
},
"peerDependenciesMeta": {
"@pinia/nuxt": {
"optional": true
},
"pinia": {
"optional": true
}
}
},
"node_modules/pkg-types": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.1.0.tgz",
"integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==",
"dev": true,
"license": "MIT",
"dependencies": {
"confbox": "^0.2.1",
@@ -4157,6 +4226,7 @@
"version": "0.2.10",
"resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.10.tgz",
"integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==",
"dev": true,
"funding": [
{
"type": "individual",
@@ -4194,7 +4264,10 @@
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/rc9/-/rc9-2.1.2.tgz",
"integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"defu": "^6.1.4",
"destr": "^2.0.3"
@@ -4328,12 +4401,16 @@
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
"license": "MIT"
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -4387,7 +4464,10 @@
"version": "3.9.0",
"resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.9.0.tgz",
"integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
"license": "MIT"
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
@@ -4406,7 +4486,10 @@
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-3.0.0.tgz",
"integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"js-tokens": "^9.0.1"
},
@@ -4449,12 +4532,16 @@
"version": "0.3.2",
"resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz",
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
"license": "MIT"
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/tinyglobby": {
"version": "0.2.14",
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz",
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.4",
@@ -4471,6 +4558,7 @@
"version": "6.4.4",
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.4.tgz",
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
@@ -4485,6 +4573,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -4573,13 +4662,17 @@
"version": "1.6.1",
"resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz",
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
"dev": true,
"license": "MIT"
},
"node_modules/unctx": {
"version": "2.4.1",
"resolved": "https://registry.npmmirror.com/unctx/-/unctx-2.4.1.tgz",
"integrity": "sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"acorn": "^8.14.0",
"estree-walker": "^3.0.3",
@@ -4591,7 +4684,10 @@
"version": "3.0.3",
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@types/estree": "^1.0.0"
}
@@ -4607,7 +4703,10 @@
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/unimport/-/unimport-5.0.1.tgz",
"integrity": "sha512-1YWzPj6wYhtwHE+9LxRlyqP4DiRrhGfJxdtH475im8ktyZXO3jHj/3PZ97zDdvkYoovFdi0K4SKl3a7l92v3sQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"acorn": "^8.14.1",
"escape-string-regexp": "^5.0.0",
@@ -4632,7 +4731,10 @@
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=12"
},
@@ -4644,7 +4746,10 @@
"version": "3.0.3",
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@types/estree": "^1.0.0"
}
@@ -4653,7 +4758,10 @@
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=12"
},
@@ -4665,6 +4773,7 @@
"version": "2.3.5",
"resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.5.tgz",
"integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==",
"dev": true,
"license": "MIT",
"dependencies": {
"acorn": "^8.14.1",
@@ -4679,6 +4788,7 @@
"version": "0.2.4",
"resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.2.4.tgz",
"integrity": "sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==",
"dev": true,
"license": "MIT",
"dependencies": {
"pathe": "^2.0.2",
@@ -4695,6 +4805,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -4794,6 +4905,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -4806,7 +4918,10 @@
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/untyped/-/untyped-2.0.0.tgz",
"integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"citty": "^0.1.6",
"defu": "^6.1.4",
@@ -5078,6 +5193,7 @@
"version": "0.6.2",
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
"dev": true,
"license": "MIT"
},
"node_modules/which": {

View File

@@ -45,8 +45,10 @@
"@types/uuid": "^10.0.0",
"@vueuse/core": "^13.3.0",
"codemirror": "^6.0.1",
"colors-named": "^1.0.2",
"colors-named-hex": "^1.0.2",
"hsl-matcher": "^1.2.4",
"pinia": "^3.0.2",
"pinia-plugin-persistedstate": "^4.3.0",
"sass": "^1.89.1",
"uuid": "^11.1.0",
"vue": "^3.5.16",

View File

@@ -4,10 +4,7 @@ import '@/assets/styles/index.css';
import {createPinia} from 'pinia';
import i18n from './i18n';
import router from './router';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
const app = createApp(App);
app.use(pinia)
app.use(i18n);

View File

@@ -24,9 +24,11 @@ import {defaultKeymap, history, historyKeymap,} from '@codemirror/commands';
import {highlightSelectionMatches} from '@codemirror/search';
import {autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap} from '@codemirror/autocomplete';
import {lintKeymap} from '@codemirror/lint';
import { vscodeSearch, customSearchKeymap, searchVisibilityField } from '../plugins/vscodeSearch';
import { vscodeSearch, customSearchKeymap, searchVisibilityField } from './vscodeSearch';
import { hyperLink } from './hyperlink';
import { color } from './colorSelector';
import { hyperLink } from '../plugins/hyperlink';
// 基本编辑器设置,包含常用扩展
export const createBasicSetup = (): Extension[] => {
return [
@@ -35,6 +37,7 @@ export const createBasicSetup = (): Extension[] => {
searchVisibilityField,
hyperLink,
color,
// 基础UI
lineNumbers(),
@@ -67,7 +70,7 @@ export const createBasicSetup = (): Extension[] => {
// 键盘映射
keymap.of([
...customSearchKeymap as KeyBinding[],
...customSearchKeymap,
...closeBracketsKeymap,
...defaultKeymap,
...historyKeymap,

View File

@@ -0,0 +1,301 @@
import { ViewPlugin, EditorView, ViewUpdate, WidgetType, Decoration, DecorationSet } from '@codemirror/view';
import { Extension, Range } from '@codemirror/state';
import { syntaxTree } from '@codemirror/language';
import colors from 'colors-named';
import hexs from 'colors-named-hex';
import hslMatcher, { hlsStringToRGB, RGBAColor } from 'hsl-matcher';
import { toFullHex, rgbToHex, hexToRgb, RGBToHSL } from './utils';
export enum ColorType {
rgb = 'RGB',
hex = 'HEX',
named = 'NAMED',
hsl = 'HSL',
}
export interface ColorState {
from: number;
to: number;
alpha: string;
colorType: ColorType;
}
const colorState = new WeakMap<HTMLInputElement, ColorState>();
type GetArrayElementType<T extends readonly any[]> = T extends readonly (infer U)[] ? U : never;
function colorDecorations(view: EditorView) {
const widgets: Array<Range<Decoration>> = [];
for (const range of view.visibleRanges) {
syntaxTree(view.state).iterate({
from: range.from,
to: range.to,
enter: ({ type, from, to }) => {
const callExp: string = view.state.doc.sliceString(from, to);
/**
* ```
* rgb(0 107 128, .5); ❌ ❌ ❌
* rgb( 0 107 128 ); ✅ ✅ ✅
* RGB( 0 107 128 ); ✅ ✅ ✅
* Rgb( 0 107 128 ); ✅ ✅ ✅
* rgb( 0 107 128 / ); ❌ ❌ ❌
* rgb( 0 107 128 / 60%); ✅ ✅ ✅
* rgb(0,107,128 / 60%); ❌ ❌ ❌
* rgb( 255, 255, 255 ) ✅ ✅ ✅
* rgba( 255, 255, 255 ) ✅ ✅ ✅
* rgba( 255, 255 , 255, ) ❌ ❌ ❌
* rgba( 255, 255 , 255, .5 ) ✅ ✅ ✅
* rgba( 255 255 255 / 0.5 ); ✅ ✅ ✅
* rgba( 255 255 255 0.5 ); ❌ ❌ ❌
* rgba( 255 255 255 / ); ❌ ❌ ❌
* ```
*/
if (type.name === 'CallExpression' && callExp.startsWith('rgb')) {
const match =
/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,?\s*(\d{1,3})\s*(,\s*\d*\.\d*\s*)?\)/i.exec(callExp) ||
/rgba?\(\s*(\d{1,3})\s*(\d{1,3})\s*(\d{1,3})\s*(\/?\s*\d+%)?(\/\s*\d+\.\d\s*)?\)/i.exec(callExp);
if (!match) return;
const [_, r, g, b, a] = match;
const hex = rgbToHex(Number(r), Number(g), Number(b));
const widget = Decoration.widget({
widget: new ColorWidget({
colorType: ColorType.rgb,
color: hex,
colorRaw: callExp,
from,
to,
alpha: a ? a.replace(/(\/|,)/g, '') : '',
}),
side: 0,
});
widgets.push(widget.range(from));
} else if (type.name === 'CallExpression' && hslMatcher(callExp)) {
/**
* # valid
* hsl(240, 100%, 50%) // ✅ comma separated
* hsl(240, 100%, 50%, 0.1) // ✅ comma separated with opacity
* hsl(240, 100%, 50%, 10%) // ✅ comma separated with % opacity
* hsl(240,100%,50%,0.1) // ✅ comma separated without spaces
* hsl(180deg, 100%, 50%, 0.1) // ✅ hue with 'deg'
* hsl(3.14rad, 100%, 50%, 0.1) // ✅ hue with 'rad'
* hsl(200grad, 100%, 50%, 0.1) // ✅ hue with 'grad'
* hsl(0.5turn, 100%, 50%, 0.1) // ✅ hue with 'turn'
* hsl(-240, -100%, -50%, -0.1) // ✅ negative values
* hsl(+240, +100%, +50%, +0.1) // ✅ explicit positive sign
* hsl(240.5, 99.99%, 49.999%, 0.9999) // ✅ non-integer values
* hsl(.9, .99%, .999%, .9999) // ✅ fraction w/o leading zero
* hsl(0240, 0100%, 0050%, 01) // ✅ leading zeros
* hsl(240.0, 100.00%, 50.000%, 1.0000) // ✅ trailing decimal zeros
* hsl(2400, 1000%, 1000%, 10) // ✅ out of range values
* hsl(-2400.01deg, -1000.5%, -1000.05%, -100) // ✅ combination of above
* hsl(2.40e+2, 1.00e+2%, 5.00e+1%, 1E-3) // ✅ scientific notation
* hsl(240 100% 50%) // ✅ space separated (CSS Color Level 4)
* hsl(240 100% 50% / 0.1) // ✅ space separated with opacity
* hsla(240, 100%, 50%) // ✅ hsla() alias
* hsla(240, 100%, 50%, 0.1) // ✅ hsla() with opacity
* HSL(240Deg, 100%, 50%) // ✅ case insensitive
*/
const match = hlsStringToRGB(callExp) as RGBAColor;
if (!match) return;
const { r, g, b } = match;
const hex = rgbToHex(Number(r), Number(g), Number(b));
const widget = Decoration.widget({
widget: new ColorWidget({
colorType: ColorType.hsl,
color: hex,
colorRaw: callExp,
from,
to,
alpha: match.a ? match.a.toString() : '',
}),
side: 0,
});
widgets.push(widget.range(from));
} else if (type.name === 'ColorLiteral') {
const [color, alpha] = toFullHex(callExp);
const widget = Decoration.widget({
widget: new ColorWidget({
colorType: ColorType.hex,
color,
colorRaw: callExp,
from,
to,
alpha,
}),
side: 0,
});
widgets.push(widget.range(from));
} else if (type.name === 'ValueName') {
const name = callExp as unknown as GetArrayElementType<typeof colors>;
if (colors.includes(name)) {
const widget = Decoration.widget({
widget: new ColorWidget({
colorType: ColorType.named,
color: hexs[colors.indexOf(name)],
colorRaw: callExp,
from,
to,
alpha: '',
}),
side: 0,
});
widgets.push(widget.range(from));
}
}
},
});
}
return Decoration.set(widgets);
}
class ColorWidget extends WidgetType {
private readonly state: ColorState;
private readonly color: string;
private readonly colorRaw: string;
constructor({
color,
colorRaw,
...state
}: ColorState & {
color: string;
colorRaw: string;
}) {
super();
this.state = state;
this.color = color;
this.colorRaw = colorRaw;
}
eq(other: ColorWidget) {
return (
other.state.colorType === this.state.colorType &&
other.color === this.color &&
other.state.from === this.state.from &&
other.state.to === this.state.to &&
other.state.alpha === this.state.alpha
);
}
toDOM() {
const picker = document.createElement('input');
colorState.set(picker, this.state);
picker.type = 'color';
picker.value = this.color;
picker.dataset['color'] = this.color;
picker.dataset['colorraw'] = this.colorRaw;
const wrapper = document.createElement('span');
wrapper.appendChild(picker);
wrapper.dataset['color'] = this.color;
wrapper.style.backgroundColor = this.colorRaw;
return wrapper;
}
ignoreEvent() {
return false;
}
}
export const colorView = (showPicker: boolean = true) =>
ViewPlugin.fromClass(
class ColorView {
decorations: DecorationSet;
constructor(view: EditorView) {
this.decorations = colorDecorations(view);
}
update(update: ViewUpdate) {
if (update.docChanged || update.viewportChanged) {
this.decorations = colorDecorations(update.view);
}
const readOnly = update.view.contentDOM.ariaReadOnly === 'true';
const editable = update.view.contentDOM.contentEditable === 'true';
const canBeEdited = readOnly === false && editable;
this.changePicker(update.view, canBeEdited);
}
changePicker(view: EditorView, canBeEdited: boolean) {
const doms = view.contentDOM.querySelectorAll('input[type=color]');
doms.forEach((inp) => {
if (!showPicker) {
inp.setAttribute('disabled', '');
} else {
canBeEdited ? inp.removeAttribute('disabled') : inp.setAttribute('disabled', '');
}
});
}
},
{
decorations: (v) => v.decorations,
eventHandlers: {
change: (e, view) => {
const target = e.target as HTMLInputElement;
if (
target.nodeName !== 'INPUT' ||
!target.parentElement ||
(!target.dataset.color && !target.dataset.colorraw)
)
return false;
const data = colorState.get(target)!;
const value = target.value;
const rgb = hexToRgb(value);
const colorraw = target.dataset.colorraw;
const slash = (target.dataset.colorraw || '').indexOf('/') > 4;
const comma = (target.dataset.colorraw || '').indexOf(',') > 4;
let converted = target.value;
if (data.colorType === ColorType.rgb) {
let funName = colorraw?.match(/^(rgba?)/) ? colorraw?.match(/^(rgba?)/)![0] : undefined;
if (comma) {
converted = rgb
? `${funName}(${rgb.r}, ${rgb.g}, ${rgb.b}${data.alpha ? ', ' + data.alpha.trim() : ''})`
: value;
} else if (slash) {
converted = rgb
? `${funName}(${rgb.r} ${rgb.g} ${rgb.b}${data.alpha ? ' / ' + data.alpha.trim() : ''})`
: value;
} else {
converted = rgb ? `${funName}(${rgb.r} ${rgb.g} ${rgb.b})` : value;
}
} else if (data.colorType === ColorType.hsl) {
const rgb = hexToRgb(value);
if (rgb) {
const { h, s, l } = RGBToHSL(rgb?.r, rgb?.g, rgb?.b);
converted = `hsl(${h}deg ${s}% ${l}%${data.alpha ? ' / ' + data.alpha : ''})`;
}
}
view.dispatch({
changes: {
from: data.from,
to: data.to,
insert: converted,
},
});
return true;
},
},
},
);
export const colorTheme = EditorView.baseTheme({
'span[data-color]': {
width: '12px',
height: '12px',
display: 'inline-block',
borderRadius: '2px',
marginRight: '0.5ch',
outline: '1px solid #00000040',
overflow: 'hidden',
verticalAlign: 'middle',
marginTop: '-2px',
},
'span[data-color] input[type="color"]': {
background: 'transparent',
display: 'block',
border: 'none',
outline: '0',
paddingLeft: '24px',
height: '12px',
},
'span[data-color] input[type="color"]::-webkit-color-swatch': {
border: 'none',
paddingLeft: '24px',
},
});
export const color: Extension = [colorView(), colorTheme];

View File

@@ -0,0 +1,64 @@
export function toFullHex(color: string): string[] {
if (color.length === 4) {
// 3-char hex
return [`#${color[1].repeat(2)}${color[2].repeat(2)}${color[3].repeat(2)}`, ''];
}
if (color.length === 5) {
// 4-char hex (alpha)
return [`#${color[1].repeat(2)}${color[2].repeat(2)}${color[3].repeat(2)}`, color[4].repeat(2)];
}
if (color.length === 9) {
// 8-char hex (alpha)
return [`#${color.slice(1, -2)}`, color.slice(-2)];
}
return [color, ''];
}
/** https://stackoverflow.com/a/5624139/1334703 */
export function rgbToHex(r: number, g: number, b: number) {
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
/** https://stackoverflow.com/a/5624139/1334703 */
export function hexToRgb(hex: string) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: null;
}
/** https://css-tricks.com/converting-color-spaces-in-javascript/#aa-rgb-to-hsl */
export function RGBToHSL(r: number, g: number, b: number) {
(r /= 255), (g /= 255), (b /= 255);
const max = Math.max(r, g, b),
min = Math.min(r, g, b);
let h = 0,
s,
l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return { h: Math.floor(h * 360), s: Math.floor(s * 100), l: Math.floor(l * 100) };
}

View File

@@ -127,8 +127,8 @@ export class CustomSearchPanel {
if (query.regexp) {
try {
this.regexCursor = new RegExpCursor(state.doc, query.search)
this.searchCursor = undefined;
this.regexCursor = new RegExpCursor(state.doc, query.search)
this.searchCursor = undefined;
} catch (error) {
// 如果正则表达式无效,清空匹配结果并显示错误状态
console.warn("Invalid regular expression:", query.search, error);
@@ -168,17 +168,17 @@ export class CustomSearchPanel {
}
else if (this.regexCursor) {
try {
const matchWord = this.regexpWordTest(state.charCategorizer(state.selection.main.head))
const matchWord = this.regexpWordTest(state.charCategorizer(state.selection.main.head))
while (!this.regexCursor.done) {
this.regexCursor.next();
while (!this.regexCursor.done) {
this.regexCursor.next();
if (!this.regexCursor.done) {
const { from, to, match } = this.regexCursor.value;
if (!this.regexCursor.done) {
const { from, to, match } = this.regexCursor.value;
if (!query.wholeWord || matchWord(from, to, match)) {
this.matches.push({ from, to });
}
if (!query.wholeWord || matchWord(from, to, match)) {
this.matches.push({ from, to });
}
}
}
} catch (error) {
@@ -222,19 +222,19 @@ export class CustomSearchPanel {
commit() {
try {
const newQuery = new SearchQuery({
search: this.searchField.value,
replace: this.replaceField.value,
caseSensitive: this.matchCase,
regexp: this.useRegex,
wholeWord: this.matchWord,
})
const newQuery = new SearchQuery({
search: this.searchField.value,
replace: this.replaceField.value,
caseSensitive: this.matchCase,
regexp: this.useRegex,
wholeWord: this.matchWord,
})
let query = getSearchQuery(this.view.state)
if (!newQuery.eq(query)) {
this.view.dispatch({
effects: setSearchQuery.of(newQuery)
})
let query = getSearchQuery(this.view.state)
if (!newQuery.eq(query)) {
this.view.dispatch({
effects: setSearchQuery.of(newQuery)
})
}
} catch (error) {
// 如果创建SearchQuery时出错通常是无效的正则表达式记录错误但不中断程序
@@ -367,7 +367,7 @@ export class CustomSearchPanel {
});
// 重新查找匹配项
this.findMatchesAndSelectClosest(this.view.state);
this.findMatchesAndSelectClosest(this.view.state);
}
}
}
@@ -392,7 +392,7 @@ export class CustomSearchPanel {
});
// 重新查找匹配项
this.findMatchesAndSelectClosest(this.view.state);
this.findMatchesAndSelectClosest(this.view.state);
}
}
@@ -592,7 +592,7 @@ export class CustomSearchPanel {
if (visible) {
// 使用 setTimeout 确保DOM已经渲染
setTimeout(() => {
this.searchField.focus();
this.searchField.focus();
this.searchField.select();
}, 0);
}

View File

@@ -72,10 +72,10 @@ export const customSearchKeymap: KeyBinding[] = [
key: "ArrowLeft",
run: searchMoveCursorLeft,
scope: 'search'
},
{
},
{
key: "ArrowRight",
run: searchMoveCursorRight,
scope: 'search'
},
},
];