feat: Premium UI overhaul, brain management, history persistence, and IME fix
This commit is contained in:
Generated
+16
-488
@@ -1,17 +1,18 @@
|
||||
{
|
||||
"name": "g1nation",
|
||||
"version": "2.2.46",
|
||||
"version": "2.2.47",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "g1nation",
|
||||
"version": "2.2.46",
|
||||
"version": "2.2.47",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jsdom": "^29.0.2"
|
||||
"marked": "^18.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/marked": "^5.0.2",
|
||||
"@types/node": "18.x",
|
||||
"@types/vscode": "^1.80.0",
|
||||
"@vercel/ncc": "^0.38.4",
|
||||
@@ -22,188 +23,6 @@
|
||||
"vscode": "^1.80.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/css-color": {
|
||||
"version": "5.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.10.tgz",
|
||||
"integrity": "sha512-02OhhkKtgNRuicQ/nF3TRnGsxL9wp0r3Y7VlKWyOHHGmGyvXv03y+PnymU8FKFJMTjIr1Bk8U2g1HWSLrpAHww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/css-calc": "^3.1.1",
|
||||
"@csstools/css-color-parser": "^4.0.2",
|
||||
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||
"@csstools/css-tokenizer": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/dom-selector": {
|
||||
"version": "7.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.9.tgz",
|
||||
"integrity": "sha512-r3ElRr7y8ucyN2KdICwGsmj19RoN13CLCa/pvGydghWK6ZzeKQ+TcDjVdtEZz2ElpndM5jXw//B9CEee0mWnVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@asamuzakjp/nwsapi": "^2.3.9",
|
||||
"bidi-js": "^1.0.3",
|
||||
"css-tree": "^3.2.1",
|
||||
"is-potential-custom-element-name": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@asamuzakjp/nwsapi": {
|
||||
"version": "2.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
|
||||
"integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@bramus/specificity": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz",
|
||||
"integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"css-tree": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"specificity": "bin/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/color-helpers": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz",
|
||||
"integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT-0",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-calc": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz",
|
||||
"integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||
"@csstools/css-tokenizer": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-color-parser": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz",
|
||||
"integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@csstools/color-helpers": "^6.0.2",
|
||||
"@csstools/css-calc": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||
"@csstools/css-tokenizer": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-parser-algorithms": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz",
|
||||
"integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@csstools/css-tokenizer": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-syntax-patches-for-csstree": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.2.tgz",
|
||||
"integrity": "sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT-0",
|
||||
"peerDependencies": {
|
||||
"css-tree": "^3.2.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"css-tree": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/css-tokenizer": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz",
|
||||
"integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
|
||||
@@ -646,22 +465,12 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@exodus/bytes": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz",
|
||||
"integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@noble/hashes": "^1.8.0 || ^2.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@noble/hashes": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
"node_modules/@types/marked": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz",
|
||||
"integrity": "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.19.130",
|
||||
@@ -690,59 +499,6 @@
|
||||
"ncc": "dist/ncc/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/bidi-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"require-from-string": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
|
||||
"integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mdn-data": "2.27.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/data-urls": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz",
|
||||
"integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-mimetype": "^5.0.0",
|
||||
"whatwg-url": "^16.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/decimal.js": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
||||
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
|
||||
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
|
||||
@@ -785,176 +541,16 @@
|
||||
"@esbuild/win32-x64": "0.28.0"
|
||||
}
|
||||
},
|
||||
"node_modules/html-encoding-sniffer": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz",
|
||||
"integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==",
|
||||
"node_modules/marked": {
|
||||
"version": "18.0.2",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-18.0.2.tgz",
|
||||
"integrity": "sha512-NsmlUYBS/Zg57rgDWMYdnre6OTj4e+qq/JS2ot3KrYLSoHLw+sDu0Nm1ZGpRgYAq6c+b1ekaY5NzVchMCQnzcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@exodus/bytes": "^1.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-potential-custom-element-name": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
|
||||
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jsdom": {
|
||||
"version": "29.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.2.tgz",
|
||||
"integrity": "sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@asamuzakjp/css-color": "^5.1.5",
|
||||
"@asamuzakjp/dom-selector": "^7.0.6",
|
||||
"@bramus/specificity": "^2.4.2",
|
||||
"@csstools/css-syntax-patches-for-csstree": "^1.1.1",
|
||||
"@exodus/bytes": "^1.15.0",
|
||||
"css-tree": "^3.2.1",
|
||||
"data-urls": "^7.0.0",
|
||||
"decimal.js": "^10.6.0",
|
||||
"html-encoding-sniffer": "^6.0.0",
|
||||
"is-potential-custom-element-name": "^1.0.1",
|
||||
"lru-cache": "^11.2.7",
|
||||
"parse5": "^8.0.0",
|
||||
"saxes": "^6.0.0",
|
||||
"symbol-tree": "^3.2.4",
|
||||
"tough-cookie": "^6.0.1",
|
||||
"undici": "^7.24.5",
|
||||
"w3c-xmlserializer": "^5.0.0",
|
||||
"webidl-conversions": "^8.0.1",
|
||||
"whatwg-mimetype": "^5.0.0",
|
||||
"whatwg-url": "^16.0.1",
|
||||
"xml-name-validator": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"canvas": "^3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"canvas": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "11.3.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.3.tgz",
|
||||
"integrity": "sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.27.1",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
|
||||
"integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/parse5": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
|
||||
"integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"entities": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/saxes": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
|
||||
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"xmlchars": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=v12.22.7"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/symbol-tree": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tldts": {
|
||||
"version": "7.0.28",
|
||||
"resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.28.tgz",
|
||||
"integrity": "sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tldts-core": "^7.0.28"
|
||||
},
|
||||
"bin": {
|
||||
"tldts": "bin/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/tldts-core": {
|
||||
"version": "7.0.28",
|
||||
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.28.tgz",
|
||||
"integrity": "sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz",
|
||||
"integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"tldts": "^7.0.5"
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz",
|
||||
"integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
@@ -971,80 +567,12 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz",
|
||||
"integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/w3c-xmlserializer": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
|
||||
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"xml-name-validator": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
|
||||
"integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-mimetype": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz",
|
||||
"integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz",
|
||||
"integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@exodus/bytes": "^1.11.0",
|
||||
"tr46": "^6.0.0",
|
||||
"webidl-conversions": "^8.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-name-validator": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
|
||||
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlchars": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -2,7 +2,7 @@
|
||||
"name": "g1nation",
|
||||
"displayName": "G1nation",
|
||||
"description": "100% local AI coding agent for VS Code. Create files, edit code, run commands, and work offline with Ollama or LM Studio.",
|
||||
"version": "2.2.46",
|
||||
"version": "2.2.57",
|
||||
"publisher": "connectailab",
|
||||
"license": "MIT",
|
||||
"icon": "assets/icon.png",
|
||||
@@ -180,6 +180,7 @@
|
||||
"pretest": "npm run compile"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/marked": "^5.0.2",
|
||||
"@types/node": "18.x",
|
||||
"@types/vscode": "^1.80.0",
|
||||
"@vercel/ncc": "^0.38.4",
|
||||
@@ -187,5 +188,6 @@
|
||||
"typescript": "^5.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"marked": "^18.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
+43
-31
@@ -465,25 +465,32 @@ export class AgentExecutor {
|
||||
return [
|
||||
`제가 실제로 \`${projectPath}\` 폴더를 읽고 1차 분석했습니다.`,
|
||||
'',
|
||||
'**제품 설명**',
|
||||
pkg?.name
|
||||
? `- 이 프로젝트는 \`${pkg.name}\`${pkg.description ? ` (${pkg.description})` : ''}로 식별됩니다.`
|
||||
: '- `package.json` 기준의 제품명은 확인되지 않았습니다.',
|
||||
readmeSummary ? `- README 기준 핵심 설명: ${readmeSummary}` : '- README 기반 제품 설명은 부족합니다. 제품 소개 문서를 보강하는 편이 좋습니다.',
|
||||
stack.length ? `- 감지된 기술 스택: ${stack.join(', ')}` : '- 기술 스택은 파일 구조만으로는 명확하지 않습니다.',
|
||||
'### 📋 제품 개요',
|
||||
'| 항목 | 내용 |',
|
||||
'| :--- | :--- |',
|
||||
pkg?.name ? `| **제품명** | \`${pkg.name}\` |` : '| **제품명** | 식별되지 않음 |',
|
||||
pkg?.description ? `| **설명** | ${pkg.description} |` : '| **설명** | - |',
|
||||
stack.length ? `| **기술 스택** | ${stack.join(', ')} |` : '| **기술 스택** | 파악 중 |',
|
||||
readmeSummary ? `| **핵심 요약** | ${readmeSummary} |` : '| **핵심 요약** | README 정보 부족 |',
|
||||
'',
|
||||
'**설계 구조**',
|
||||
topDirs.length ? topDirs.map(item => `- ${item}`).join('\n') : '- 상위 디렉터리 구조가 단순하거나 비어 있습니다.',
|
||||
entryPoints.length ? `- 주요 진입점 후보: ${entryPoints.join(', ')}` : '- 명확한 앱 진입점은 아직 식별되지 않았습니다.',
|
||||
configFiles.length ? `- 주요 설정 파일: ${configFiles.slice(0, 12).join(', ')}` : '- 주요 설정 파일이 많지 않습니다.',
|
||||
'### 🏗️ 설계 구조',
|
||||
'| 디렉토리 | 파일 수 |',
|
||||
'| :--- | :--- |',
|
||||
...(topDirs.length ? topDirs : ['| - | - |']),
|
||||
'',
|
||||
'**코드 리뷰 관점의 1차 소견**',
|
||||
reviewFindings.join('\n'),
|
||||
'**주요 진입점 및 설정**',
|
||||
entryPoints.length ? `- 진입점: ${entryPoints.map(e => `\`${e}\``).join(', ')}` : '- 명확한 진입점 미식별',
|
||||
configFiles.length ? `- 설정: ${configFiles.slice(0, 8).map(f => `\`${f}\``).join(', ')}` : '- 주요 설정 파일 없음',
|
||||
'',
|
||||
'**다음에 더 깊게 볼 부분**',
|
||||
'- 실제 비즈니스 플로우는 `src`, `app`, `lib`, `components` 내부 핵심 파일을 2차로 읽어야 정확히 판단할 수 있습니다.',
|
||||
'- 지금 단계에서는 “아무것도 안 하고 말만 하는” 답변이 아니라, 실제 파일 시스템을 기준으로 프로젝트 형태를 먼저 파악한 결과입니다.',
|
||||
'- 원하시면 다음 턴에서 제가 핵심 소스 파일을 더 읽어 아키텍처 다이어그램 수준으로 이어서 정리하겠습니다.'
|
||||
'### 🔍 코드 리뷰 1차 소견',
|
||||
...reviewFindings.map(f => f.startsWith('-') ? f : `- ${f}`),
|
||||
'',
|
||||
'### 🚀 향후 분석 제안',
|
||||
'1. **비즈니스 로직 분석**: `src`, `app`, `lib` 내부의 핵심 비즈니스 흐름 파악',
|
||||
'2. **아키텍처 시각화**: 파일 간 의존성 및 데이터 흐름 다이어그램 작성',
|
||||
'3. **상세 코드 리뷰**: 특정 파일이나 기능에 대한 심층 분석',
|
||||
'',
|
||||
'*분석을 계속하시려면 궁금한 파일이나 모듈을 말씀해 주세요.*'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
@@ -521,7 +528,7 @@ export class AgentExecutor {
|
||||
.slice(0, 12)
|
||||
.map(entry => {
|
||||
const count = this.collectProjectFiles(path.join(projectPath, entry.name), 200, projectPath).length;
|
||||
return `\`${entry.name}/\`: 약 ${count}개 파일`;
|
||||
return `| \`${entry.name}/\` | 약 ${count}개 |`;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -638,27 +645,32 @@ export class AgentExecutor {
|
||||
.map((entry) => {
|
||||
const dirPath = path.join(brainDir, entry.name);
|
||||
const count = findBrainFiles(dirPath).length;
|
||||
return `- ${entry.name}/: ${count}개 문서`;
|
||||
return `| \`${entry.name}/\` | ${count}개 문서 |`;
|
||||
});
|
||||
|
||||
const fileSummaries = entries
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
|
||||
.slice(0, 8)
|
||||
.slice(0, 10)
|
||||
.map((entry) => `- ${entry.name}`);
|
||||
|
||||
const sampleFiles = files
|
||||
.slice(0, 10)
|
||||
.map((file) => `- ${path.relative(brainDir, file)}`);
|
||||
|
||||
const sections = [
|
||||
`현재 연결된 제2뇌는 **${activeBrain.name}**입니다.`,
|
||||
`경로: \`${brainDir}\``,
|
||||
`총 Markdown 지식 문서: **${files.length}개**`,
|
||||
activeBrain.description ? `설명: ${activeBrain.description}` : '',
|
||||
directorySummaries.length ? `\n상위 폴더 구조는 이렇게 보입니다.\n${directorySummaries.join('\n')}` : '',
|
||||
fileSummaries.length ? `\n루트에 있는 주요 문서는 이런 것들이 있습니다.\n${fileSummaries.join('\n')}` : '',
|
||||
sampleFiles.length ? `\n제가 우선 참고할 수 있는 샘플 문서는 다음과 같습니다.\n${sampleFiles.join('\n')}` : '',
|
||||
`\n이 자료는 충분히 도움이 됩니다. 앞으로 질문을 받으면 먼저 이 제2뇌에서 관련 기준이나 맥락을 찾고, 부족할 때만 제 일반 지식으로 보완하겠습니다.`
|
||||
`현재 연결된 제2뇌 **${activeBrain.name}**의 개요입니다.`,
|
||||
'',
|
||||
'### 🧠 지식 베이스 현황',
|
||||
'| 항목 | 정보 |',
|
||||
'| :--- | :--- |',
|
||||
`| **위치** | \`${brainDir}\` |`,
|
||||
`| **문서 총계** | **${files.length}개** |`,
|
||||
activeBrain.description ? `| **설명** | ${activeBrain.description} |` : '| **설명** | 설정된 설명 없음 |',
|
||||
'',
|
||||
'### 📂 주요 카테고리',
|
||||
'| 폴더명 | 문서 수 |',
|
||||
'| :--- | :--- |',
|
||||
...(directorySummaries.length ? directorySummaries : ['| - | - |']),
|
||||
'',
|
||||
fileSummaries.length ? `### 📄 최근/주요 문서\n${fileSummaries.join('\n')}` : '',
|
||||
'',
|
||||
'이 자료들을 기반으로 답변을 최적화하겠습니다. 특정 주제에 대해 깊이 있는 분석이 필요하시면 말씀해 주세요.'
|
||||
].filter(Boolean);
|
||||
|
||||
return sections.join('\n');
|
||||
|
||||
+492
-346
@@ -118,6 +118,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
await this.syncBrain();
|
||||
await this._sendBrainStatus();
|
||||
break;
|
||||
case 'addBrain':
|
||||
await this._addBrainProfile();
|
||||
break;
|
||||
case 'setBrainProfile':
|
||||
await this._setActiveBrainProfile(data.id);
|
||||
break;
|
||||
@@ -644,367 +647,510 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
|
||||
private _getHtml(webview: vscode.Webview): string {
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="ko"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>G1nation</title>
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
:root{
|
||||
--bg:#000000;--bg2:#050505;--surface:rgba(0,18,5,.75);--surface2:rgba(0,35,10,.6);
|
||||
--border:rgba(255,255,255,.08);--border2:rgba(255,255,255,.12);
|
||||
--text:#A1A1AA;--text-bright:#FFFFFF;--text-dim:#71717A;
|
||||
--accent:#00FF41;--accent2:#008F11;--accent3:#00FF41;
|
||||
--accent-glow:rgba(0,255,65,.25);--accent2-glow:rgba(0,143,17,.2);
|
||||
--input-bg:rgba(0,10,2,.9);--code-bg:#020502;
|
||||
--green:#00FF41;--yellow:#ffab40;--cyan:#00e5ff;--red:#ff5252;
|
||||
}
|
||||
body.vscode-light {
|
||||
--bg:#fafafa;--bg2:#ffffff;--surface:rgba(255,255,255,.8);--surface2:rgba(240,240,245,.8);
|
||||
--border:rgba(0,0,0,.08);--border2:rgba(0,0,0,.15);
|
||||
--text:#454555;--text-bright:#111118;--text-dim:#888899;
|
||||
--accent-glow:rgba(124,106,255,.1);--accent2-glow:rgba(224,64,251,.08);
|
||||
--input-bg:rgba(255,255,255,.9);--code-bg:#f5f5f7;
|
||||
}
|
||||
html,body{height:100%;font-family:'SF Pro Display',-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;font-size:13px;background:var(--bg);color:var(--text);display:flex;flex-direction:column;overflow:hidden;min-height:0}
|
||||
body::before{content:'';position:fixed;top:-50%;left:-50%;width:200%;height:200%;background:radial-gradient(ellipse at 20% 50%,rgba(124,106,255,.06) 0%,transparent 50%),radial-gradient(ellipse at 80% 20%,rgba(224,64,251,.04) 0%,transparent 50%),radial-gradient(ellipse at 50% 80%,rgba(0,229,255,.03) 0%,transparent 50%);animation:aurora 20s ease-in-out infinite;z-index:0;pointer-events:none}
|
||||
@keyframes aurora{0%,100%{transform:translate(0,0) rotate(0deg)}33%{transform:translate(2%,-1%) rotate(.5deg)}66%{transform:translate(-1%,2%) rotate(-.5deg)}}
|
||||
.header{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:rgba(10,10,12,.8);backdrop-filter:blur(20px);border-bottom:1px solid var(--border);flex-shrink:0;position:relative;z-index:10}
|
||||
.header::after{content:'';position:absolute;bottom:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent 5%,var(--accent) 30%,var(--accent2) 50%,var(--accent3) 70%,transparent 95%);opacity:.5;animation:headerGlow 4s ease-in-out infinite alternate}
|
||||
@keyframes headerGlow{0%{opacity:.3}100%{opacity:.6}}
|
||||
.thinking-bar{height:2px;background:transparent;position:relative;overflow:hidden;flex-shrink:0;z-index:10}
|
||||
.thinking-bar.active{background:rgba(124,106,255,.1)}
|
||||
.thinking-bar.active::after{content:'';position:absolute;top:0;left:-40%;width:40%;height:100%;background:linear-gradient(90deg,transparent,var(--accent),var(--accent2),var(--accent3),transparent);animation:thinkSlide 1.5s ease-in-out infinite}
|
||||
@keyframes thinkSlide{0%{left:-40%}100%{left:100%}}
|
||||
.brain-strip{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:7px 14px;border-bottom:1px solid var(--border);background:rgba(0,10,2,.64);z-index:8;position:relative}
|
||||
.brain-strip-main{font-size:11px;color:var(--accent);font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.brain-strip-sub{font-size:10px;color:var(--text-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:45%}
|
||||
.header-left{display:flex;align-items:center;gap:8px}
|
||||
.logo{width:26px;height:26px;border-radius:6px;background:#050505;border:1px solid rgba(0,255,65,.3);display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--accent);box-shadow:0 0 15px rgba(0,255,65,.15);animation:logoPulse 3s ease-in-out infinite;position:relative;text-shadow:0 0 8px var(--accent)}
|
||||
@keyframes logoPulse{0%,100%{box-shadow:0 0 10px rgba(0,255,65,.1)}50%{box-shadow:0 0 25px rgba(0,255,65,.3)}}
|
||||
.brand{font-weight:800;font-size:14px;color:var(--text-bright);letter-spacing:-.5px;background:linear-gradient(135deg,#fff 40%,var(--accent) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
||||
.header-right{display:flex;align-items:center;gap:5px;flex-wrap:wrap;justify-content:flex-end}
|
||||
.select-wrap{display:flex;align-items:center;gap:5px}
|
||||
.select-wrap select{max-width:140px}
|
||||
.brain-meta{font-size:10px;color:var(--text-dim);max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.history-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.85);backdrop-filter:blur(15px);z-index:100;display:none;flex-direction:column;padding:20px;animation:fadeIn 0.3s ease-out}
|
||||
.history-overlay.visible{display:flex}
|
||||
.history-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}
|
||||
.history-title{font-size:18px;font-weight:bold;color:var(--accent)}
|
||||
.history-list{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:10px}
|
||||
.history-item{background:rgba(255,255,255,0.05);border:1px solid var(--border);padding:12px;border-radius:10px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:all 0.2s}
|
||||
.history-item:hover{border-color:var(--accent);background:rgba(0,255,65,0.05)}
|
||||
.history-item-info{display:flex;flex-direction:column;gap:4px;flex:1}
|
||||
.history-item-title{font-weight:600;font-size:12px;color:var(--text-bright)}
|
||||
.history-item-date{font-size:10px;color:var(--text-dim)}
|
||||
.history-del-btn{background:transparent;border:none;color:var(--text-dim);cursor:pointer;padding:8px;font-size:14px;transition:color 0.2s}
|
||||
.history-del-btn:hover{color:var(--red)}
|
||||
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
|
||||
select{background:rgba(22,22,28,.9);color:var(--text-bright);border:1px solid var(--border2);padding:5px 8px;border-radius:8px;font-size:10px;cursor:pointer;outline:none;max-width:120px;transition:all .3s;backdrop-filter:blur(8px)}
|
||||
select:hover,select:focus{border-color:var(--accent);box-shadow:0 0 12px var(--accent-glow)}
|
||||
.btn-icon{background:rgba(22,22,28,.7);border:1px solid var(--border2);color:var(--text-dim);width:28px;height:28px;border-radius:8px;font-size:13px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .3s;backdrop-filter:blur(8px);position:relative;overflow:hidden}
|
||||
.btn-icon:hover{color:var(--text-bright);border-color:var(--accent);transform:translateY(-1px);box-shadow:0 4px 15px var(--accent-glow)}
|
||||
.chat{flex:1;overflow-y:auto;padding:16px 14px;display:flex;flex-direction:column;gap:16px;position:relative;z-index:1;min-height:0}
|
||||
.chat::-webkit-scrollbar{width:2px}.chat::-webkit-scrollbar-track{background:transparent}.chat::-webkit-scrollbar-thumb{background:var(--accent);border-radius:2px;opacity:.5}
|
||||
.msg{display:flex;flex-direction:column;gap:5px;animation:msgIn .5s cubic-bezier(.16,1,.3,1)}
|
||||
.msg-head{display:flex;align-items:center;gap:7px;font-weight:600;font-size:11px;color:var(--text)}
|
||||
.msg-time{font-weight:400;font-size:9px;color:var(--text-dim);margin-left:auto;opacity:.6}
|
||||
.av{width:22px;height:22px;border-radius:7px;display:flex;align-items:center;justify-content:center;font-size:11px;flex-shrink:0}
|
||||
.av-user{background:var(--surface2);color:var(--text);border:1px solid var(--border2)}
|
||||
.av-ai{background:linear-gradient(135deg,var(--accent),var(--accent2));color:#fff;box-shadow:0 0 10px rgba(124,106,255,.3)}
|
||||
.msg-body{padding-left:29px;line-height:1.75;color:var(--text);white-space:pre-wrap;word-break:break-word;font-size:13px}
|
||||
.msg-user .msg-body{background:var(--surface);border:1px solid var(--border2);border-radius:14px;padding:10px 14px;margin-left:29px;color:var(--text-bright);backdrop-filter:blur(8px)}
|
||||
.msg-body pre{background:var(--code-bg);border:1px solid var(--border2);border-radius:10px;padding:14px 16px;overflow-x:auto;margin:8px 0;font-size:12px;line-height:1.6;color:#c9d1d9;position:relative}
|
||||
.msg-body code{font-family:'SF Mono','JetBrains Mono','Fira Code','Menlo',monospace;font-size:11.5px}
|
||||
.msg-body :not(pre)>code{background:rgba(124,106,255,.1);color:var(--accent);padding:2px 7px;border-radius:5px;border:1px solid rgba(124,106,255,.15)}
|
||||
.code-wrap{position:relative}
|
||||
.code-lang{position:absolute;top:0;left:14px;background:linear-gradient(135deg,var(--accent),var(--accent2));color:#fff;padding:2px 10px;border-radius:0 0 6px 6px;font-size:9px;font-family:'SF Mono',monospace;text-transform:uppercase;letter-spacing:.5px;font-weight:600}
|
||||
.copy-btn{position:absolute;top:8px;right:8px;background:var(--surface2);border:1px solid var(--border2);color:var(--text-dim);padding:4px 12px;border-radius:6px;font-size:10px;cursor:pointer;opacity:0;transition:all .3s;z-index:1;backdrop-filter:blur(8px)}
|
||||
.code-wrap:hover .copy-btn{opacity:1}.copy-btn:hover{background:var(--accent);color:#fff;border-color:var(--accent);box-shadow:0 0 12px var(--accent-glow)}
|
||||
.welcome{text-align:center;padding:0 20px 20px;position:relative}
|
||||
.welcome-logo{width:56px;height:56px;border-radius:16px;margin:0 auto 16px;background:#050505;border:1px solid rgba(0,255,65,.3);display:flex;align-items:center;justify-content:center;font-size:32px;color:var(--accent);box-shadow:inset 0 0 15px rgba(0,255,65,.1), 0 0 30px rgba(0,255,65,.2);animation:welcomeFloat 4s ease-in-out infinite;position:relative;text-shadow:0 0 15px var(--accent)}
|
||||
@keyframes welcomeFloat{0%,100%{transform:translateY(0) scale(1)}50%{transform:translateY(-6px) scale(1.03)}}
|
||||
.welcome-title{font-size:22px;font-weight:900;letter-spacing:-1px;color:var(--text-bright);margin-bottom:8px}
|
||||
.welcome-sub{color:var(--text-dim);font-size:12px;line-height:1.7;margin-bottom:18px;letter-spacing:-.2px}
|
||||
.loading-wrap{padding-left:29px;padding-top:6px;display:flex;align-items:center;gap:10px}
|
||||
.loading-dots{display:flex;gap:4px}
|
||||
.loading-dots span{width:6px;height:6px;border-radius:50%;background:var(--accent);animation:dotBounce 1.4s ease-in-out infinite}
|
||||
.loading-dots span:nth-child(2){animation-delay:.2s;background:var(--accent2)}
|
||||
.loading-dots span:nth-child(3){animation-delay:.4s;background:var(--accent3)}
|
||||
@keyframes dotBounce{0%,80%,100%{transform:scale(.6);opacity:.4}40%{transform:scale(1.2);opacity:1}}
|
||||
.loading-text{font-size:11px;color:var(--text-dim);animation:pulse 2s ease-in-out infinite;letter-spacing:.3px}
|
||||
.auto-status{margin:12px 0 4px 29px;padding:10px 12px;border:1px solid rgba(255,255,255,.16);border-radius:10px;background:rgba(0,255,65,.08);color:#fff;font-size:13px;font-weight:700;line-height:1.5;box-shadow:0 0 18px rgba(0,255,65,.12)}
|
||||
.input-wrap{padding:8px 14px 14px;flex-shrink:0;position:relative;z-index:1}
|
||||
.input-box{background:var(--input-bg);border:1px solid var(--border2);border-radius:14px;padding:12px 14px;display:flex;flex-direction:column;gap:8px;transition:all .3s;position:relative;backdrop-filter:blur(12px)}
|
||||
.input-box:focus-within{border-color:var(--accent);box-shadow:0 0 24px rgba(0,255,65,.15)}
|
||||
textarea{width:100%;background:transparent;border:none;color:var(--text-bright);font-family:inherit;font-size:13px;line-height:1.5;resize:none;outline:none;min-height:22px;max-height:150px}
|
||||
textarea::placeholder{color:var(--text-dim)}
|
||||
.input-footer{display:flex;align-items:center;justify-content:space-between}
|
||||
.input-hint{font-size:10px;color:var(--text-dim);opacity:.5}
|
||||
.input-btns{display:flex;gap:5px}
|
||||
.send-btn{background:linear-gradient(135deg,var(--accent),var(--accent2));border:none;color:#fff;width:32px;height:32px;border-radius:10px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:14px;transition:all .2s;box-shadow:0 2px 12px rgba(124,106,255,.35);position:relative;overflow:hidden}
|
||||
.send-btn:hover{transform:translateY(-2px) scale(1.05);box-shadow:0 6px 24px rgba(124,106,255,.45)}
|
||||
.send-btn:active{transform:scale(.92)}.send-btn:disabled{opacity:.2;cursor:not-allowed}
|
||||
.stop-btn{background:var(--red);border:none;color:#fff;width:32px;height:32px;border-radius:10px;cursor:pointer;display:none;align-items:center;justify-content:center;font-size:11px;box-shadow:0 0 12px rgba(255,82,82,.3)}
|
||||
.stop-btn.visible{display:flex}
|
||||
@keyframes msgIn{from{opacity:0;transform:translateY(12px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}
|
||||
@keyframes pulse{0%,100%{opacity:.4}50%{opacity:1}}
|
||||
.stream-active::after{content:'';display:inline-block;width:2px;height:14px;background:var(--accent);margin-left:2px;animation:blink .6s step-end infinite;vertical-align:text-bottom;box-shadow:0 0 6px var(--accent)}
|
||||
@keyframes blink{0%,100%{opacity:1}50%{opacity:0}}
|
||||
.main-view{flex:1;display:flex;flex-direction:column;overflow:hidden;transition:all .5s cubic-bezier(.16,1,.3,1);min-height:0;max-height:100%}
|
||||
body.init .main-view{justify-content:center;margin-top:-6vh}
|
||||
body.init .chat{flex:0 0 auto;overflow:visible;padding-bottom:15px}
|
||||
body.init .input-wrap{max-width:680px;width:100%;margin:0 auto}
|
||||
.attach-btn{background:transparent;border:1px solid var(--border2);color:var(--text-dim);width:32px;height:32px;border-radius:10px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:16px;transition:all .3s;flex-shrink:0}
|
||||
.attach-btn:hover{color:var(--accent);border-color:var(--accent);box-shadow:0 0 12px var(--accent-glow)}
|
||||
.attach-preview{display:none;gap:6px;padding:0 0 6px;flex-wrap:wrap}
|
||||
.attach-preview.visible{display:flex}
|
||||
.attach-chip{display:flex;align-items:center;gap:5px;background:var(--surface2);border:1px solid var(--border2);border-radius:8px;padding:4px 10px;font-size:10px;color:var(--text)}
|
||||
.attach-chip .chip-remove{cursor:pointer;color:var(--text-dim);font-size:12px;margin-left:2px}
|
||||
.attach-chip .chip-remove:hover{color:var(--red)}
|
||||
.attach-thumb{width:28px;height:28px;border-radius:5px;object-fit:cover;border:1px solid var(--border2)}
|
||||
.regen-btn{display:inline-flex;align-items:center;gap:4px;background:transparent;border:none;color:var(--text-dim);padding:4px 6px;border-radius:4px;font-size:11px;cursor:pointer;transition:color 0.2s;margin-top:6px;margin-left:29px;opacity:0.7}
|
||||
.regen-btn:hover{color:var(--text);opacity:1}
|
||||
</style></head><body class="init">
|
||||
<div class="header"><div class="header-left"><div class="logo">✦</div><span class="brand">G1nation</span></div><div class="header-right"><div id="engineStatusDot" style="width:8px;height:8px;border-radius:50%;background:var(--text-dim);margin-right:-2px" title="Checking AI Engine..."></div><div class="select-wrap"><select id="modelSel" title="Active AI model"></select><button class="btn-icon" id="refreshModelsBtn" title="Refresh Models">↻</button></div><div class="select-wrap"><select id="brainSel" title="Active Second Brain"></select><button class="btn-icon" id="manageBrainsBtn" title="Add or manage brains">✎</button></div><button class="btn-icon" id="internetBtn" title="Internet Access: OFF">🌐</button><button class="btn-icon" id="brainBtn" title="Sync Second Brain">🧠<span id="brainCountBadge" style="position:absolute;top:-5px;right:-5px;background:var(--accent);color:#000;font-size:8px;padding:1px 3px;border-radius:4px;display:none;font-weight:bold">0</span></button><button class="btn-icon" id="historyBtn" title="Chat History">📜</button><button class="btn-icon" id="settingsBtn" title="Settings">⚙️</button><button class="btn-icon" id="newChatBtn" title="New Chat">+</button></div></div>
|
||||
<div id="historyOverlay" class="history-overlay">
|
||||
<div class="history-header"><span class="history-title">Chat Sessions</span><button class="btn-icon" id="closeHistoryBtn">✕</button></div>
|
||||
<div class="history-list" id="historyList"></div>
|
||||
</div>
|
||||
<div class="thinking-bar" id="thinkingBar"></div>
|
||||
<div class="brain-strip"><div id="brainStatusInfo" class="brain-strip-main">Brain: checking...</div><div id="brainStatusMeta" class="brain-strip-sub"></div></div>
|
||||
<div class="main-view" id="mainView">
|
||||
<div class="chat" id="chat">
|
||||
<div class="welcome">
|
||||
<div class="welcome-logo">✦</div>
|
||||
<div class="welcome-title">G1nation</div>
|
||||
<div class="welcome-sub">Security · Optimized · Knowledge Mesh<br>Choose a brain above, then ask what it knows.</div>
|
||||
</div></div>
|
||||
<div class="input-wrap"><div class="input-box">
|
||||
<div class="attach-preview" id="attachPreview"></div>
|
||||
<textarea id="input" rows="1" placeholder="What shall we build today?"></textarea>
|
||||
<div class="input-footer"><span class="input-hint">Enter to Send · Shift+Enter for New Line</span>
|
||||
<div class="input-btns"><button class="attach-btn" id="attachBtn" title="Attach Files">+</button><button class="stop-btn" id="stopBtn">■</button><button class="send-btn" id="sendBtn">↑</button></div></div></div>
|
||||
<input type="file" id="fileInput" multiple accept="image/*,.txt,.md,.csv,.json,.js,.ts,.jsx,.tsx,.html,.css,.py,.java,.rs,.go,.yaml,.yml,.xml,.toml,.sql,.sh" hidden></div>
|
||||
</div>
|
||||
<script>
|
||||
try {
|
||||
const vscode=acquireVsCodeApi(),chat=document.getElementById('chat'),input=document.getElementById('input'),
|
||||
sendBtn=document.getElementById('sendBtn'),stopBtn=document.getElementById('stopBtn'),
|
||||
modelSel=document.getElementById('modelSel'),brainSel=document.getElementById('brainSel'),refreshModelsBtn=document.getElementById('refreshModelsBtn'),manageBrainsBtn=document.getElementById('manageBrainsBtn'),newChatBtn=document.getElementById('newChatBtn'),settingsBtn=document.getElementById('settingsBtn'),brainBtn=document.getElementById('brainBtn'),
|
||||
historyBtn=document.getElementById('historyBtn'),historyOverlay=document.getElementById('historyOverlay'),closeHistoryBtn=document.getElementById('closeHistoryBtn'),historyList=document.getElementById('historyList'),
|
||||
internetBtn=document.getElementById('internetBtn'),attachBtn=document.getElementById('attachBtn'),fileInput=document.getElementById('fileInput'),attachPreview=document.getElementById('attachPreview'),
|
||||
thinkingBar=document.getElementById('thinkingBar');
|
||||
let loader=null,sending=false,pendingFiles=[],internetEnabled=false;
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>G1nation</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0d1117;
|
||||
--bg-secondary: #0d1117;
|
||||
--surface: #161b22;
|
||||
--border: #30363d;
|
||||
--border-bright: #484f58;
|
||||
--text-primary: #c9d1d9;
|
||||
--text-bright: #ffffff;
|
||||
--text-dim: #8b949e;
|
||||
--accent: #58a6ff;
|
||||
--accent-glow: rgba(88, 166, 255, 0.15);
|
||||
--success: #238636;
|
||||
--warning: #d29922;
|
||||
--error: #f85149;
|
||||
--code-bg: #161b22;
|
||||
--table-header-bg: #161b22;
|
||||
--table-row-hover: #21262d;
|
||||
--input-bg: #0d1117;
|
||||
}
|
||||
|
||||
historyBtn.addEventListener('click', ()=>historyOverlay.classList.add('visible'));
|
||||
closeHistoryBtn.addEventListener('click', ()=>historyOverlay.classList.remove('visible'));
|
||||
refreshModelsBtn.addEventListener('click', ()=>vscode.postMessage({type:'refreshModels'}));
|
||||
manageBrainsBtn.addEventListener('click', ()=>vscode.postMessage({type:'manageBrains'}));
|
||||
brainSel.addEventListener('change', ()=>vscode.postMessage({type:'setBrainProfile',id:brainSel.value}));
|
||||
body.vscode-light {
|
||||
--bg: #ffffff;
|
||||
--bg-secondary: #f6f8fa;
|
||||
--surface: #ffffff;
|
||||
--border: #d0d7de;
|
||||
--border-bright: #afb8c1;
|
||||
--text-primary: #24292f;
|
||||
--text-bright: #111118;
|
||||
--text-dim: #57606a;
|
||||
--accent: #0969da;
|
||||
--accent-glow: rgba(9, 105, 218, 0.1);
|
||||
--success: #1a7f37;
|
||||
--warning: #9a6700;
|
||||
--error: #cf222e;
|
||||
--code-bg: #f6f8fa;
|
||||
--table-header-bg: #f6f8fa;
|
||||
--table-row-hover: #f3f4f6;
|
||||
--input-bg: #ffffff;
|
||||
}
|
||||
|
||||
internetBtn.addEventListener('click', ()=>{
|
||||
internetEnabled=!internetEnabled;
|
||||
internetBtn.style.opacity=internetEnabled?'1':'0.4';
|
||||
internetBtn.style.filter=internetEnabled?'none':'grayscale(1)';
|
||||
});
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
background: var(--bg);
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
vscode.postMessage({type:'getModels'});
|
||||
setTimeout(()=>vscode.postMessage({type:'ready'}),300);
|
||||
input.addEventListener('input',()=>{input.style.height='auto';input.style.height=Math.min(input.scrollHeight,150)+'px'});
|
||||
function getTime(){return new Date().toLocaleTimeString('en-US',{hour:'2-digit',minute:'2-digit'})}
|
||||
function esc(s){const d=document.createElement('div');d.innerText=s;return d.innerHTML}
|
||||
/* --- Header --- */
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--bg-secondary);
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
function fmt(t){
|
||||
let formatted = esc(t);
|
||||
formatted = formatted.replace(/\`\`\`([\s\S]*?)\`\`\`/g, '<pre><code>$1</code></pre>');
|
||||
formatted = formatted.replace(/\`([^\`]+)\`/g, '<code>$1</code>');
|
||||
return formatted;
|
||||
}
|
||||
.header-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
|
||||
function normalizeMsgContent(content){
|
||||
if(Array.isArray(content)) return content.map(part=>typeof part==='string'?part:(part?.text||part?.name||JSON.stringify(part))).join('\\n');
|
||||
return String(content||'');
|
||||
}
|
||||
.header-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 0 14px 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
function resetChatView(){
|
||||
chat.innerHTML='';
|
||||
streamBody=null;hideLoader();setSending(false);
|
||||
document.body.classList.remove('init');
|
||||
}
|
||||
.brand { font-weight: 700; font-size: 14px; color: var(--text-bright); letter-spacing: -0.5px; display: flex; align-items: center; gap: 8px; }
|
||||
.logo { width: 22px; height: 22px; background: var(--accent); color: #fff; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 900; }
|
||||
|
||||
function renderHistory(history){
|
||||
resetChatView();
|
||||
(history||[]).filter(m => !m.internal).forEach(m => addMsg(normalizeMsgContent(m.content), m.role === 'assistant' ? 'ai' : 'user'));
|
||||
if((history||[]).length===0){
|
||||
chat.innerHTML='<div class="welcome"><div class="welcome-logo">✦</div><div class="welcome-title">G1nation</div><div class="welcome-sub">No messages in this session.</div></div>';
|
||||
document.body.classList.add('init');
|
||||
}
|
||||
chat.scrollTop=chat.scrollHeight;
|
||||
}
|
||||
.chat {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
function addMsg(text,role){
|
||||
const isUser=role==='user',isErr=role==='error';
|
||||
const el=document.createElement('div');el.className='msg'+(isUser?' msg-user':'')+(isErr?' msg-error':'');
|
||||
const head=document.createElement('div');head.className='msg-head';
|
||||
head.innerHTML=(isUser?'<div class="av av-user">👤</div><span>You</span>':'<div class="av av-ai">✦</div><span>G1nation</span>')+'<span class="msg-time">'+getTime()+'</span>';
|
||||
const body=document.createElement('div');body.className='msg-body';
|
||||
if(isUser){body.innerText=text}else{body.innerHTML=fmt(text)}
|
||||
el.appendChild(head);el.appendChild(body);chat.appendChild(el);chat.scrollTop=chat.scrollHeight;
|
||||
}
|
||||
/* --- Messages --- */
|
||||
.msg {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
animation: msgIn 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
function showLoader(){thinkingBar.classList.add('active')}
|
||||
function hideLoader(){thinkingBar.classList.remove('active')}
|
||||
function setSending(v){sending=v;sendBtn.disabled=v;stopBtn.classList.toggle('visible',v);input.disabled=v;}
|
||||
@keyframes msgIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
function send(){
|
||||
const text=input.value.trim();
|
||||
if(!text && pendingFiles.length===0) return;
|
||||
document.body.classList.remove('init');
|
||||
const w=document.querySelector('.welcome');if(w)w.remove();
|
||||
addMsg(text,'user');
|
||||
input.value='';input.style.height='auto';setSending(true);showLoader();
|
||||
vscode.postMessage({type:'prompt',value:text,model:modelSel.value,internet:internetEnabled,files:pendingFiles.length?pendingFiles:undefined});
|
||||
pendingFiles=[];renderPreview();
|
||||
}
|
||||
.msg-head { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 11px; color: var(--text-dim); }
|
||||
.av { width: 22px; height: 22px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 12px; }
|
||||
.av-user { background: var(--border); color: var(--text-primary); }
|
||||
.av-ai { background: var(--accent); color: #fff; }
|
||||
|
||||
attachBtn.addEventListener('click',()=>fileInput.click());
|
||||
fileInput.addEventListener('change',()=>{
|
||||
const files=Array.from(fileInput.files);
|
||||
files.forEach(file=>{
|
||||
const reader=new FileReader();
|
||||
reader.onload=()=>{
|
||||
const base64=reader.result.split(',')[1];
|
||||
pendingFiles.push({name:file.name,type:file.type,data:base64});
|
||||
renderPreview();
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
});
|
||||
.msg-body {
|
||||
padding-left: 30px;
|
||||
color: var(--text-primary);
|
||||
font-size: 13.5px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
function renderPreview(){
|
||||
attachPreview.innerHTML='';
|
||||
if(pendingFiles.length===0){attachPreview.classList.remove('visible');return;}
|
||||
attachPreview.classList.add('visible');
|
||||
pendingFiles.forEach((f,i)=>{
|
||||
const chip=document.createElement('div');chip.className='attach-chip';
|
||||
chip.innerHTML='<span>📎</span><span class="chip-name">'+f.name+'</span><span class="chip-remove">✕</span>';
|
||||
chip.querySelector('.chip-remove').addEventListener('click',()=>{pendingFiles.splice(i,1);renderPreview();});
|
||||
attachPreview.appendChild(chip);
|
||||
});
|
||||
}
|
||||
.msg-user .msg-body {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 10px 14px;
|
||||
margin-left: 30px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
sendBtn.addEventListener('click',send);
|
||||
input.addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();send()}});
|
||||
newChatBtn.addEventListener('click',()=>vscode.postMessage({type:'newChat'}));
|
||||
settingsBtn.addEventListener('click',()=>vscode.postMessage({type:'openSettings'}));
|
||||
brainBtn.addEventListener('click',()=>vscode.postMessage({type:'syncBrain'}));
|
||||
stopBtn.addEventListener('click',()=>{vscode.postMessage({type:'stopGeneration'});hideLoader();setSending(false);});
|
||||
/* --- Markdown Style --- */
|
||||
.markdown-body h1, .markdown-body h2, .markdown-body h3 {
|
||||
color: var(--text-bright);
|
||||
margin: 1.5em 0 0.8em;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
let streamBody=null,sessionCache={};
|
||||
window.addEventListener('message',e=>{const msg=e.data;switch(msg.type){
|
||||
case 'restoreHistory':
|
||||
renderHistory(msg.value||[]);
|
||||
break;
|
||||
case 'sessionLoaded':
|
||||
renderHistory(msg.value.history||[]);
|
||||
historyOverlay.classList.remove('visible');
|
||||
break;
|
||||
case 'sessionList':
|
||||
historyList.innerHTML='';
|
||||
sessionCache={};
|
||||
if(!msg.value || msg.value.length===0){
|
||||
historyList.innerHTML='<div class="history-item"><div class="history-item-info"><div class="history-item-title">No saved chats yet</div><div class="history-item-date">Start a conversation and it will appear here.</div></div></div>';
|
||||
break;
|
||||
}
|
||||
msg.value.forEach(s=>{
|
||||
sessionCache[s.id]=s;
|
||||
const el=document.createElement('div');el.className='history-item';
|
||||
el.dataset.sessionId=s.id;
|
||||
el.innerHTML='<div class="history-item-info"><div class="history-item-title">'+esc(s.title)+'</div><div class="history-item-date">'+new Date(s.timestamp).toLocaleString()+' · '+(s.messageCount||0)+' messages</div></div><button class="history-del-btn" title="Delete">🗑</button>';
|
||||
el.addEventListener('click',(e)=>{
|
||||
if(e.target.closest('.history-del-btn')) return;
|
||||
const cached=sessionCache[s.id];
|
||||
if(cached && cached.history){renderHistory(cached.history);historyOverlay.classList.remove('visible');}
|
||||
vscode.postMessage({type:'loadSession',id:s.id});
|
||||
.markdown-body h1 { font-size: 1.5em; border-bottom: 1px solid var(--border); padding-bottom: 0.3em; color: var(--accent); }
|
||||
.markdown-body h2 { font-size: 1.3em; border-bottom: 1px solid var(--border); padding-bottom: 0.3em; }
|
||||
.markdown-body table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1.2em 0;
|
||||
font-size: 12px;
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.markdown-body th { background: var(--table-header-bg); color: var(--accent); font-weight: 600; text-align: left; padding: 10px 12px; border: 1px solid var(--border); }
|
||||
.markdown-body td { padding: 8px 12px; border: 1px solid var(--border); color: var(--text-primary); }
|
||||
.markdown-body tr:nth-child(even) { background: rgba(255, 255, 255, 0.02); }
|
||||
|
||||
.markdown-body pre { background: var(--code-bg); border: 1px solid var(--border); border-radius: 8px; padding: 14px; overflow-x: auto; margin: 12px 0; }
|
||||
.markdown-body code { font-family: 'SF Mono', monospace; font-size: 11.5px; background: rgba(175, 184, 193, 0.2); padding: 0.2em 0.4em; border-radius: 4px; }
|
||||
|
||||
/* --- UI Elements --- */
|
||||
.copy-btn {
|
||||
position: absolute; top: 0; right: 0; background: var(--bg-secondary); border: 1px solid var(--border);
|
||||
color: var(--text-dim); padding: 4px 10px; border-radius: 6px; font-size: 10px; cursor: pointer; opacity: 0; transition: 0.2s;
|
||||
z-index: 20;
|
||||
}
|
||||
.msg:hover .copy-btn { opacity: 1; }
|
||||
.copy-btn:hover { color: var(--text-bright); border-color: var(--accent); background: var(--accent-glow); }
|
||||
|
||||
.icon-btn {
|
||||
background: var(--surface); border: 1px solid var(--border); color: var(--text-dim); width: 28px; height: 28px;
|
||||
border-radius: 6px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: 0.2s; font-size: 13px;
|
||||
}
|
||||
.icon-btn:hover { color: var(--text-bright); border-color: var(--accent); background: var(--accent-glow); }
|
||||
.icon-btn.active { color: var(--accent); border-color: var(--accent); background: var(--accent-glow); }
|
||||
|
||||
select { background: var(--surface); color: var(--text-primary); border: 1px solid var(--border); padding: 4px 8px; border-radius: 6px; font-size: 10.5px; cursor: pointer; }
|
||||
|
||||
/* --- Input & Attachments --- */
|
||||
.input-wrap {
|
||||
padding: 12px 14px 16px; background: var(--bg); border-top: 1px solid var(--border); flex-shrink: 0;
|
||||
}
|
||||
|
||||
.input-box {
|
||||
background: var(--input-bg); border: 1px solid var(--border); border-radius: 12px; padding: 10px 14px;
|
||||
display: flex; flex-direction: column; gap: 8px; transition: 0.2s;
|
||||
}
|
||||
.input-box:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }
|
||||
|
||||
textarea {
|
||||
width: 100%; background: transparent; border: none; color: var(--text-bright);
|
||||
font-family: inherit; font-size: 13.5px; resize: none; outline: none; min-height: 24px; max-height: 160px;
|
||||
}
|
||||
|
||||
.attachment-preview {
|
||||
display: none; gap: 8px; padding-bottom: 8px; border-bottom: 1px solid var(--border); flex-wrap: wrap;
|
||||
}
|
||||
.attachment-preview.visible { display: flex; }
|
||||
|
||||
.file-chip {
|
||||
display: flex; align-items: center; gap: 6px; background: var(--surface); border: 1px solid var(--border);
|
||||
padding: 4px 8px; border-radius: 6px; font-size: 11px; color: var(--text-primary);
|
||||
}
|
||||
.file-chip .remove { cursor: pointer; color: var(--text-dim); }
|
||||
.file-chip .remove:hover { color: var(--error); }
|
||||
|
||||
.input-footer { display: flex; align-items: center; justify-content: space-between; }
|
||||
.footer-left { display: flex; align-items: center; gap: 8px; }
|
||||
|
||||
.send-btn {
|
||||
background: var(--accent); color: #fff; border: none; padding: 6px 14px; border-radius: 6px;
|
||||
font-weight: 600; font-size: 12px; cursor: pointer;
|
||||
}
|
||||
.send-btn:disabled { opacity: 0.3; cursor: not-allowed; }
|
||||
|
||||
/* --- Overlays & Others --- */
|
||||
.thinking-bar { height: 2px; background: transparent; position: relative; overflow: hidden; margin-top: -1px; }
|
||||
.thinking-bar.active::after {
|
||||
content: ''; position: absolute; top: 0; left: -40%; width: 40%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, var(--accent), transparent); animation: think 1.5s infinite;
|
||||
}
|
||||
@keyframes think { 0% { left: -40%; } 100% { left: 100%; } }
|
||||
|
||||
.history-overlay {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8);
|
||||
backdrop-filter: blur(10px); z-index: 1000; display: none; flex-direction: column; padding: 20px;
|
||||
}
|
||||
.history-overlay.visible { display: flex; }
|
||||
|
||||
.stream-active::after {
|
||||
content: ''; display: inline-block; width: 6px; height: 14px; background: var(--accent);
|
||||
margin-left: 4px; animation: blink 0.8s step-end infinite; vertical-align: middle;
|
||||
}
|
||||
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
|
||||
|
||||
.welcome { text-align: center; padding: 40px 20px; color: var(--text-dim); }
|
||||
.welcome-logo { font-size: 48px; color: var(--accent); margin-bottom: 16px; opacity: 0.8; }
|
||||
.welcome-title { font-size: 20px; font-weight: 700; color: var(--text-bright); margin-bottom: 8px; }
|
||||
|
||||
/* --- History List --- */
|
||||
.history-item {
|
||||
padding: 12px; border-radius: 8px; background: var(--surface); border: 1px solid var(--border);
|
||||
margin-bottom: 10px; cursor: pointer; transition: 0.2s;
|
||||
}
|
||||
.history-item:hover { border-color: var(--accent); background: var(--accent-glow); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="header-top">
|
||||
<div class="brand"><div class="logo">✦</div> G1nation</div>
|
||||
<div style="display: flex; gap: 6px;">
|
||||
<button class="icon-btn" id="newChatBtn" title="New Chat">+</button>
|
||||
<button class="icon-btn" id="settingsBtn" title="Settings">⚙️</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-controls">
|
||||
<div id="statusDot" style="width:6px; height:6px; border-radius:50%; background:var(--text-dim);"></div>
|
||||
<select id="modelSel" title="Select Model"></select>
|
||||
<select id="brainSel" title="Select Brain"></select>
|
||||
<button class="icon-btn" id="internetBtn" title="Internet Access">🌐</button>
|
||||
<button class="icon-btn" id="brainBtn" title="Sync Knowledge">🧠</button>
|
||||
<button class="icon-btn" id="historyBtn" title="History">📜</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="historyOverlay" class="history-overlay">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
|
||||
<h2 style="color:var(--text-bright);">Chat History</h2>
|
||||
<button class="icon-btn" id="closeHistoryBtn">✕</button>
|
||||
</div>
|
||||
<div id="historyList" style="flex:1; overflow-y:auto;"></div>
|
||||
</div>
|
||||
|
||||
<div class="thinking-bar" id="thinkingBar"></div>
|
||||
|
||||
<div class="chat" id="chat">
|
||||
<div class="welcome">
|
||||
<div class="welcome-logo">✦</div>
|
||||
<div class="welcome-title">Welcome to G1nation</div>
|
||||
<p>Your premium local AI assistant.<br>Ready to analyze projects and build reports.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-wrap">
|
||||
<div class="input-box">
|
||||
<div id="attachPreview" class="attachment-preview"></div>
|
||||
<textarea id="input" rows="1" placeholder="Type your request..."></textarea>
|
||||
<div class="input-footer">
|
||||
<div class="footer-left">
|
||||
<button class="icon-btn" id="attachBtn" title="Attach Files">📎</button>
|
||||
<span id="statusLabel" style="font-size:10px; color:var(--text-dim);">Ready</span>
|
||||
</div>
|
||||
<button id="sendBtn" class="send-btn">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="file" id="fileInput" multiple hidden accept="image/*,.txt,.md,.pdf,.csv,.json,.js,.ts,.py,.java,.rs,.go">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const vscode = acquireVsCodeApi();
|
||||
const chat = document.getElementById('chat');
|
||||
const input = document.getElementById('input');
|
||||
const sendBtn = document.getElementById('sendBtn');
|
||||
const thinkingBar = document.getElementById('thinkingBar');
|
||||
const statusLabel = document.getElementById('statusLabel');
|
||||
const modelSel = document.getElementById('modelSel');
|
||||
const brainSel = document.getElementById('brainSel');
|
||||
const historyOverlay = document.getElementById('historyOverlay');
|
||||
const historyList = document.getElementById('historyList');
|
||||
const statusDot = document.getElementById('statusDot');
|
||||
const attachBtn = document.getElementById('attachBtn');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const attachPreview = document.getElementById('attachPreview');
|
||||
|
||||
let streamBody = null;
|
||||
let internetEnabled = false;
|
||||
let pendingFiles = [];
|
||||
|
||||
function fmt(text) { return marked.parse(text || ''); }
|
||||
|
||||
function copyToClipboard(text, btn) {
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text; textarea.style.position = 'fixed'; textarea.style.opacity = '0';
|
||||
document.body.appendChild(textarea); textarea.select();
|
||||
try {
|
||||
if (document.execCommand('copy')) {
|
||||
btn.innerText = '✅ Copied!'; setTimeout(() => { btn.innerText = '📋 Copy'; }, 2000);
|
||||
}
|
||||
} catch (err) { console.error('Copy failed', err); }
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
|
||||
function addMsg(text, role) {
|
||||
const isUser = role === 'user';
|
||||
const msgEl = document.createElement('div');
|
||||
msgEl.className = 'msg ' + (isUser ? 'msg-user' : 'msg-ai');
|
||||
msgEl._raw = text;
|
||||
|
||||
const head = document.createElement('div');
|
||||
head.className = 'msg-head';
|
||||
head.innerHTML = isUser ? '<div class="av av-user">U</div> You' : '<div class="av av-ai">✦</div> G1nation';
|
||||
|
||||
const body = document.createElement('div');
|
||||
body.className = 'msg-body markdown-body';
|
||||
|
||||
if (isUser) {
|
||||
body.innerText = text;
|
||||
} else {
|
||||
body.innerHTML = fmt(text);
|
||||
const copyBtn = document.createElement('button');
|
||||
copyBtn.className = 'copy-btn'; copyBtn.innerText = '📋 Copy';
|
||||
copyBtn.onclick = (e) => { e.stopPropagation(); copyToClipboard(msgEl._raw, copyBtn); };
|
||||
msgEl.appendChild(copyBtn);
|
||||
}
|
||||
|
||||
msgEl.appendChild(head); msgEl.appendChild(body);
|
||||
chat.appendChild(msgEl); chat.scrollTop = chat.scrollHeight;
|
||||
return { body, msgEl };
|
||||
}
|
||||
|
||||
window.addEventListener('message', e => {
|
||||
const msg = e.data;
|
||||
switch(msg.type) {
|
||||
case 'streamStart':
|
||||
thinkingBar.classList.remove('active');
|
||||
if (document.querySelector('.welcome')) document.querySelector('.welcome').remove();
|
||||
const res = addMsg('', 'assistant');
|
||||
streamBody = res.body; streamBody._parent = res.msgEl; streamBody._parent._raw = '';
|
||||
streamBody.classList.add('stream-active');
|
||||
break;
|
||||
case 'streamChunk':
|
||||
if (streamBody) {
|
||||
streamBody._parent._raw += msg.value;
|
||||
streamBody.innerHTML = fmt(streamBody._parent._raw);
|
||||
chat.scrollTop = chat.scrollHeight;
|
||||
}
|
||||
break;
|
||||
case 'streamEnd':
|
||||
if (streamBody) streamBody.classList.remove('stream-active');
|
||||
streamBody = null; sendBtn.disabled = false;
|
||||
break;
|
||||
case 'restoreHistory':
|
||||
case 'sessionLoaded':
|
||||
const history = msg.type === 'sessionLoaded' ? msg.value.history : msg.value;
|
||||
if (history && history.length > 0) {
|
||||
chat.innerHTML = '';
|
||||
history.forEach(m => addMsg(m.content, m.role === 'assistant' ? 'assistant' : 'user'));
|
||||
}
|
||||
historyOverlay.classList.remove('visible');
|
||||
break;
|
||||
case 'clearChat':
|
||||
chat.innerHTML = '<div class="welcome"><div class="welcome-logo">✦</div><div class="welcome-title">Welcome to G1nation</div><p>Your premium local AI assistant.<br>Ready to analyze projects and build reports.</p></div>';
|
||||
break;
|
||||
case 'focusInput':
|
||||
input.focus();
|
||||
break;
|
||||
case 'modelsList':
|
||||
modelSel.innerHTML = '';
|
||||
msg.value.models.forEach(m => {
|
||||
const o = document.createElement('option'); o.value = m; o.innerText = m;
|
||||
if (m === msg.value.selected) o.selected = true;
|
||||
modelSel.appendChild(o);
|
||||
});
|
||||
statusLabel.innerText = \`Model: \${msg.value.selected}\`;
|
||||
break;
|
||||
case 'brainProfiles':
|
||||
brainSel.innerHTML = '';
|
||||
msg.value.profiles.forEach(p => {
|
||||
const o = document.createElement('option'); o.value = p.id; o.innerText = p.name;
|
||||
if (p.id === msg.value.activeBrainId) o.selected = true;
|
||||
brainSel.appendChild(o);
|
||||
});
|
||||
const addOpt = document.createElement('option');
|
||||
addOpt.value = 'new'; addOpt.innerText = '+ Add New Brain...';
|
||||
brainSel.appendChild(addOpt);
|
||||
break;
|
||||
case 'sessionList':
|
||||
historyList.innerHTML = '';
|
||||
msg.value.forEach(s => {
|
||||
const el = document.createElement('div'); el.className = 'history-item';
|
||||
el.innerHTML = \`<div style="font-weight:600; color:var(--text-bright); mb-2">\${s.title}</div><div style="font-size:10px; color:var(--text-dim)">\${new Date(s.timestamp).toLocaleString()} · \${s.messageCount} msgs</div>\`;
|
||||
el.onclick = () => vscode.postMessage({ type: 'loadSession', id: s.id });
|
||||
historyList.appendChild(el);
|
||||
});
|
||||
break;
|
||||
case 'engineStatus':
|
||||
statusDot.style.background = msg.value.online ? 'var(--success)' : 'var(--error)';
|
||||
break;
|
||||
case 'autoContinue':
|
||||
statusLabel.innerText = msg.value; thinkingBar.classList.add('active');
|
||||
setTimeout(() => { thinkingBar.classList.remove('active'); }, 3000);
|
||||
break;
|
||||
case 'error':
|
||||
thinkingBar.classList.remove('active'); sendBtn.disabled = false;
|
||||
addMsg(msg.value, 'error');
|
||||
break;
|
||||
}
|
||||
});
|
||||
el.querySelector('.history-del-btn').addEventListener('click',()=>{
|
||||
vscode.postMessage({type:'deleteSession',id:s.id});
|
||||
|
||||
function renderAttachments() {
|
||||
attachPreview.innerHTML = '';
|
||||
if (pendingFiles.length === 0) { attachPreview.classList.remove('visible'); return; }
|
||||
attachPreview.classList.add('visible');
|
||||
pendingFiles.forEach((f, i) => {
|
||||
const chip = document.createElement('div'); chip.className = 'file-chip';
|
||||
chip.innerHTML = \`<span>📎</span> \${f.name} <span class="remove" onclick="removeFile(\${i})">✕</span>\`;
|
||||
attachPreview.appendChild(chip);
|
||||
});
|
||||
}
|
||||
window.removeFile = (i) => { pendingFiles.splice(i, 1); renderAttachments(); };
|
||||
attachBtn.onclick = () => fileInput.click();
|
||||
fileInput.onchange = () => {
|
||||
Array.from(fileInput.files).forEach(file => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const base64 = reader.result.split(',')[1];
|
||||
pendingFiles.push({ name: file.name, type: file.type, data: base64 });
|
||||
renderAttachments();
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
fileInput.value = '';
|
||||
};
|
||||
|
||||
function send() {
|
||||
const val = input.value.trim();
|
||||
if (!val && pendingFiles.length === 0) return;
|
||||
addMsg(val || (pendingFiles.length > 0 ? \`[Sent \${pendingFiles.length} files]\` : ''), 'user');
|
||||
vscode.postMessage({ type: 'prompt', value: val, model: modelSel.value, internet: internetEnabled, files: pendingFiles.length > 0 ? pendingFiles : undefined });
|
||||
input.value = ''; input.style.height = 'auto'; pendingFiles = []; renderAttachments();
|
||||
sendBtn.disabled = true; thinkingBar.classList.add('active');
|
||||
}
|
||||
|
||||
sendBtn.onclick = send;
|
||||
input.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
if (e.isComposing) return;
|
||||
e.preventDefault();
|
||||
send();
|
||||
}
|
||||
});
|
||||
historyList.appendChild(el);
|
||||
});
|
||||
break;
|
||||
case 'streamStart':
|
||||
hideLoader();
|
||||
const el=document.createElement('div');el.className='msg';
|
||||
el.innerHTML='<div class="msg-head"><div class="av av-ai">✦</div><span>G1nation</span><span class="msg-time">'+getTime()+'</span></div><div class="msg-body stream-active"></div>';
|
||||
chat.appendChild(el);streamBody=el.querySelector('.msg-body');chat.scrollTop=chat.scrollHeight;
|
||||
break;
|
||||
case 'streamChunk':
|
||||
if(streamBody){streamBody.innerHTML=fmt(streamBody._raw=(streamBody._raw||'')+msg.value);chat.scrollTop=chat.scrollHeight;}
|
||||
break;
|
||||
case 'streamEnd':
|
||||
if(streamBody)streamBody.classList.remove('stream-active');
|
||||
setSending(false);streamBody=null;
|
||||
break;
|
||||
case 'modelsList':
|
||||
modelSel.innerHTML='';
|
||||
(msg.value.models||[]).forEach(m=>{const o=document.createElement('option');o.value=m;o.textContent=m;modelSel.appendChild(o)});
|
||||
if(msg.value.selected) modelSel.value=msg.value.selected;
|
||||
break;
|
||||
case 'brainProfiles':
|
||||
brainSel.innerHTML='';
|
||||
(msg.value.profiles||[]).forEach(profile=>{
|
||||
const o=document.createElement('option');o.value=profile.id;o.textContent=profile.name;brainSel.appendChild(o);
|
||||
o.title=profile.path;
|
||||
});
|
||||
if(msg.value.activeBrainId) brainSel.value=msg.value.activeBrainId;
|
||||
break;
|
||||
case 'engineStatus':
|
||||
const dot = document.getElementById('engineStatusDot');
|
||||
if(dot){
|
||||
dot.style.background = msg.value.online ? 'var(--accent)' : 'var(--red)';
|
||||
dot.title = msg.value.online ? \`AI Engine Online (\${msg.value.url})\` : \`AI Engine Offline (Check \${msg.value.url})\`;
|
||||
}
|
||||
break;
|
||||
case 'brainStatus':
|
||||
const badge = document.getElementById('brainCountBadge');
|
||||
const info = document.getElementById('brainStatusInfo');
|
||||
const meta = document.getElementById('brainStatusMeta');
|
||||
if(badge){
|
||||
badge.innerText = msg.value.count > 999 ? '999+' : msg.value.count;
|
||||
badge.style.display = msg.value.count > 0 ? 'block' : 'none';
|
||||
}
|
||||
if(info){
|
||||
info.innerText = \`Brain: \${msg.value.name} · \${msg.value.count} files\`;
|
||||
info.title = msg.value.path;
|
||||
}
|
||||
if(meta){
|
||||
meta.innerText = msg.value.description ? \`\${msg.value.description} · \${msg.value.path}\` : msg.value.path;
|
||||
meta.title = msg.value.path;
|
||||
}
|
||||
const syncBrainBtn = document.getElementById('brainBtn');
|
||||
if(syncBrainBtn) syncBrainBtn.title = \`Sync Brain: \${msg.value.name} (\${msg.value.count} files at \${msg.value.path})\`;
|
||||
break;
|
||||
case 'clearChat':
|
||||
streamBody=null;hideLoader();setSending(false);
|
||||
chat.innerHTML='<div class="welcome"><div class="welcome-logo">✦</div><div class="welcome-title">G1nation</div><div class="welcome-sub">System Cleaned.</div></div>';
|
||||
document.body.classList.add('init');
|
||||
break;
|
||||
case 'injectPrompt':
|
||||
input.value=msg.value;send();
|
||||
break;
|
||||
case 'autoContinue':
|
||||
showLoader();setSending(true);
|
||||
const hint = document.createElement('div');
|
||||
hint.className = 'auto-status';
|
||||
hint.innerText = msg.value;
|
||||
chat.appendChild(hint);
|
||||
chat.scrollTop = chat.scrollHeight;
|
||||
break;
|
||||
case 'error':
|
||||
hideLoader();setSending(false);addMsg('Error: '+msg.value,'error');
|
||||
break;
|
||||
} });
|
||||
} catch(err) { console.error(err); }
|
||||
</script></body></html>`;
|
||||
input.addEventListener('input', () => { input.style.height = 'auto'; input.style.height = input.scrollHeight + 'px'; });
|
||||
|
||||
document.getElementById('newChatBtn').onclick = () => vscode.postMessage({ type: 'newChat' });
|
||||
document.getElementById('settingsBtn').onclick = () => vscode.postMessage({ type: 'openSettings' });
|
||||
document.getElementById('internetBtn').onclick = () => {
|
||||
internetEnabled = !internetEnabled; document.getElementById('internetBtn').classList.toggle('active', internetEnabled);
|
||||
};
|
||||
document.getElementById('brainBtn').onclick = () => vscode.postMessage({ type: 'syncBrain' });
|
||||
document.getElementById('historyBtn').onclick = () => vscode.postMessage({ type: 'getSessions' });
|
||||
document.getElementById('historyBtn').addEventListener('click', () => historyOverlay.classList.add('visible'));
|
||||
document.getElementById('closeHistoryBtn').onclick = () => historyOverlay.classList.remove('visible');
|
||||
modelSel.onchange = () => vscode.postMessage({ type: 'refreshModels' });
|
||||
brainSel.onchange = () => {
|
||||
if (brainSel.value === 'new') {
|
||||
vscode.postMessage({ type: 'addBrain' });
|
||||
} else {
|
||||
vscode.postMessage({ type: 'setBrainProfile', id: brainSel.value });
|
||||
}
|
||||
};
|
||||
|
||||
vscode.postMessage({ type: 'getModels' });
|
||||
vscode.postMessage({ type: 'ready' });
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user