diff --git a/package-lock.json b/package-lock.json index ae277057..9c254b68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -108,6 +108,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -467,6 +480,13 @@ "node": ">=6" } }, + "node_modules/@cfworker/json-schema": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", + "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", + "dev": true, + "license": "MIT" + }, "node_modules/@csstools/color-helpers": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", @@ -2642,6 +2662,354 @@ "node": ">=14" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3690,6 +4058,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/qrcode-terminal": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/qrcode-terminal/-/qrcode-terminal-0.12.2.tgz", @@ -4873,6 +5248,27 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/archiver": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", @@ -5017,6 +5413,13 @@ "streamx": "^2.15.0" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5283,6 +5686,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/autoprefixer": { + "version": "10.4.22", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz", + "integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.27.0", + "caniuse-lite": "^1.0.30001754", + "fraction.js": "^5.3.4", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -5358,6 +5799,16 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.31", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", + "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/bignumber.js": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", @@ -5367,6 +5818,19 @@ "node": "*" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/binaryextensions": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", @@ -5534,6 +5998,40 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -5679,6 +6177,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001757", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", + "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chai": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", @@ -5817,6 +6346,22 @@ "entities": "^6.0.0" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -6412,6 +6957,19 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/cssstyle": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", @@ -6729,6 +7287,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/diff": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", @@ -6738,6 +7303,13 @@ "node": ">=0.3.1" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -6903,6 +7475,13 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/electron-to-chromium": { + "version": "1.5.260", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", + "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", + "dev": true, + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -8140,6 +8719,20 @@ "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", "license": "MIT" }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -8964,6 +9557,13 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -9411,6 +10011,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", @@ -10022,6 +10635,16 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", @@ -10424,6 +11047,13 @@ "url": "https://github.com/sponsors/antonk52" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -11169,6 +11799,18 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nan": { "version": "2.23.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz", @@ -11314,6 +11956,13 @@ "nan": "^2.17.0" } }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, "node_modules/node-sarif-builder": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.2.0.tgz", @@ -11352,6 +12001,16 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/npm-normalize-package-bin": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", @@ -11662,6 +12321,16 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -12248,6 +12917,16 @@ "node": ">=4" } }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/pkce-challenge": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", @@ -12306,6 +12985,140 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -12747,6 +13560,26 @@ "node": ">=0.8" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/read-package-json-fast": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", @@ -12877,6 +13710,20 @@ "node": ">=10" } }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -13275,6 +14122,27 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/sass": { + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", @@ -14121,6 +14989,39 @@ "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.5.tgz", "integrity": "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==" }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -14287,6 +15188,95 @@ "node": ">=8" } }, + "node_modules/tailwindcss": { + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", + "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/tar": { "version": "7.5.1", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", @@ -14434,6 +15424,29 @@ "url": "https://bevry.me/fund" } }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/thingies": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.5.0.tgz", @@ -14670,6 +15683,13 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -14990,6 +16010,37 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/update-notifier": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-7.3.1.tgz", @@ -16294,21 +17345,33 @@ "license": "LICENSE", "dependencies": { "@modelcontextprotocol/sdk": "^1.15.1", + "@qwen-code/qwen-code": "file:../cli", + "@qwen-code/qwen-code-core": "file:../core", "cors": "^2.8.5", "express": "^5.1.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", "zod": "^3.25.76" }, "devDependencies": { + "@cfworker/json-schema": "^4.1.1", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/node": "20.x", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@types/semver": "^7.7.1", "@types/vscode": "^1.99.0", "@typescript-eslint/eslint-plugin": "^8.31.1", "@typescript-eslint/parser": "^8.31.1", "@vscode/vsce": "^3.6.0", + "autoprefixer": "^10.4.22", "esbuild": "^0.25.3", "eslint": "^9.25.1", "npm-run-all2": "^8.0.2", + "postcss": "^8.5.6", + "sass": "^1.94.1", + "tailwindcss": "^3.4.18", "typescript": "^5.8.3", "vitest": "^3.2.4" }, @@ -16316,6 +17379,34 @@ "vscode": "^1.99.0" } }, + "packages/vscode-ide-companion/node_modules/@types/react": { + "version": "18.3.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", + "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "packages/vscode-ide-companion/node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "packages/vscode-ide-companion/node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, "packages/vscode-ide-companion/node_modules/@types/vscode": { "version": "1.99.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.99.0.tgz", @@ -16335,6 +17426,13 @@ "node": ">= 0.6" } }, + "packages/vscode-ide-companion/node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, "packages/vscode-ide-companion/node_modules/express": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", @@ -16409,6 +17507,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "packages/vscode-ide-companion/node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "packages/vscode-ide-companion/node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "packages/vscode-ide-companion/node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "packages/vscode-ide-companion/node_modules/send": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", diff --git a/packages/vscode-ide-companion/src/acp/acpConnection.ts b/packages/vscode-ide-companion/src/acp/acpConnection.ts index c8035cd8..13639d4d 100644 --- a/packages/vscode-ide-companion/src/acp/acpConnection.ts +++ b/packages/vscode-ide-companion/src/acp/acpConnection.ts @@ -45,7 +45,6 @@ import { AcpSessionManager } from './acpSessionManager.js'; * * Custom Methods (Not in standard ACP): * ⚠️ session/list - List available sessions (custom extension) - * ⚠️ session/switch - Switch to different session (custom extension) */ export class AcpConnection { private child: ChildProcess | null = null; @@ -70,12 +69,12 @@ export class AcpConnection { } /** - * 连接到ACP后端 + * Connect to ACP backend * - * @param backend - 后端类型 - * @param cliPath - CLI路径 - * @param workingDir - 工作目录 - * @param extraArgs - 额外的命令行参数 + * @param backend - Backend type + * @param cliPath - CLI path + * @param workingDir - Working directory + * @param extraArgs - Extra command line arguments */ async connect( backend: AcpBackend, @@ -92,8 +91,8 @@ export class AcpConnection { const isWindows = process.platform === 'win32'; const env = { ...process.env }; - // 如果在extraArgs中配置了代理,也将其设置为环境变量 - // 这确保token刷新请求也使用代理 + // If proxy is configured in extraArgs, also set it as environment variable + // This ensures token refresh requests also use the proxy const proxyArg = extraArgs.find( (arg, i) => arg === '--proxy' && i + 1 < extraArgs.length, ); @@ -134,9 +133,9 @@ export class AcpConnection { } /** - * 设置子进程处理器 + * Set up child process handlers * - * @param backend - 后端名称 + * @param backend - Backend name */ private async setupChildProcessHandlers(backend: string): Promise { let spawnError: Error | null = null; @@ -163,7 +162,7 @@ export class AcpConnection { ); }); - // 等待进程启动 + // Wait for process to start await new Promise((resolve) => setTimeout(resolve, 1000)); if (spawnError) { @@ -174,7 +173,7 @@ export class AcpConnection { throw new Error(`${backend} ACP process failed to start`); } - // 处理来自ACP服务器的消息 + // Handle messages from ACP server let buffer = ''; this.child.stdout?.on('data', (data) => { buffer += data.toString(); @@ -191,7 +190,7 @@ export class AcpConnection { ); this.handleMessage(message); } catch (_error) { - // 忽略非JSON行 + // Ignore non-JSON lines console.log( '[ACP] <<< Non-JSON line (ignored):', line.substring(0, 200), @@ -212,9 +211,9 @@ export class AcpConnection { } /** - * 处理接收到的消息 + * Handle received messages * - * @param message - ACP消息 + * @param message - ACP message */ private handleMessage(message: AcpMessage): void { const callbacks: AcpConnectionCallbacks = { @@ -223,9 +222,9 @@ export class AcpConnection { onEndTurn: this.onEndTurn, }; - // 处理消息 + // Handle message if ('method' in message) { - // 请求或通知 + // Request or notification this.messageHandler .handleIncomingRequest(message, callbacks) .then((result) => { @@ -260,10 +259,10 @@ export class AcpConnection { } /** - * 认证 + * Authenticate * - * @param methodId - 认证方法ID - * @returns 认证响应 + * @param methodId - Authentication method ID + * @returns Authentication response */ async authenticate(methodId?: string): Promise { return this.sessionManager.authenticate( @@ -275,10 +274,10 @@ export class AcpConnection { } /** - * 创建新会话 + * Create new session * - * @param cwd - 工作目录 - * @returns 新会话响应 + * @param cwd - Working directory + * @returns New session response */ async newSession(cwd: string = process.cwd()): Promise { return this.sessionManager.newSession( @@ -290,10 +289,10 @@ export class AcpConnection { } /** - * 发送提示消息 + * Send prompt message * - * @param prompt - 提示内容 - * @returns 响应 + * @param prompt - Prompt content + * @returns Response */ async sendPrompt(prompt: string): Promise { return this.sessionManager.sendPrompt( @@ -305,10 +304,10 @@ export class AcpConnection { } /** - * 加载已有会话 + * Load existing session * - * @param sessionId - 会话ID - * @returns 加载响应 + * @param sessionId - Session ID + * @returns Load response */ async loadSession(sessionId: string): Promise { return this.sessionManager.loadSession( @@ -320,9 +319,9 @@ export class AcpConnection { } /** - * 获取会话列表 + * Get session list * - * @returns 会话列表响应 + * @returns Session list response */ async listSessions(): Promise { return this.sessionManager.listSessions( @@ -333,27 +332,27 @@ export class AcpConnection { } /** - * 切换到指定会话 + * Switch to specified session * - * @param sessionId - 会话ID - * @returns 切换响应 + * @param sessionId - Session ID + * @returns Switch response */ async switchSession(sessionId: string): Promise { return this.sessionManager.switchSession(sessionId, this.nextRequestId); } /** - * 取消当前会话的提示生成 + * Cancel current session prompt generation */ async cancelSession(): Promise { await this.sessionManager.cancelSession(this.child); } /** - * 保存当前会话 + * Save current session * - * @param tag - 保存标签 - * @returns 保存响应 + * @param tag - Save tag + * @returns Save response */ async saveSession(tag: string): Promise { return this.sessionManager.saveSession( @@ -365,7 +364,7 @@ export class AcpConnection { } /** - * 断开连接 + * Disconnect */ disconnect(): void { if (this.child) { @@ -379,21 +378,21 @@ export class AcpConnection { } /** - * 检查是否已连接 + * Check if connected */ get isConnected(): boolean { return this.child !== null && !this.child.killed; } /** - * 检查是否有活动会话 + * Check if there is an active session */ get hasActiveSession(): boolean { return this.sessionManager.getCurrentSessionId() !== null; } /** - * 获取当前会话ID + * Get current session ID */ get currentSessionId(): string | null { return this.sessionManager.getCurrentSessionId(); diff --git a/packages/vscode-ide-companion/src/acp/schema.ts b/packages/vscode-ide-companion/src/acp/schema.ts index c3b92cb1..7d7e8527 100644 --- a/packages/vscode-ide-companion/src/acp/schema.ts +++ b/packages/vscode-ide-companion/src/acp/schema.ts @@ -55,5 +55,4 @@ export const CLIENT_METHODS = { */ export const CUSTOM_METHODS = { session_list: 'session/list', - session_switch: 'session/switch', } as const; diff --git a/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts b/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts index 161cf297..fce12e16 100644 --- a/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts +++ b/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts @@ -70,13 +70,21 @@ export class QwenConnectionHandler { // Check if we have valid cached authentication if (authStateManager) { + console.log('[QwenAgentManager] Checking for cached authentication...'); + console.log('[QwenAgentManager] Working dir:', workingDir); + console.log('[QwenAgentManager] Auth method:', authMethod); const hasValidAuth = await authStateManager.hasValidAuth( workingDir, authMethod, ); + console.log('[QwenAgentManager] Has valid auth:', hasValidAuth); if (hasValidAuth) { console.log('[QwenAgentManager] Using cached authentication'); + } else { + console.log('[QwenAgentManager] No valid cached authentication found'); } + } else { + console.log('[QwenAgentManager] No authStateManager provided'); } // Try to restore existing session or create new session @@ -156,7 +164,10 @@ export class QwenConnectionHandler { console.log( '[QwenAgentManager] Saving auth state after successful authentication', ); + console.log('[QwenAgentManager] Working dir for save:', workingDir); + console.log('[QwenAgentManager] Auth method for save:', authMethod); await authStateManager.saveAuthState(workingDir, authMethod); + console.log('[QwenAgentManager] Auth state save completed'); } } catch (authError) { console.error('[QwenAgentManager] Authentication failed:', authError); diff --git a/packages/vscode-ide-companion/src/auth/authStateManager.ts b/packages/vscode-ide-companion/src/auth/authStateManager.ts index bc2de16c..c2cfa47e 100644 --- a/packages/vscode-ide-companion/src/auth/authStateManager.ts +++ b/packages/vscode-ide-companion/src/auth/authStateManager.ts @@ -37,6 +37,7 @@ export class AuthStateManager { workingDir: state.workingDir, authMethod: state.authMethod, timestamp: new Date(state.timestamp).toISOString(), + isAuthenticated: state.isAuthenticated, }); console.log('[AuthStateManager] Checking against:', { workingDir, @@ -50,6 +51,11 @@ export class AuthStateManager { if (isExpired) { console.log('[AuthStateManager] Cached auth expired'); + console.log( + '[AuthStateManager] Cache age:', + Math.floor((now - state.timestamp) / 1000 / 60), + 'minutes', + ); await this.clearAuthState(); return false; } @@ -60,6 +66,10 @@ export class AuthStateManager { if (!isSameContext) { console.log('[AuthStateManager] Working dir or auth method changed'); + console.log('[AuthStateManager] Cached workingDir:', state.workingDir); + console.log('[AuthStateManager] Current workingDir:', workingDir); + console.log('[AuthStateManager] Cached authMethod:', state.authMethod); + console.log('[AuthStateManager] Current authMethod:', authMethod); return false; } @@ -78,31 +88,54 @@ export class AuthStateManager { timestamp: Date.now(), }; + console.log('[AuthStateManager] Saving auth state:', { + workingDir, + authMethod, + timestamp: new Date(state.timestamp).toISOString(), + }); + await this.context.globalState.update( AuthStateManager.AUTH_STATE_KEY, state, ); console.log('[AuthStateManager] Auth state saved'); + + // Verify the state was saved correctly + const savedState = await this.getAuthState(); + console.log('[AuthStateManager] Verified saved state:', savedState); } /** * Clear authentication state */ async clearAuthState(): Promise { + console.log('[AuthStateManager] Clearing auth state'); + const currentState = await this.getAuthState(); + console.log( + '[AuthStateManager] Current state before clearing:', + currentState, + ); + await this.context.globalState.update( AuthStateManager.AUTH_STATE_KEY, undefined, ); console.log('[AuthStateManager] Auth state cleared'); + + // Verify the state was cleared + const newState = await this.getAuthState(); + console.log('[AuthStateManager] State after clearing:', newState); } /** * Get current auth state */ private async getAuthState(): Promise { - return this.context.globalState.get( + const a = this.context.globalState.get( AuthStateManager.AUTH_STATE_KEY, ); + console.log('[AuthStateManager] Auth state:', a); + return a; } /** diff --git a/packages/vscode-ide-companion/src/diff-manager.ts b/packages/vscode-ide-companion/src/diff-manager.ts index 83636c56..4f151870 100644 --- a/packages/vscode-ide-companion/src/diff-manager.ts +++ b/packages/vscode-ide-companion/src/diff-manager.ts @@ -81,8 +81,6 @@ export class DiffManager { * @param newContent The modified content (right side) */ async showDiff(filePath: string, oldContent: string, newContent: string) { - const _fileUri = vscode.Uri.file(filePath); - // Left side: old content using qwen-diff scheme const leftDocUri = vscode.Uri.from({ scheme: DIFF_SCHEME, @@ -118,6 +116,7 @@ export class DiffManager { rightDocUri, diffTitle, { + viewColumn: vscode.ViewColumn.Beside, preview: false, preserveFocus: true, }, diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index bbef0b60..6e0196c4 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -14,7 +14,7 @@ import { IDE_DEFINITIONS, type IdeInfo, } from '@qwen-code/qwen-code-core/src/ide/detect-ide.js'; -import { WebViewProvider } from './WebViewProvider.js'; +import { WebViewProvider } from './webview/WebViewProvider.js'; import { AuthStateManager } from './auth/authStateManager.js'; const CLI_IDE_COMPANION_IDENTIFIER = 'qwenlm.qwen-code-vscode-ide-companion'; @@ -115,12 +115,29 @@ export async function activate(context: vscode.ExtensionContext) { const diffManager = new DiffManager(log, diffContentProvider); // Initialize Auth State Manager + console.log('[Extension] Initializing global AuthStateManager'); authStateManager = new AuthStateManager(context); + console.log( + '[Extension] Global AuthStateManager initialized:', + !!authStateManager, + ); // Helper function to create a new WebView provider instance const createWebViewProvider = (): WebViewProvider => { - const provider = new WebViewProvider(context, context.extensionUri); + console.log( + '[Extension] Creating WebViewProvider with global AuthStateManager:', + !!authStateManager, + ); + const provider = new WebViewProvider( + context, + context.extensionUri, + authStateManager, + ); webViewProviders.push(provider); + console.log( + '[Extension] WebViewProvider created, total providers:', + webViewProviders.length, + ); return provider; }; @@ -138,19 +155,26 @@ export async function activate(context: vscode.ExtensionContext) { // Create a new provider for the restored panel const provider = createWebViewProvider(); - provider.restorePanel(webviewPanel); + console.log('[Extension] Provider created for deserialization'); - // Restore state if available + // Restore state if available BEFORE restoring the panel if (state && typeof state === 'object') { + console.log('[Extension] Restoring state:', state); provider.restoreState( state as { conversationId: string | null; agentInitialized: boolean; }, ); + } else { + console.log('[Extension] No state to restore or invalid state'); } + await provider.restorePanel(webviewPanel); + console.log('[Extension] Panel restore completed'); + log('WebView panel restored from serialization'); + return true; }, }), ); diff --git a/packages/vscode-ide-companion/src/lib/tailwindUtils.d.ts b/packages/vscode-ide-companion/src/lib/tailwindUtils.d.ts deleted file mode 100644 index 12f38526..00000000 --- a/packages/vscode-ide-companion/src/lib/tailwindUtils.d.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Type declarations for tailwindUtils.js - -export function buttonClasses( - variant?: 'primary' | 'secondary' | 'ghost' | 'icon', - disabled?: boolean, -): string; - -export function inputClasses(): string; - -export function cardClasses(): string; - -export function dialogClasses(): string; - -export function qwenColorClasses( - color: 'orange' | 'clay-orange' | 'ivory' | 'slate' | 'green', -): string; - -export function spacingClasses( - size?: 'small' | 'medium' | 'large' | 'xlarge', - direction?: 'all' | 'x' | 'y' | 't' | 'r' | 'b' | 'l', -): string; - -export function borderRadiusClasses( - size?: 'small' | 'medium' | 'large', -): string; - -export const commonClasses: { - flexCenter: string; - flexBetween: string; - flexCol: string; - textMuted: string; - textSmall: string; - textLarge: string; - fontWeightMedium: string; - fontWeightSemibold: string; - marginAuto: string; - fullWidth: string; - fullHeight: string; - truncate: string; - srOnly: string; - transition: string; -}; diff --git a/packages/vscode-ide-companion/src/lib/tailwindUtils.js b/packages/vscode-ide-companion/src/lib/tailwindUtils.js deleted file mode 100644 index 1b28cc54..00000000 --- a/packages/vscode-ide-companion/src/lib/tailwindUtils.js +++ /dev/null @@ -1,131 +0,0 @@ -// Tailwind CSS 工具类集合 -// 用于封装常用的样式组合,便于在组件中复用 - -/** - * 生成按钮样式类 - * @param {string} variant - 按钮变体: 'primary', 'secondary', 'ghost', 'icon' - * @param {boolean} disabled - 是否禁用 - * @returns {string} Tailwind类字符串 - */ -export const buttonClasses = (variant = 'primary', disabled = false) => { - const baseClasses = 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2'; - - const variantClasses = { - primary: 'bg-qwen-orange text-qwen-ivory hover:bg-qwen-clay-orange shadow-sm', - secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-100 dark:hover:bg-gray-700', - ghost: 'hover:bg-gray-100 dark:hover:bg-gray-800', - icon: 'hover:bg-gray-100 dark:hover:bg-gray-800 p-1' - }; - - const disabledClasses = disabled ? 'opacity-50 pointer-events-none' : ''; - - return `${baseClasses} ${variantClasses[variant] || variantClasses.primary} ${disabledClasses}`; -}; - -/** - * 生成输入框样式类 - * @returns {string} Tailwind类字符串 - */ -export const inputClasses = () => { - return 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50'; -}; - -/** - * 生成卡片样式类 - * @returns {string} Tailwind类字符串 - */ -export const cardClasses = () => { - return 'rounded-lg border bg-card text-card-foreground shadow-sm'; -}; - -/** - * 生成对话框样式类 - * @returns {string} Tailwind类字符串 - */ -export const dialogClasses = () => { - return 'fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50'; -}; - -/** - * 生成Qwen品牌颜色类 - * @param {string} color - 颜色名称: 'orange', 'clay-orange', 'ivory', 'slate', 'green' - * @returns {string} Tailwind类字符串 - */ -export const qwenColorClasses = (color) => { - const colorMap = { - 'orange': 'text-qwen-orange', - 'clay-orange': 'text-qwen-clay-orange', - 'ivory': 'text-qwen-ivory', - 'slate': 'text-qwen-slate', - 'green': 'text-qwen-green' - }; - - return colorMap[color] || 'text-qwen-orange'; -}; - -/** - * 生成间距类 - * @param {string} size - 尺寸: 'small', 'medium', 'large', 'xlarge' - * @param {string} direction - 方向: 'all', 'x', 'y', 't', 'r', 'b', 'l' - * @returns {string} Tailwind类字符串 - */ -export const spacingClasses = (size = 'medium', direction = 'all') => { - const sizeMap = { - 'small': 'small', - 'medium': 'medium', - 'large': 'large', - 'xlarge': 'xlarge' - }; - - const directionMap = { - 'all': 'p', - 'x': 'px', - 'y': 'py', - 't': 'pt', - 'r': 'pr', - 'b': 'pb', - 'l': 'pl' - }; - - return `${directionMap[direction]}-${sizeMap[size]}`; -}; - -/** - * 生成圆角类 - * @param {string} size - 尺寸: 'small', 'medium', 'large' - * @returns {string} Tailwind类字符串 - */ -export const borderRadiusClasses = (size = 'medium') => { - const sizeMap = { - 'small': 'rounded-small', - 'medium': 'rounded-medium', - 'large': 'rounded-large' - }; - - return sizeMap[size] || 'rounded-medium'; -}; - -// 导出常用的类组合 -export const commonClasses = { - // 布局类 - flexCenter: 'flex items-center justify-center', - flexBetween: 'flex items-center justify-between', - flexCol: 'flex flex-col', - - // 文本类 - textMuted: 'text-gray-500 dark:text-gray-400', - textSmall: 'text-sm', - textLarge: 'text-lg', - fontWeightMedium: 'font-medium', - fontWeightSemibold: 'font-semibold', - - // 间距类 - marginAuto: 'm-auto', - fullWidth: 'w-full', - fullHeight: 'h-full', - - // 其他常用类 - truncate: 'truncate', - srOnly: 'sr-only', - transition: 'transition-all duration-200 ease-in-out' -}; \ No newline at end of file diff --git a/packages/vscode-ide-companion/src/services/qwenSessionReader.ts b/packages/vscode-ide-companion/src/services/qwenSessionReader.ts index ba8083e2..6e2d065d 100644 --- a/packages/vscode-ide-companion/src/services/qwenSessionReader.ts +++ b/packages/vscode-ide-companion/src/services/qwenSessionReader.ts @@ -42,7 +42,7 @@ export class QwenSessionReader { } /** - * 获取所有会话列表(可选:仅当前项目或所有项目) + * Get all session list (optional: current project only or all projects) */ async getAllSessions( workingDir?: string, @@ -52,13 +52,13 @@ export class QwenSessionReader { const sessions: QwenSession[] = []; if (!allProjects && workingDir) { - // 仅当前项目 + // Current project only const projectHash = await this.getProjectHash(workingDir); const chatsDir = path.join(this.qwenDir, 'tmp', projectHash, 'chats'); const projectSessions = await this.readSessionsFromDir(chatsDir); sessions.push(...projectSessions); } else { - // 所有项目 + // All projects const tmpDir = path.join(this.qwenDir, 'tmp'); if (!fs.existsSync(tmpDir)) { console.log('[QwenSessionReader] Tmp directory not found:', tmpDir); @@ -73,7 +73,7 @@ export class QwenSessionReader { } } - // 按最后更新时间排序 + // Sort by last updated time sessions.sort( (a, b) => new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime(), @@ -87,7 +87,7 @@ export class QwenSessionReader { } /** - * 从指定目录读取所有会话 + * Read all sessions from specified directory */ private async readSessionsFromDir(chatsDir: string): Promise { const sessions: QwenSession[] = []; @@ -120,7 +120,7 @@ export class QwenSessionReader { } /** - * 获取特定会话的详情 + * Get details of specific session */ async getSession( sessionId: string, @@ -132,8 +132,8 @@ export class QwenSessionReader { } /** - * 计算项目 hash(需要与 Qwen CLI 一致) - * Qwen CLI 使用项目路径的 SHA256 hash + * Calculate project hash (needs to be consistent with Qwen CLI) + * Qwen CLI uses SHA256 hash of project path */ private async getProjectHash(workingDir: string): Promise { const crypto = await import('crypto'); @@ -141,12 +141,12 @@ export class QwenSessionReader { } /** - * 获取会话的标题(基于第一条用户消息) + * Get session title (based on first user message) */ getSessionTitle(session: QwenSession): string { const firstUserMessage = session.messages.find((m) => m.type === 'user'); if (firstUserMessage) { - // 截取前50个字符作为标题 + // Extract first 50 characters as title return ( firstUserMessage.content.substring(0, 50) + (firstUserMessage.content.length > 50 ? '...' : '') @@ -156,7 +156,7 @@ export class QwenSessionReader { } /** - * 删除会话文件 + * Delete session file */ async deleteSession( sessionId: string, diff --git a/packages/vscode-ide-companion/src/webview/CliInstaller.ts b/packages/vscode-ide-companion/src/utils/CliInstaller.ts similarity index 98% rename from packages/vscode-ide-companion/src/webview/CliInstaller.ts rename to packages/vscode-ide-companion/src/utils/CliInstaller.ts index f1223659..b9aeb748 100644 --- a/packages/vscode-ide-companion/src/webview/CliInstaller.ts +++ b/packages/vscode-ide-companion/src/utils/CliInstaller.ts @@ -5,7 +5,7 @@ */ import * as vscode from 'vscode'; -import { CliDetector } from '../utils/cliDetector.js'; +import { CliDetector } from './cliDetector.js'; /** * CLI 检测和安装处理器 diff --git a/packages/vscode-ide-companion/src/utils/webviewUtils.ts b/packages/vscode-ide-companion/src/utils/webviewUtils.ts index 08616248..ed1b3135 100644 --- a/packages/vscode-ide-companion/src/utils/webviewUtils.ts +++ b/packages/vscode-ide-companion/src/utils/webviewUtils.ts @@ -5,21 +5,21 @@ */ /** - * 从完整路径中提取文件名 - * @param fsPath 文件的完整路径 - * @returns 文件名(不含路径) + * Extract filename from full path + * @param fsPath Full path of the file + * @returns Filename (without path) */ export function getFileName(fsPath: string): string { - // 使用 path.basename 的逻辑:找到最后一个路径分隔符后的部分 + // Use path.basename logic: find the part after the last path separator const lastSlash = Math.max(fsPath.lastIndexOf('/'), fsPath.lastIndexOf('\\')); return lastSlash >= 0 ? fsPath.substring(lastSlash + 1) : fsPath; } /** - * HTML 转义函数,防止 XSS 攻击 - * 将特殊字符转换为 HTML 实体 - * @param text 需要转义的文本 - * @returns 转义后的文本 + * HTML escape function to prevent XSS attacks + * Convert special characters to HTML entities + * @param text Text to escape + * @returns Escaped text */ export function escapeHtml(text: string): string { return text diff --git a/packages/vscode-ide-companion/src/webview/FileOperations.ts b/packages/vscode-ide-companion/src/webview/FileOperations.ts index 045beae5..262974a3 100644 --- a/packages/vscode-ide-companion/src/webview/FileOperations.ts +++ b/packages/vscode-ide-companion/src/webview/FileOperations.ts @@ -141,6 +141,7 @@ export class FileOperations { newUri, `${fileName} (Before ↔ After)`, { + viewColumn: vscode.ViewColumn.Beside, preview: false, preserveFocus: false, }, diff --git a/packages/vscode-ide-companion/src/webview/PanelManager.ts b/packages/vscode-ide-companion/src/webview/PanelManager.ts index 75764eba..08f30e6d 100644 --- a/packages/vscode-ide-companion/src/webview/PanelManager.ts +++ b/packages/vscode-ide-companion/src/webview/PanelManager.ts @@ -30,6 +30,7 @@ export class PanelManager { * 设置 Panel(用于恢复) */ setPanel(panel: vscode.WebviewPanel): void { + console.log('[PanelManager] Setting panel for restoration'); this.panel = panel; } @@ -171,19 +172,6 @@ export class PanelManager { console.log( '[PanelManager] Skipping auto-lock to allow multiple Qwen Code tabs', ); - - // If you want to enable auto-locking for the first tab, uncomment the following: - // const existingQwenInfo = this.findExistingQwenCodeGroup(); - // if (!existingQwenInfo) { - // console.log('[PanelManager] First Qwen Code tab, locking editor group'); - // try { - // this.revealPanel(false); - // await vscode.commands.executeCommand('workbench.action.lockEditorGroup'); - // console.log('[PanelManager] Editor group locked successfully'); - // } catch (error) { - // console.warn('[PanelManager] Failed to lock editor group:', error); - // } - // } } /** diff --git a/packages/vscode-ide-companion/src/WebViewProvider.ts b/packages/vscode-ide-companion/src/webview/WebViewProvider.ts similarity index 80% rename from packages/vscode-ide-companion/src/WebViewProvider.ts rename to packages/vscode-ide-companion/src/webview/WebViewProvider.ts index b4b4e1fe..6e924763 100644 --- a/packages/vscode-ide-companion/src/WebViewProvider.ts +++ b/packages/vscode-ide-companion/src/webview/WebViewProvider.ts @@ -5,16 +5,16 @@ */ import * as vscode from 'vscode'; -import { QwenAgentManager } from './agents/qwenAgentManager.js'; -import { ConversationStore } from './storage/conversationStore.js'; -import type { AcpPermissionRequest } from './shared/acpTypes.js'; -import { CliDetector } from './utils/cliDetector.js'; -import { AuthStateManager } from './auth/authStateManager.js'; -import { PanelManager } from './webview/PanelManager.js'; -import { MessageHandler } from './webview/MessageHandler.js'; -import { WebViewContent } from './webview/WebViewContent.js'; -import { CliInstaller } from './webview/CliInstaller.js'; -import { getFileName } from './utils/webviewUtils.js'; +import { QwenAgentManager } from '../agents/qwenAgentManager.js'; +import { ConversationStore } from '../storage/conversationStore.js'; +import type { AcpPermissionRequest } from '../shared/acpTypes.js'; +import { CliDetector } from '../utils/cliDetector.js'; +import { AuthStateManager } from '../auth/authStateManager.js'; +import { PanelManager } from './PanelManager.js'; +import { MessageHandler } from './MessageHandler.js'; +import { WebViewContent } from './WebViewContent.js'; +import { CliInstaller } from '../utils/CliInstaller.js'; +import { getFileName } from '../utils/webviewUtils.js'; export class WebViewProvider { private panelManager: PanelManager; @@ -28,10 +28,12 @@ export class WebViewProvider { constructor( context: vscode.ExtensionContext, private extensionUri: vscode.Uri, + authStateManager?: AuthStateManager, // 可选的全局AuthStateManager实例 ) { this.agentManager = new QwenAgentManager(); this.conversationStore = new ConversationStore(context); - this.authStateManager = new AuthStateManager(context); + // 如果提供了全局的authStateManager,则使用它,否则创建新的实例 + this.authStateManager = authStateManager || new AuthStateManager(context); this.panelManager = new PanelManager(extensionUri, () => { // Panel dispose callback this.disposables.forEach((d) => d.dispose()); @@ -174,6 +176,13 @@ export class WebViewProvider { return; } + // Set up state serialization + newPanel.onDidChangeViewState(() => { + console.log( + '[WebViewProvider] Panel view state changed, triggering serialization check', + ); + }); + // Capture the Tab that corresponds to our WebviewPanel this.panelManager.captureTab(); @@ -293,19 +302,50 @@ export class WebViewProvider { }); } - // Don't auto-login; user must use /login command - // Just initialize empty conversation for the UI - if (!this.agentInitialized) { - console.log( - '[WebViewProvider] Agent not initialized, waiting for /login command', + // Smart login restore: Check if we have valid cached auth and restore connection if available + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + const workingDir = workspaceFolder?.uri.fsPath || process.cwd(); + const config = vscode.workspace.getConfiguration('qwenCode'); + const openaiApiKey = config.get('qwen.openaiApiKey', ''); + const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth'; + + // Check if we have valid cached authentication + let hasValidAuth = false; + if (this.authStateManager) { + hasValidAuth = await this.authStateManager.hasValidAuth( + workingDir, + authMethod, ); - await this.initializeEmptyConversation(); - } else { + console.log( + '[WebViewProvider] Has valid cached auth on show:', + hasValidAuth, + ); + } + + if (hasValidAuth && !this.agentInitialized) { + console.log( + '[WebViewProvider] Found valid cached auth, attempting to restore connection...', + ); + try { + await this.initializeAgentConnection(); + console.log('[WebViewProvider] Connection restored successfully'); + } catch (error) { + console.error('[WebViewProvider] Failed to restore connection:', error); + // Fall back to empty conversation if restore fails + await this.initializeEmptyConversation(); + } + } else if (this.agentInitialized) { console.log( '[WebViewProvider] Agent already initialized, reusing existing connection', ); // Reload current session messages await this.loadCurrentSessionMessages(); + } else { + console.log( + '[WebViewProvider] No valid cached auth or agent already initialized, showing empty conversation', + ); + // Just initialize empty conversation for the UI + await this.initializeEmptyConversation(); } } @@ -352,6 +392,10 @@ export class WebViewProvider { try { console.log('[WebViewProvider] Connecting to agent...'); + console.log( + '[WebViewProvider] Using authStateManager:', + !!this.authStateManager, + ); const authInfo = await this.authStateManager.getAuthInfo(); console.log('[WebViewProvider] Auth cache status:', authInfo); @@ -385,10 +429,18 @@ export class WebViewProvider { */ async forceReLogin(): Promise { console.log('[WebViewProvider] Force re-login requested'); + console.log( + '[WebViewProvider] Current authStateManager:', + !!this.authStateManager, + ); // Clear existing auth cache - await this.authStateManager.clearAuthState(); - console.log('[WebViewProvider] Auth cache cleared'); + if (this.authStateManager) { + await this.authStateManager.clearAuthState(); + console.log('[WebViewProvider] Auth cache cleared'); + } else { + console.log('[WebViewProvider] No authStateManager to clear'); + } // Disconnect existing connection if any if (this.agentInitialized) { @@ -555,6 +607,10 @@ export class WebViewProvider { */ async restorePanel(panel: vscode.WebviewPanel): Promise { console.log('[WebViewProvider] Restoring WebView panel'); + console.log( + '[WebViewProvider] Current authStateManager in restore:', + !!this.authStateManager, + ); this.panelManager.setPanel(panel); panel.webview.html = WebViewContent.generate(panel, this.extensionUri); @@ -643,10 +699,41 @@ export class WebViewProvider { console.log('[WebViewProvider] Panel restored successfully'); - // Refresh connection on restore (will use cached auth if available) - if (this.agentInitialized) { + // Smart login restore: Check if we have valid cached auth and restore connection if available + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + const workingDir = workspaceFolder?.uri.fsPath || process.cwd(); + const config = vscode.workspace.getConfiguration('qwenCode'); + const openaiApiKey = config.get('qwen.openaiApiKey', ''); + const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth'; + + // Check if we have valid cached authentication + let hasValidAuth = false; + if (this.authStateManager) { + hasValidAuth = await this.authStateManager.hasValidAuth( + workingDir, + authMethod, + ); console.log( - '[WebViewProvider] Agent was initialized, refreshing connection...', + '[WebViewProvider] Has valid cached auth on restore:', + hasValidAuth, + ); + } + + if (hasValidAuth && !this.agentInitialized) { + console.log( + '[WebViewProvider] Found valid cached auth, attempting to restore connection...', + ); + try { + await this.initializeAgentConnection(); + console.log('[WebViewProvider] Connection restored successfully'); + } catch (error) { + console.error('[WebViewProvider] Failed to restore connection:', error); + // Fall back to empty conversation if restore fails + await this.initializeEmptyConversation(); + } + } else if (this.agentInitialized) { + console.log( + '[WebViewProvider] Agent already initialized, refreshing connection...', ); try { await this.refreshConnection(); @@ -659,8 +746,9 @@ export class WebViewProvider { } } else { console.log( - '[WebViewProvider] Agent not initialized, waiting for /login command', + '[WebViewProvider] No valid cached auth or agent already initialized, showing empty conversation', ); + // Just initialize empty conversation for the UI await this.initializeEmptyConversation(); } } @@ -673,10 +761,28 @@ export class WebViewProvider { conversationId: string | null; agentInitialized: boolean; } { - return { + console.log('[WebViewProvider] Getting state for serialization'); + console.log( + '[WebViewProvider] Current conversationId:', + this.messageHandler.getCurrentConversationId(), + ); + console.log( + '[WebViewProvider] Current agentInitialized:', + this.agentInitialized, + ); + const state = { conversationId: this.messageHandler.getCurrentConversationId(), agentInitialized: this.agentInitialized, }; + console.log('[WebViewProvider] Returning state:', state); + return state; + } + + /** + * Get the current panel + */ + getPanel(): vscode.WebviewPanel | null { + return this.panelManager.getPanel(); } /** @@ -689,6 +795,10 @@ export class WebViewProvider { console.log('[WebViewProvider] Restoring state:', state); this.messageHandler.setCurrentConversationId(state.conversationId); this.agentInitialized = state.agentInitialized; + console.log( + '[WebViewProvider] State restored. agentInitialized:', + this.agentInitialized, + ); // Reload content after restore const panel = this.panelManager.getPanel(); diff --git a/packages/vscode-ide-companion/src/webview/components/shared/FileLink.tsx b/packages/vscode-ide-companion/src/webview/components/shared/FileLink.tsx index 08daf429..33bf77ad 100644 --- a/packages/vscode-ide-companion/src/webview/components/shared/FileLink.tsx +++ b/packages/vscode-ide-companion/src/webview/components/shared/FileLink.tsx @@ -3,8 +3,8 @@ * Copyright 2025 Qwen Team * SPDX-License-Identifier: Apache-2.0 * - * FileLink 组件 - 可点击的文件路径链接 - * 支持点击打开文件并跳转到指定行号和列号 + * FileLink component - Clickable file path links + * Supports clicking to open files and jump to specified line and column numbers */ import type React from 'react'; @@ -15,24 +15,24 @@ import './FileLink.css'; * Props for FileLink */ interface FileLinkProps { - /** 文件路径 */ + /** File path */ path: string; - /** 可选的行号(从 1 开始) */ + /** Optional line number (starting from 1) */ line?: number | null; - /** 可选的列号(从 1 开始) */ + /** Optional column number (starting from 1) */ column?: number | null; - /** 是否显示完整路径,默认 false(只显示文件名) */ + /** Whether to show full path, default false (show filename only) */ showFullPath?: boolean; - /** 可选的自定义类名 */ + /** Optional custom class name */ className?: string; - /** 是否禁用点击行为(当父元素已经处理点击时使用) */ + /** Whether to disable click behavior (use when parent element handles clicks) */ disableClick?: boolean; } /** - * 从完整路径中提取文件名 - * @param path 文件路径 - * @returns 文件名 + * Extract filename from full path + * @param path File path + * @returns Filename */ function getFileName(path: string): string { const segments = path.split(/[/\\]/); @@ -40,13 +40,13 @@ function getFileName(path: string): string { } /** - * FileLink 组件 - 可点击的文件链接 + * FileLink component - Clickable file link * - * 功能: - * - 点击打开文件 - * - 支持行号和列号跳转 - * - 悬停显示完整路径 - * - 可选显示模式(完整路径 vs 仅文件名) + * Features: + * - Click to open file + * - Support line and column number navigation + * - Hover to show full path + * - Optional display mode (full path vs filename only) * * @example * ```tsx @@ -65,22 +65,22 @@ export const FileLink: React.FC = ({ const vscode = useVSCode(); /** - * 处理点击事件 - 发送消息到 VSCode 打开文件 + * Handle click event - Send message to VSCode to open file */ const handleClick = (e: React.MouseEvent) => { - // 总是阻止默认行为(防止 标签的 # 跳转) + // Always prevent default behavior (prevent tag # navigation) e.preventDefault(); if (disableClick) { - // 如果禁用点击,直接返回,不阻止冒泡 - // 这样父元素可以处理点击事件 + // If click is disabled, return directly without stopping propagation + // This allows parent elements to handle click events return; } - // 如果启用点击,阻止事件冒泡 + // If click is enabled, stop event propagation e.stopPropagation(); - // 构建包含行号和列号的完整路径 + // Build full path including line and column numbers let fullPath = path; if (line !== null && line !== undefined) { fullPath += `:${line}`; @@ -97,10 +97,10 @@ export const FileLink: React.FC = ({ }); }; - // 构建显示文本 + // Build display text const displayPath = showFullPath ? path : getFileName(path); - // 构建悬停提示(始终显示完整路径) + // Build hover tooltip (always show full path) const fullDisplayText = line !== null && line !== undefined ? column !== null && column !== undefined diff --git a/packages/vscode-ide-companion/src/webview/components/toolcalls/EditToolCall.tsx b/packages/vscode-ide-companion/src/webview/components/toolcalls/EditToolCall.tsx index c613f1ff..ca6757c9 100644 --- a/packages/vscode-ide-companion/src/webview/components/toolcalls/EditToolCall.tsx +++ b/packages/vscode-ide-companion/src/webview/components/toolcalls/EditToolCall.tsx @@ -94,7 +94,7 @@ export const EditToolCall: React.FC = ({ toolCall }) => { -
+
diff --git a/packages/vscode-ide-companion/src/webview/components/toolcalls/ExecuteToolCall.tsx b/packages/vscode-ide-companion/src/webview/components/toolcalls/ExecuteToolCall.tsx index 3acb7994..1beda8c9 100644 --- a/packages/vscode-ide-companion/src/webview/components/toolcalls/ExecuteToolCall.tsx +++ b/packages/vscode-ide-companion/src/webview/components/toolcalls/ExecuteToolCall.tsx @@ -39,7 +39,7 @@ export const ExecuteToolCall: React.FC = ({ toolCall }) => { {commandText}
-
+
IN @@ -73,7 +73,7 @@ export const ExecuteToolCall: React.FC = ({ toolCall }) => { {commandText}
-
+
IN diff --git a/packages/vscode-ide-companion/src/webview/components/toolcalls/shared/LayoutComponents.tsx b/packages/vscode-ide-companion/src/webview/components/toolcalls/shared/LayoutComponents.tsx index 107e54f6..b6e695cf 100644 --- a/packages/vscode-ide-companion/src/webview/components/toolcalls/shared/LayoutComponents.tsx +++ b/packages/vscode-ide-companion/src/webview/components/toolcalls/shared/LayoutComponents.tsx @@ -59,7 +59,7 @@ export const ToolCallContainer: React.FC = ({ > ● -
+
{label} @@ -195,7 +195,7 @@ interface LocationsListProps { * List of file locations with clickable links */ export const LocationsList: React.FC = ({ locations }) => ( -
+
{locations.map((loc, idx) => ( ))} diff --git a/packages/vscode-ide-companion/src/webview/handlers/SettingsMessageHandler.ts b/packages/vscode-ide-companion/src/webview/handlers/SettingsMessageHandler.ts index a434425e..3ac91e31 100644 --- a/packages/vscode-ide-companion/src/webview/handlers/SettingsMessageHandler.ts +++ b/packages/vscode-ide-companion/src/webview/handlers/SettingsMessageHandler.ts @@ -40,10 +40,12 @@ export class SettingsMessageHandler extends BaseMessageHandler { */ private async handleOpenSettings(): Promise { try { - await vscode.commands.executeCommand( - 'workbench.action.openSettings', - 'qwenCode', - ); + // Open settings in a side panel + await vscode.commands.executeCommand('workbench.action.openSettings', { + // TODO: + // openToSide: true, + query: 'qwenCode', + }); } catch (error) { console.error('[SettingsMessageHandler] Failed to open settings:', error); vscode.window.showErrorMessage(`Failed to open settings: ${error}`); diff --git a/packages/vscode-ide-companion/src/webview/utils/diffStats.ts b/packages/vscode-ide-companion/src/webview/utils/diffStats.ts index ba752311..78918821 100644 --- a/packages/vscode-ide-companion/src/webview/utils/diffStats.ts +++ b/packages/vscode-ide-companion/src/webview/utils/diffStats.ts @@ -3,35 +3,35 @@ * Copyright 2025 Qwen Team * SPDX-License-Identifier: Apache-2.0 * - * Diff 统计计算工具 + * Diff statistics calculation tool */ /** - * Diff 统计信息 + * Diff statistics */ export interface DiffStats { - /** 新增行数 */ + /** Number of added lines */ added: number; - /** 删除行数 */ + /** Number of removed lines */ removed: number; - /** 修改行数(估算值) */ + /** Number of changed lines (estimated value) */ changed: number; - /** 总变更行数 */ + /** Total number of changed lines */ total: number; } /** - * 计算两个文本之间的 diff 统计信息 + * Calculate diff statistics between two texts * - * 使用简单的行对比算法(避免引入重量级 diff 库) - * 算法说明: - * 1. 将文本按行分割 - * 2. 比较行的集合差异 - * 3. 估算修改行数(同时出现在新增和删除中的行数) + * Using a simple line comparison algorithm (avoiding heavy-weight diff libraries) + * Algorithm explanation: + * 1. Split text by lines + * 2. Compare set differences of lines + * 3. Estimate changed lines (lines that appear in both added and removed) * - * @param oldText 旧文本内容 - * @param newText 新文本内容 - * @returns diff 统计信息 + * @param oldText Old text content + * @param newText New text content + * @returns Diff statistics * * @example * ```typescript @@ -46,15 +46,15 @@ export function calculateDiffStats( oldText: string | null | undefined, newText: string | undefined, ): DiffStats { - // 处理空值情况 + // Handle null values const oldContent = oldText || ''; const newContent = newText || ''; - // 按行分割 + // Split by lines const oldLines = oldContent.split('\n').filter((line) => line.trim() !== ''); const newLines = newContent.split('\n').filter((line) => line.trim() !== ''); - // 如果其中一个为空,直接计算 + // If one of them is empty, calculate directly if (oldLines.length === 0) { return { added: newLines.length, @@ -73,18 +73,18 @@ export function calculateDiffStats( }; } - // 使用 Set 进行快速查找 + // Use Set for fast lookup const oldSet = new Set(oldLines); const newSet = new Set(newLines); - // 计算新增:在 new 中但不在 old 中的行 + // Calculate added: lines in new but not in old const addedLines = newLines.filter((line) => !oldSet.has(line)); - // 计算删除:在 old 中但不在 new 中的行 + // Calculate removed: lines in old but not in new const removedLines = oldLines.filter((line) => !newSet.has(line)); - // 估算修改:取较小值(因为修改的行既被删除又被添加) - // 这是一个简化的估算,实际的 diff 算法会更精确 + // Estimate changes: take the minimum value (because changed lines are both deleted and added) + // This is a simplified estimation, actual diff algorithms would be more precise const estimatedChanged = Math.min(addedLines.length, removedLines.length); const added = addedLines.length - estimatedChanged; @@ -100,10 +100,10 @@ export function calculateDiffStats( } /** - * 格式化 diff 统计信息为人类可读的文本 + * Format diff statistics as human-readable text * - * @param stats diff 统计信息 - * @returns 格式化后的文本,例如 "+5 -3 ~2" + * @param stats Diff statistics + * @returns Formatted text, e.g. "+5 -3 ~2" * * @example * ```typescript @@ -130,10 +130,10 @@ export function formatDiffStats(stats: DiffStats): string { } /** - * 格式化详细的 diff 统计信息 + * Format detailed diff statistics * - * @param stats diff 统计信息 - * @returns 详细的描述文本 + * @param stats Diff statistics + * @returns Detailed description text * * @example * ```typescript diff --git a/packages/vscode-ide-companion/src/webview/utils/resourceUrl.ts b/packages/vscode-ide-companion/src/webview/utils/resourceUrl.ts index f7298633..d55d4e14 100644 --- a/packages/vscode-ide-companion/src/webview/utils/resourceUrl.ts +++ b/packages/vscode-ide-companion/src/webview/utils/resourceUrl.ts @@ -33,15 +33,15 @@ function getExtensionUri(): string | undefined { } /** - * 验证 URL 是否为安全的 VS Code webview 资源 URL - * 防止 XSS 攻击 + * Validate if URL is a secure VS Code webview resource URL + * Prevent XSS attacks * - * @param url - 待验证的 URL - * @returns 是否为安全的 URL + * @param url - URL to validate + * @returns Whether it is a secure URL */ function isValidWebviewUrl(url: string): boolean { try { - // VS Code webview 资源 URL 的合法协议 + // Valid protocols for VS Code webview resource URLs const allowedProtocols = [ 'vscode-webview-resource:', 'https-vscode-webview-resource:', @@ -49,7 +49,7 @@ function isValidWebviewUrl(url: string): boolean { 'https:', ]; - // 检查是否以合法协议开头 + // Check if it starts with a valid protocol return allowedProtocols.some((protocol) => url.startsWith(protocol)); } catch { return false; @@ -76,7 +76,7 @@ export function generateResourceUrl(relativePath: string): string { return ''; } - // 验证 extensionUri 是否为安全的 URL + // Validate if extensionUri is a secure URL if (!isValidWebviewUrl(extensionUri)) { console.error( '[resourceUrl] Invalid extension URI - possible security risk:', @@ -97,7 +97,7 @@ export function generateResourceUrl(relativePath: string): string { const fullUrl = `${baseUri}${cleanPath}`; - // 验证最终生成的 URL 是否安全 + // Validate if the final generated URL is secure if (!isValidWebviewUrl(fullUrl)) { console.error('[resourceUrl] Generated URL failed validation:', fullUrl); return '';