Spaces:
Runtime error
Runtime error
matt HOFFNER
commited on
Commit
Β·
22ff301
1
Parent(s):
ed45bdf
cleanup
Browse files- package-lock.json +183 -0
- package.json +1 -0
- src/components/Chat/Chat.tsx +0 -355
- src/components/Chat/ChatInput.tsx +0 -251
- src/components/Chat/ChatLoader.tsx +0 -20
- src/components/ChatWindow.jsx +2 -2
- src/components/Loader.jsx +6 -1
- src/components/MessageList.jsx +3 -2
- src/pages/api/home.context.tsx +0 -23
- src/pages/api/home.state.tsx +0 -42
- src/pages/api/home.tsx +0 -157
- src/utils/file-handler.ts +2 -2
- src/utils/index.ts +0 -30
package-lock.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
| 22 |
"next": "13.4.2",
|
| 23 |
"react": "18.2.0",
|
| 24 |
"react-dom": "18.2.0",
|
|
|
|
| 25 |
"typescript": "5.0.4",
|
| 26 |
"uuid": "^9.0.0"
|
| 27 |
},
|
|
@@ -137,6 +138,18 @@
|
|
| 137 |
"node": ">=6.9.0"
|
| 138 |
}
|
| 139 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
"node_modules/@babel/helper-compilation-targets": {
|
| 141 |
"version": "7.22.1",
|
| 142 |
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz",
|
|
@@ -478,6 +491,33 @@
|
|
| 478 |
"node": ">=6.9.0"
|
| 479 |
}
|
| 480 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
"node_modules/@esbuild/android-arm": {
|
| 482 |
"version": "0.17.19",
|
| 483 |
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
|
|
@@ -1777,6 +1817,28 @@
|
|
| 1777 |
"deep-equal": "^2.0.5"
|
| 1778 |
}
|
| 1779 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1780 |
"node_modules/balanced-match": {
|
| 1781 |
"version": "1.0.2",
|
| 1782 |
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
|
@@ -1962,6 +2024,15 @@
|
|
| 1962 |
"node": ">=6"
|
| 1963 |
}
|
| 1964 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1965 |
"node_modules/caniuse-lite": {
|
| 1966 |
"version": "1.0.30001492",
|
| 1967 |
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001492.tgz",
|
|
@@ -2096,6 +2167,26 @@
|
|
| 2096 |
"node": ">= 8"
|
| 2097 |
}
|
| 2098 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2099 |
"node_modules/csstype": {
|
| 2100 |
"version": "3.1.2",
|
| 2101 |
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
|
|
@@ -3468,6 +3559,15 @@
|
|
| 3468 |
"url": "https://github.com/sponsors/ljharb"
|
| 3469 |
}
|
| 3470 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3471 |
"node_modules/human-signals": {
|
| 3472 |
"version": "4.3.1",
|
| 3473 |
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
|
|
@@ -4304,6 +4404,12 @@
|
|
| 4304 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 4305 |
}
|
| 4306 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4307 |
"node_modules/lodash.merge": {
|
| 4308 |
"version": "4.6.2",
|
| 4309 |
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
|
@@ -5282,6 +5388,26 @@
|
|
| 5282 |
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
| 5283 |
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
| 5284 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5285 |
"node_modules/readable-stream": {
|
| 5286 |
"version": "3.6.2",
|
| 5287 |
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
|
@@ -5628,6 +5754,12 @@
|
|
| 5628 |
"node": ">=10"
|
| 5629 |
}
|
| 5630 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5631 |
"node_modules/sharp": {
|
| 5632 |
"version": "0.32.1",
|
| 5633 |
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.1.tgz",
|
|
@@ -5888,6 +6020,57 @@
|
|
| 5888 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 5889 |
}
|
| 5890 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5891 |
"node_modules/styled-jsx": {
|
| 5892 |
"version": "5.1.1",
|
| 5893 |
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
|
|
|
|
| 22 |
"next": "13.4.2",
|
| 23 |
"react": "18.2.0",
|
| 24 |
"react-dom": "18.2.0",
|
| 25 |
+
"react95": "^4.0.0",
|
| 26 |
"typescript": "5.0.4",
|
| 27 |
"uuid": "^9.0.0"
|
| 28 |
},
|
|
|
|
| 138 |
"node": ">=6.9.0"
|
| 139 |
}
|
| 140 |
},
|
| 141 |
+
"node_modules/@babel/helper-annotate-as-pure": {
|
| 142 |
+
"version": "7.18.6",
|
| 143 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
|
| 144 |
+
"integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
|
| 145 |
+
"peer": true,
|
| 146 |
+
"dependencies": {
|
| 147 |
+
"@babel/types": "^7.18.6"
|
| 148 |
+
},
|
| 149 |
+
"engines": {
|
| 150 |
+
"node": ">=6.9.0"
|
| 151 |
+
}
|
| 152 |
+
},
|
| 153 |
"node_modules/@babel/helper-compilation-targets": {
|
| 154 |
"version": "7.22.1",
|
| 155 |
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz",
|
|
|
|
| 491 |
"node": ">=6.9.0"
|
| 492 |
}
|
| 493 |
},
|
| 494 |
+
"node_modules/@emotion/is-prop-valid": {
|
| 495 |
+
"version": "1.2.1",
|
| 496 |
+
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
|
| 497 |
+
"integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==",
|
| 498 |
+
"peer": true,
|
| 499 |
+
"dependencies": {
|
| 500 |
+
"@emotion/memoize": "^0.8.1"
|
| 501 |
+
}
|
| 502 |
+
},
|
| 503 |
+
"node_modules/@emotion/memoize": {
|
| 504 |
+
"version": "0.8.1",
|
| 505 |
+
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
|
| 506 |
+
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==",
|
| 507 |
+
"peer": true
|
| 508 |
+
},
|
| 509 |
+
"node_modules/@emotion/stylis": {
|
| 510 |
+
"version": "0.8.5",
|
| 511 |
+
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
|
| 512 |
+
"integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==",
|
| 513 |
+
"peer": true
|
| 514 |
+
},
|
| 515 |
+
"node_modules/@emotion/unitless": {
|
| 516 |
+
"version": "0.7.5",
|
| 517 |
+
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
| 518 |
+
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
|
| 519 |
+
"peer": true
|
| 520 |
+
},
|
| 521 |
"node_modules/@esbuild/android-arm": {
|
| 522 |
"version": "0.17.19",
|
| 523 |
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
|
|
|
|
| 1817 |
"deep-equal": "^2.0.5"
|
| 1818 |
}
|
| 1819 |
},
|
| 1820 |
+
"node_modules/babel-plugin-styled-components": {
|
| 1821 |
+
"version": "2.1.3",
|
| 1822 |
+
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.3.tgz",
|
| 1823 |
+
"integrity": "sha512-jBioLwBVHpOMU4NsueH/ADcHrjS0Y/WTpt2eGVmmuSFNEv2DF3XhcMncuZlbbjxQ4vzxg+yEr6E6TNjrIQbsJQ==",
|
| 1824 |
+
"peer": true,
|
| 1825 |
+
"dependencies": {
|
| 1826 |
+
"@babel/helper-annotate-as-pure": "^7.18.6",
|
| 1827 |
+
"@babel/helper-module-imports": "^7.21.4",
|
| 1828 |
+
"babel-plugin-syntax-jsx": "^6.18.0",
|
| 1829 |
+
"lodash": "^4.17.21",
|
| 1830 |
+
"picomatch": "^2.3.1"
|
| 1831 |
+
},
|
| 1832 |
+
"peerDependencies": {
|
| 1833 |
+
"styled-components": ">= 2"
|
| 1834 |
+
}
|
| 1835 |
+
},
|
| 1836 |
+
"node_modules/babel-plugin-syntax-jsx": {
|
| 1837 |
+
"version": "6.18.0",
|
| 1838 |
+
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
|
| 1839 |
+
"integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==",
|
| 1840 |
+
"peer": true
|
| 1841 |
+
},
|
| 1842 |
"node_modules/balanced-match": {
|
| 1843 |
"version": "1.0.2",
|
| 1844 |
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
|
|
|
| 2024 |
"node": ">=6"
|
| 2025 |
}
|
| 2026 |
},
|
| 2027 |
+
"node_modules/camelize": {
|
| 2028 |
+
"version": "1.0.1",
|
| 2029 |
+
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
|
| 2030 |
+
"integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
|
| 2031 |
+
"peer": true,
|
| 2032 |
+
"funding": {
|
| 2033 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 2034 |
+
}
|
| 2035 |
+
},
|
| 2036 |
"node_modules/caniuse-lite": {
|
| 2037 |
"version": "1.0.30001492",
|
| 2038 |
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001492.tgz",
|
|
|
|
| 2167 |
"node": ">= 8"
|
| 2168 |
}
|
| 2169 |
},
|
| 2170 |
+
"node_modules/css-color-keywords": {
|
| 2171 |
+
"version": "1.0.0",
|
| 2172 |
+
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
| 2173 |
+
"integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
|
| 2174 |
+
"peer": true,
|
| 2175 |
+
"engines": {
|
| 2176 |
+
"node": ">=4"
|
| 2177 |
+
}
|
| 2178 |
+
},
|
| 2179 |
+
"node_modules/css-to-react-native": {
|
| 2180 |
+
"version": "3.2.0",
|
| 2181 |
+
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
|
| 2182 |
+
"integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
|
| 2183 |
+
"peer": true,
|
| 2184 |
+
"dependencies": {
|
| 2185 |
+
"camelize": "^1.0.0",
|
| 2186 |
+
"css-color-keywords": "^1.0.0",
|
| 2187 |
+
"postcss-value-parser": "^4.0.2"
|
| 2188 |
+
}
|
| 2189 |
+
},
|
| 2190 |
"node_modules/csstype": {
|
| 2191 |
"version": "3.1.2",
|
| 2192 |
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
|
|
|
|
| 3559 |
"url": "https://github.com/sponsors/ljharb"
|
| 3560 |
}
|
| 3561 |
},
|
| 3562 |
+
"node_modules/hoist-non-react-statics": {
|
| 3563 |
+
"version": "3.3.2",
|
| 3564 |
+
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
| 3565 |
+
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
| 3566 |
+
"peer": true,
|
| 3567 |
+
"dependencies": {
|
| 3568 |
+
"react-is": "^16.7.0"
|
| 3569 |
+
}
|
| 3570 |
+
},
|
| 3571 |
"node_modules/human-signals": {
|
| 3572 |
"version": "4.3.1",
|
| 3573 |
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
|
|
|
|
| 4404 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 4405 |
}
|
| 4406 |
},
|
| 4407 |
+
"node_modules/lodash": {
|
| 4408 |
+
"version": "4.17.21",
|
| 4409 |
+
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
| 4410 |
+
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
| 4411 |
+
"peer": true
|
| 4412 |
+
},
|
| 4413 |
"node_modules/lodash.merge": {
|
| 4414 |
"version": "4.6.2",
|
| 4415 |
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
|
|
|
| 5388 |
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
| 5389 |
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
| 5390 |
},
|
| 5391 |
+
"node_modules/react95": {
|
| 5392 |
+
"version": "4.0.0",
|
| 5393 |
+
"resolved": "https://registry.npmjs.org/react95/-/react95-4.0.0.tgz",
|
| 5394 |
+
"integrity": "sha512-C/2Ecws+ShHMJa3/ki6d3ufCukEYyZ+up45NJhX+uecFh5sXNdkEX8bPZ4pKTGl0mSTMkiCJTGOJB1Gmf1W3dA==",
|
| 5395 |
+
"funding": [
|
| 5396 |
+
{
|
| 5397 |
+
"type": "paypal",
|
| 5398 |
+
"url": "https://www.paypal.me/react95"
|
| 5399 |
+
},
|
| 5400 |
+
{
|
| 5401 |
+
"type": "patreon",
|
| 5402 |
+
"url": "https://www.patreon.com/arturbien"
|
| 5403 |
+
}
|
| 5404 |
+
],
|
| 5405 |
+
"peerDependencies": {
|
| 5406 |
+
"react": ">= 16.8.0",
|
| 5407 |
+
"react-dom": ">= 16.8.0",
|
| 5408 |
+
"styled-components": ">= 5.3.3"
|
| 5409 |
+
}
|
| 5410 |
+
},
|
| 5411 |
"node_modules/readable-stream": {
|
| 5412 |
"version": "3.6.2",
|
| 5413 |
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
|
|
|
| 5754 |
"node": ">=10"
|
| 5755 |
}
|
| 5756 |
},
|
| 5757 |
+
"node_modules/shallowequal": {
|
| 5758 |
+
"version": "1.1.0",
|
| 5759 |
+
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
| 5760 |
+
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
|
| 5761 |
+
"peer": true
|
| 5762 |
+
},
|
| 5763 |
"node_modules/sharp": {
|
| 5764 |
"version": "0.32.1",
|
| 5765 |
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.1.tgz",
|
|
|
|
| 6020 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 6021 |
}
|
| 6022 |
},
|
| 6023 |
+
"node_modules/styled-components": {
|
| 6024 |
+
"version": "5.3.11",
|
| 6025 |
+
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz",
|
| 6026 |
+
"integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==",
|
| 6027 |
+
"peer": true,
|
| 6028 |
+
"dependencies": {
|
| 6029 |
+
"@babel/helper-module-imports": "^7.0.0",
|
| 6030 |
+
"@babel/traverse": "^7.4.5",
|
| 6031 |
+
"@emotion/is-prop-valid": "^1.1.0",
|
| 6032 |
+
"@emotion/stylis": "^0.8.4",
|
| 6033 |
+
"@emotion/unitless": "^0.7.4",
|
| 6034 |
+
"babel-plugin-styled-components": ">= 1.12.0",
|
| 6035 |
+
"css-to-react-native": "^3.0.0",
|
| 6036 |
+
"hoist-non-react-statics": "^3.0.0",
|
| 6037 |
+
"shallowequal": "^1.1.0",
|
| 6038 |
+
"supports-color": "^5.5.0"
|
| 6039 |
+
},
|
| 6040 |
+
"engines": {
|
| 6041 |
+
"node": ">=10"
|
| 6042 |
+
},
|
| 6043 |
+
"funding": {
|
| 6044 |
+
"type": "opencollective",
|
| 6045 |
+
"url": "https://opencollective.com/styled-components"
|
| 6046 |
+
},
|
| 6047 |
+
"peerDependencies": {
|
| 6048 |
+
"react": ">= 16.8.0",
|
| 6049 |
+
"react-dom": ">= 16.8.0",
|
| 6050 |
+
"react-is": ">= 16.8.0"
|
| 6051 |
+
}
|
| 6052 |
+
},
|
| 6053 |
+
"node_modules/styled-components/node_modules/has-flag": {
|
| 6054 |
+
"version": "3.0.0",
|
| 6055 |
+
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
| 6056 |
+
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
| 6057 |
+
"peer": true,
|
| 6058 |
+
"engines": {
|
| 6059 |
+
"node": ">=4"
|
| 6060 |
+
}
|
| 6061 |
+
},
|
| 6062 |
+
"node_modules/styled-components/node_modules/supports-color": {
|
| 6063 |
+
"version": "5.5.0",
|
| 6064 |
+
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
| 6065 |
+
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
| 6066 |
+
"peer": true,
|
| 6067 |
+
"dependencies": {
|
| 6068 |
+
"has-flag": "^3.0.0"
|
| 6069 |
+
},
|
| 6070 |
+
"engines": {
|
| 6071 |
+
"node": ">=4"
|
| 6072 |
+
}
|
| 6073 |
+
},
|
| 6074 |
"node_modules/styled-jsx": {
|
| 6075 |
"version": "5.1.1",
|
| 6076 |
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
|
package.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
| 22 |
"next": "13.4.2",
|
| 23 |
"react": "18.2.0",
|
| 24 |
"react-dom": "18.2.0",
|
|
|
|
| 25 |
"typescript": "5.0.4",
|
| 26 |
"uuid": "^9.0.0"
|
| 27 |
},
|
|
|
|
| 22 |
"next": "13.4.2",
|
| 23 |
"react": "18.2.0",
|
| 24 |
"react-dom": "18.2.0",
|
| 25 |
+
"react95": "^4.0.0",
|
| 26 |
"typescript": "5.0.4",
|
| 27 |
"uuid": "^9.0.0"
|
| 28 |
},
|
src/components/Chat/Chat.tsx
DELETED
|
@@ -1,355 +0,0 @@
|
|
| 1 |
-
import { IconClearAll, IconSettings } from '@tabler/icons-react';
|
| 2 |
-
import {
|
| 3 |
-
MutableRefObject,
|
| 4 |
-
memo,
|
| 5 |
-
useCallback,
|
| 6 |
-
useContext,
|
| 7 |
-
useEffect,
|
| 8 |
-
useRef,
|
| 9 |
-
useState,
|
| 10 |
-
} from 'react';
|
| 11 |
-
|
| 12 |
-
import {
|
| 13 |
-
saveConversation,
|
| 14 |
-
saveConversations,
|
| 15 |
-
updateConversation,
|
| 16 |
-
throttle
|
| 17 |
-
} from '@/utils';
|
| 18 |
-
|
| 19 |
-
import { ChatBody, Conversation, Message } from '@/types/chat';
|
| 20 |
-
|
| 21 |
-
import HomeContext from '@/pages/api/home.context';
|
| 22 |
-
|
| 23 |
-
import { ChatInput } from './ChatInput';
|
| 24 |
-
import { ChatLoader } from './ChatLoader';
|
| 25 |
-
|
| 26 |
-
interface Props {
|
| 27 |
-
stopConversationRef: MutableRefObject<boolean>;
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
-
export const Chat = memo(({ stopConversationRef }: Props) => {
|
| 31 |
-
const {
|
| 32 |
-
state: {
|
| 33 |
-
selectedConversation,
|
| 34 |
-
conversations,
|
| 35 |
-
loading
|
| 36 |
-
},
|
| 37 |
-
handleUpdateConversation,
|
| 38 |
-
dispatch: homeDispatch,
|
| 39 |
-
}: any = useContext(HomeContext);
|
| 40 |
-
|
| 41 |
-
const [currentMessage, setCurrentMessage] = useState<Message>();
|
| 42 |
-
const [autoScrollEnabled, setAutoScrollEnabled] = useState<boolean>(true);
|
| 43 |
-
const [showSettings, setShowSettings] = useState<boolean>(false);
|
| 44 |
-
const [showScrollDownButton, setShowScrollDownButton] =
|
| 45 |
-
useState<boolean>(false);
|
| 46 |
-
|
| 47 |
-
const messagesEndRef = useRef<HTMLDivElement>(null);
|
| 48 |
-
const chatContainerRef = useRef<HTMLDivElement>(null);
|
| 49 |
-
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
| 50 |
-
|
| 51 |
-
const handleSend = useCallback(
|
| 52 |
-
async (message: Message, deleteCount = 0, plugin: Plugin | null = null) => {
|
| 53 |
-
if (selectedConversation) {
|
| 54 |
-
let updatedConversation: Conversation;
|
| 55 |
-
if (deleteCount) {
|
| 56 |
-
const updatedMessages = [...selectedConversation.messages];
|
| 57 |
-
for (let i = 0; i < deleteCount; i++) {
|
| 58 |
-
updatedMessages.pop();
|
| 59 |
-
}
|
| 60 |
-
updatedConversation = {
|
| 61 |
-
...selectedConversation,
|
| 62 |
-
messages: [...updatedMessages, message],
|
| 63 |
-
};
|
| 64 |
-
} else {
|
| 65 |
-
updatedConversation = {
|
| 66 |
-
...selectedConversation,
|
| 67 |
-
messages: [...selectedConversation.messages, message],
|
| 68 |
-
};
|
| 69 |
-
}
|
| 70 |
-
homeDispatch({
|
| 71 |
-
field: 'selectedConversation',
|
| 72 |
-
value: updatedConversation,
|
| 73 |
-
});
|
| 74 |
-
homeDispatch({ field: 'loading', value: true });
|
| 75 |
-
homeDispatch({ field: 'messageIsStreaming', value: true });
|
| 76 |
-
const chatBody: ChatBody = {
|
| 77 |
-
model: updatedConversation.model,
|
| 78 |
-
messages: updatedConversation.messages,
|
| 79 |
-
prompt: updatedConversation.prompt
|
| 80 |
-
};
|
| 81 |
-
const endpoint = "/v1/api/create"
|
| 82 |
-
const body = JSON.stringify(chatBody);
|
| 83 |
-
const controller = new AbortController();
|
| 84 |
-
const response = await fetch(endpoint, {
|
| 85 |
-
method: 'POST',
|
| 86 |
-
headers: {
|
| 87 |
-
'Content-Type': 'application/json',
|
| 88 |
-
},
|
| 89 |
-
signal: controller.signal,
|
| 90 |
-
body,
|
| 91 |
-
});
|
| 92 |
-
if (!response.ok) {
|
| 93 |
-
homeDispatch({ field: 'loading', value: false });
|
| 94 |
-
homeDispatch({ field: 'messageIsStreaming', value: false });
|
| 95 |
-
console.error(response.statusText);
|
| 96 |
-
return;
|
| 97 |
-
}
|
| 98 |
-
const data = response.body;
|
| 99 |
-
if (!data) {
|
| 100 |
-
homeDispatch({ field: 'loading', value: false });
|
| 101 |
-
homeDispatch({ field: 'messageIsStreaming', value: false });
|
| 102 |
-
return;
|
| 103 |
-
}
|
| 104 |
-
if (!plugin) {
|
| 105 |
-
if (updatedConversation.messages.length === 1) {
|
| 106 |
-
const { content } = message;
|
| 107 |
-
const customName =
|
| 108 |
-
content.length > 30 ? content.substring(0, 30) + '...' : content;
|
| 109 |
-
updatedConversation = {
|
| 110 |
-
...updatedConversation,
|
| 111 |
-
name: customName,
|
| 112 |
-
};
|
| 113 |
-
}
|
| 114 |
-
homeDispatch({ field: 'loading', value: false });
|
| 115 |
-
const reader = data.getReader();
|
| 116 |
-
const decoder = new TextDecoder();
|
| 117 |
-
let done = false;
|
| 118 |
-
let isFirst = true;
|
| 119 |
-
let text = '';
|
| 120 |
-
while (!done) {
|
| 121 |
-
if (stopConversationRef.current === true) {
|
| 122 |
-
controller.abort();
|
| 123 |
-
done = true;
|
| 124 |
-
break;
|
| 125 |
-
}
|
| 126 |
-
const { value, done: doneReading } = await reader.read();
|
| 127 |
-
done = doneReading;
|
| 128 |
-
const chunkValue = decoder.decode(value);
|
| 129 |
-
text += chunkValue;
|
| 130 |
-
if (isFirst) {
|
| 131 |
-
isFirst = false;
|
| 132 |
-
const updatedMessages: Message[] = [
|
| 133 |
-
...updatedConversation.messages,
|
| 134 |
-
{ role: 'assistant', content: chunkValue },
|
| 135 |
-
];
|
| 136 |
-
updatedConversation = {
|
| 137 |
-
...updatedConversation,
|
| 138 |
-
messages: updatedMessages,
|
| 139 |
-
};
|
| 140 |
-
homeDispatch({
|
| 141 |
-
field: 'selectedConversation',
|
| 142 |
-
value: updatedConversation,
|
| 143 |
-
});
|
| 144 |
-
} else {
|
| 145 |
-
const updatedMessages: Message[] =
|
| 146 |
-
updatedConversation.messages.map((message: any, index: number) => {
|
| 147 |
-
if (index === updatedConversation.messages.length - 1) {
|
| 148 |
-
return {
|
| 149 |
-
...message,
|
| 150 |
-
content: text,
|
| 151 |
-
};
|
| 152 |
-
}
|
| 153 |
-
return message;
|
| 154 |
-
});
|
| 155 |
-
updatedConversation = {
|
| 156 |
-
...updatedConversation,
|
| 157 |
-
messages: updatedMessages,
|
| 158 |
-
};
|
| 159 |
-
homeDispatch({
|
| 160 |
-
field: 'selectedConversation',
|
| 161 |
-
value: updatedConversation,
|
| 162 |
-
});
|
| 163 |
-
}
|
| 164 |
-
}
|
| 165 |
-
saveConversation(updatedConversation);
|
| 166 |
-
const updatedConversations: Conversation[] = conversations.map(
|
| 167 |
-
(conversation: { id: any; }) => {
|
| 168 |
-
if (conversation.id === selectedConversation.id) {
|
| 169 |
-
return updatedConversation;
|
| 170 |
-
}
|
| 171 |
-
return conversation;
|
| 172 |
-
},
|
| 173 |
-
);
|
| 174 |
-
if (updatedConversations.length === 0) {
|
| 175 |
-
updatedConversations.push(updatedConversation);
|
| 176 |
-
}
|
| 177 |
-
homeDispatch({ field: 'conversations', value: updatedConversations });
|
| 178 |
-
saveConversations(updatedConversations);
|
| 179 |
-
homeDispatch({ field: 'messageIsStreaming', value: false });
|
| 180 |
-
} else {
|
| 181 |
-
const { answer } = await response.json();
|
| 182 |
-
const updatedMessages: Message[] = [
|
| 183 |
-
...updatedConversation.messages,
|
| 184 |
-
{ role: 'assistant', content: answer },
|
| 185 |
-
];
|
| 186 |
-
updatedConversation = {
|
| 187 |
-
...updatedConversation,
|
| 188 |
-
messages: updatedMessages,
|
| 189 |
-
};
|
| 190 |
-
homeDispatch({
|
| 191 |
-
field: 'selectedConversation',
|
| 192 |
-
value: updateConversation,
|
| 193 |
-
});
|
| 194 |
-
saveConversation(updatedConversation);
|
| 195 |
-
const updatedConversations: Conversation[] = conversations.map(
|
| 196 |
-
(conversation: { id: any; }) => {
|
| 197 |
-
if (conversation.id === selectedConversation.id) {
|
| 198 |
-
return updatedConversation;
|
| 199 |
-
}
|
| 200 |
-
return conversation;
|
| 201 |
-
},
|
| 202 |
-
);
|
| 203 |
-
if (updatedConversations.length === 0) {
|
| 204 |
-
updatedConversations.push(updatedConversation);
|
| 205 |
-
}
|
| 206 |
-
homeDispatch({ field: 'conversations', value: updatedConversations });
|
| 207 |
-
saveConversations(updatedConversations);
|
| 208 |
-
homeDispatch({ field: 'loading', value: false });
|
| 209 |
-
homeDispatch({ field: 'messageIsStreaming', value: false });
|
| 210 |
-
}
|
| 211 |
-
}
|
| 212 |
-
},
|
| 213 |
-
[
|
| 214 |
-
conversations,
|
| 215 |
-
selectedConversation,
|
| 216 |
-
stopConversationRef,
|
| 217 |
-
],
|
| 218 |
-
);
|
| 219 |
-
|
| 220 |
-
const handleScroll = () => {
|
| 221 |
-
if (chatContainerRef.current) {
|
| 222 |
-
const { scrollTop, scrollHeight, clientHeight } =
|
| 223 |
-
chatContainerRef.current;
|
| 224 |
-
const bottomTolerance = 30;
|
| 225 |
-
|
| 226 |
-
if (scrollTop + clientHeight < scrollHeight - bottomTolerance) {
|
| 227 |
-
setAutoScrollEnabled(false);
|
| 228 |
-
setShowScrollDownButton(true);
|
| 229 |
-
} else {
|
| 230 |
-
setAutoScrollEnabled(true);
|
| 231 |
-
setShowScrollDownButton(false);
|
| 232 |
-
}
|
| 233 |
-
}
|
| 234 |
-
};
|
| 235 |
-
|
| 236 |
-
const handleScrollDown = () => {
|
| 237 |
-
chatContainerRef.current?.scrollTo({
|
| 238 |
-
top: chatContainerRef.current.scrollHeight,
|
| 239 |
-
behavior: 'smooth',
|
| 240 |
-
});
|
| 241 |
-
};
|
| 242 |
-
|
| 243 |
-
const handleSettings = () => {
|
| 244 |
-
setShowSettings(!showSettings);
|
| 245 |
-
};
|
| 246 |
-
|
| 247 |
-
const onClearAll = () => {
|
| 248 |
-
if (
|
| 249 |
-
confirm('Are you sure you want to clear all messages?') &&
|
| 250 |
-
selectedConversation
|
| 251 |
-
) {
|
| 252 |
-
handleUpdateConversation(selectedConversation, {
|
| 253 |
-
key: 'messages',
|
| 254 |
-
value: [],
|
| 255 |
-
});
|
| 256 |
-
}
|
| 257 |
-
};
|
| 258 |
-
|
| 259 |
-
const scrollDown = () => {
|
| 260 |
-
if (autoScrollEnabled) {
|
| 261 |
-
messagesEndRef.current?.scrollIntoView(true);
|
| 262 |
-
}
|
| 263 |
-
};
|
| 264 |
-
const throttledScrollDown = throttle(scrollDown, 250);
|
| 265 |
-
|
| 266 |
-
useEffect(() => {
|
| 267 |
-
throttledScrollDown();
|
| 268 |
-
selectedConversation &&
|
| 269 |
-
setCurrentMessage(
|
| 270 |
-
selectedConversation.messages[selectedConversation.messages.length - 2],
|
| 271 |
-
);
|
| 272 |
-
}, [selectedConversation, throttledScrollDown]);
|
| 273 |
-
|
| 274 |
-
useEffect(() => {
|
| 275 |
-
const observer = new IntersectionObserver(
|
| 276 |
-
([entry]) => {
|
| 277 |
-
setAutoScrollEnabled(entry.isIntersecting);
|
| 278 |
-
if (entry.isIntersecting) {
|
| 279 |
-
textareaRef.current?.focus();
|
| 280 |
-
}
|
| 281 |
-
},
|
| 282 |
-
{
|
| 283 |
-
root: null,
|
| 284 |
-
threshold: 0.5,
|
| 285 |
-
},
|
| 286 |
-
);
|
| 287 |
-
const messagesEndElement = messagesEndRef.current;
|
| 288 |
-
if (messagesEndElement) {
|
| 289 |
-
observer.observe(messagesEndElement);
|
| 290 |
-
}
|
| 291 |
-
return () => {
|
| 292 |
-
if (messagesEndElement) {
|
| 293 |
-
observer.unobserve(messagesEndElement);
|
| 294 |
-
}
|
| 295 |
-
};
|
| 296 |
-
}, [messagesEndRef]);
|
| 297 |
-
|
| 298 |
-
return (
|
| 299 |
-
<div className="relative flex-1 overflow-hidden bg-white dark:bg-[#343541]">
|
| 300 |
-
<div
|
| 301 |
-
className="max-h-full overflow-x-hidden"
|
| 302 |
-
ref={chatContainerRef}
|
| 303 |
-
onScroll={handleScroll}
|
| 304 |
-
>
|
| 305 |
-
{selectedConversation?.messages.length === 0 ? (
|
| 306 |
-
<>
|
| 307 |
-
<div className="mx-auto flex flex-col space-y-5 md:space-y-10 px-3 pt-5 md:pt-12 sm:max-w-[600px]">
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
</div>
|
| 311 |
-
</>
|
| 312 |
-
) : (
|
| 313 |
-
<>
|
| 314 |
-
<div className="sticky top-0 z-10 flex justify-center border border-b-neutral-300 bg-neutral-100 py-2 text-sm text-neutral-500 dark:border-none dark:bg-[#444654] dark:text-neutral-200">
|
| 315 |
-
<button
|
| 316 |
-
className="ml-2 cursor-pointer hover:opacity-50"
|
| 317 |
-
onClick={handleSettings}
|
| 318 |
-
>
|
| 319 |
-
<IconSettings size={18} />
|
| 320 |
-
</button>
|
| 321 |
-
<button
|
| 322 |
-
className="ml-2 cursor-pointer hover:opacity-50"
|
| 323 |
-
onClick={onClearAll}
|
| 324 |
-
>
|
| 325 |
-
<IconClearAll size={18} />
|
| 326 |
-
</button>
|
| 327 |
-
</div>
|
| 328 |
-
{loading && <ChatLoader />}
|
| 329 |
-
<div
|
| 330 |
-
className="h-[162px] bg-white dark:bg-[#343541]"
|
| 331 |
-
ref={messagesEndRef}
|
| 332 |
-
/>
|
| 333 |
-
</>
|
| 334 |
-
)}
|
| 335 |
-
</div>
|
| 336 |
-
|
| 337 |
-
<ChatInput
|
| 338 |
-
stopConversationRef={stopConversationRef}
|
| 339 |
-
textareaRef={textareaRef}
|
| 340 |
-
onSend={(message: any) => {
|
| 341 |
-
setCurrentMessage(message);
|
| 342 |
-
handleSend(message, 0);
|
| 343 |
-
}}
|
| 344 |
-
onScrollDownClick={handleScrollDown}
|
| 345 |
-
onRegenerate={() => {
|
| 346 |
-
if (currentMessage) {
|
| 347 |
-
handleSend(currentMessage, 2, null);
|
| 348 |
-
}
|
| 349 |
-
}}
|
| 350 |
-
showScrollDownButton={showScrollDownButton}
|
| 351 |
-
/>
|
| 352 |
-
</div>
|
| 353 |
-
);
|
| 354 |
-
});
|
| 355 |
-
Chat.displayName = 'Chat';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/components/Chat/ChatInput.tsx
DELETED
|
@@ -1,251 +0,0 @@
|
|
| 1 |
-
import {
|
| 2 |
-
IconPlayerStop,
|
| 3 |
-
IconRepeat,
|
| 4 |
-
IconSend,
|
| 5 |
-
} from '@tabler/icons-react';
|
| 6 |
-
import {
|
| 7 |
-
KeyboardEvent,
|
| 8 |
-
MutableRefObject,
|
| 9 |
-
useCallback,
|
| 10 |
-
useContext,
|
| 11 |
-
useEffect,
|
| 12 |
-
useRef,
|
| 13 |
-
useState,
|
| 14 |
-
} from 'react';
|
| 15 |
-
|
| 16 |
-
import { Message } from '@/types/chat';
|
| 17 |
-
import { Prompt } from '@/types/prompt';
|
| 18 |
-
|
| 19 |
-
import HomeContext from '@/pages/api/home.context';
|
| 20 |
-
|
| 21 |
-
interface Props {
|
| 22 |
-
onSend: (message: Message) => void;
|
| 23 |
-
onRegenerate: () => void;
|
| 24 |
-
onScrollDownClick: () => void;
|
| 25 |
-
stopConversationRef: MutableRefObject<boolean>;
|
| 26 |
-
textareaRef: MutableRefObject<HTMLTextAreaElement | null>;
|
| 27 |
-
showScrollDownButton: boolean;
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
-
export const ChatInput = ({
|
| 31 |
-
onSend,
|
| 32 |
-
onRegenerate,
|
| 33 |
-
stopConversationRef,
|
| 34 |
-
textareaRef,
|
| 35 |
-
}: Props) => {
|
| 36 |
-
|
| 37 |
-
const {
|
| 38 |
-
state: { selectedConversation, messageIsStreaming, prompts },
|
| 39 |
-
|
| 40 |
-
dispatch: homeDispatch,
|
| 41 |
-
}: any = useContext(HomeContext);
|
| 42 |
-
|
| 43 |
-
const [content, setContent] = useState<string>();
|
| 44 |
-
const [isTyping, setIsTyping] = useState<boolean>(false);
|
| 45 |
-
const [showPromptList, setShowPromptList] = useState(false);
|
| 46 |
-
const [activePromptIndex, setActivePromptIndex] = useState(0);
|
| 47 |
-
const [promptInputValue, setPromptInputValue] = useState('');
|
| 48 |
-
const [variables, setVariables] = useState<string[]>([]);
|
| 49 |
-
|
| 50 |
-
const promptListRef = useRef<HTMLUListElement | null>(null);
|
| 51 |
-
|
| 52 |
-
const filteredPrompts = prompts.filter((prompt: { name: string; }) =>
|
| 53 |
-
prompt.name.toLowerCase().includes(promptInputValue.toLowerCase()),
|
| 54 |
-
);
|
| 55 |
-
|
| 56 |
-
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
| 57 |
-
const value = e.target.value;
|
| 58 |
-
const maxLength = selectedConversation?.model.maxLength;
|
| 59 |
-
|
| 60 |
-
if (maxLength && value.length > maxLength) {
|
| 61 |
-
alert(
|
| 62 |
-
`Message limit is ${maxLength} characters. You have entered ${value.length} characters.`,
|
| 63 |
-
);
|
| 64 |
-
return;
|
| 65 |
-
}
|
| 66 |
-
|
| 67 |
-
setContent(value);
|
| 68 |
-
updatePromptListVisibility(value);
|
| 69 |
-
};
|
| 70 |
-
|
| 71 |
-
const handleSend = () => {
|
| 72 |
-
if (messageIsStreaming) {
|
| 73 |
-
return;
|
| 74 |
-
}
|
| 75 |
-
|
| 76 |
-
if (!content) {
|
| 77 |
-
alert('Please enter a message');
|
| 78 |
-
return;
|
| 79 |
-
}
|
| 80 |
-
|
| 81 |
-
onSend({ role: 'user', content });
|
| 82 |
-
setContent('');
|
| 83 |
-
|
| 84 |
-
if (window.innerWidth < 640 && textareaRef && textareaRef.current) {
|
| 85 |
-
textareaRef.current.blur();
|
| 86 |
-
}
|
| 87 |
-
};
|
| 88 |
-
|
| 89 |
-
const handleStopConversation = () => {
|
| 90 |
-
stopConversationRef.current = true;
|
| 91 |
-
setTimeout(() => {
|
| 92 |
-
stopConversationRef.current = false;
|
| 93 |
-
}, 1000);
|
| 94 |
-
};
|
| 95 |
-
|
| 96 |
-
const isMobile = () => {
|
| 97 |
-
const userAgent =
|
| 98 |
-
typeof window.navigator === 'undefined' ? '' : navigator.userAgent;
|
| 99 |
-
const mobileRegex =
|
| 100 |
-
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i;
|
| 101 |
-
return mobileRegex.test(userAgent);
|
| 102 |
-
};
|
| 103 |
-
|
| 104 |
-
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
| 105 |
-
if (e.key === 'Enter' && !isTyping && !isMobile() && !e.shiftKey) {
|
| 106 |
-
e.preventDefault();
|
| 107 |
-
handleSend();
|
| 108 |
-
} else if (e.key === '/' && e.metaKey) {
|
| 109 |
-
e.preventDefault();
|
| 110 |
-
|
| 111 |
-
}
|
| 112 |
-
};
|
| 113 |
-
|
| 114 |
-
const parseVariables = (content: string) => {
|
| 115 |
-
const regex = /{{(.*?)}}/g;
|
| 116 |
-
const foundVariables = [];
|
| 117 |
-
let match;
|
| 118 |
-
|
| 119 |
-
while ((match = regex.exec(content)) !== null) {
|
| 120 |
-
foundVariables.push(match[1]);
|
| 121 |
-
}
|
| 122 |
-
|
| 123 |
-
return foundVariables;
|
| 124 |
-
};
|
| 125 |
-
|
| 126 |
-
const updatePromptListVisibility = useCallback((text: string) => {
|
| 127 |
-
const match = text.match(/\/\w*$/);
|
| 128 |
-
|
| 129 |
-
if (match) {
|
| 130 |
-
setShowPromptList(true);
|
| 131 |
-
setPromptInputValue(match[0].slice(1));
|
| 132 |
-
} else {
|
| 133 |
-
setShowPromptList(false);
|
| 134 |
-
setPromptInputValue('');
|
| 135 |
-
}
|
| 136 |
-
}, []);
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
const handleSubmit = (updatedVariables: string[]) => {
|
| 140 |
-
const newContent = content?.replace(/{{(.*?)}}/g, (match, variable) => {
|
| 141 |
-
const index = variables.indexOf(variable);
|
| 142 |
-
return updatedVariables[index];
|
| 143 |
-
});
|
| 144 |
-
|
| 145 |
-
setContent(newContent);
|
| 146 |
-
|
| 147 |
-
if (textareaRef && textareaRef.current) {
|
| 148 |
-
textareaRef.current.focus();
|
| 149 |
-
}
|
| 150 |
-
};
|
| 151 |
-
|
| 152 |
-
useEffect(() => {
|
| 153 |
-
if (promptListRef.current) {
|
| 154 |
-
promptListRef.current.scrollTop = activePromptIndex * 30;
|
| 155 |
-
}
|
| 156 |
-
}, [activePromptIndex]);
|
| 157 |
-
|
| 158 |
-
useEffect(() => {
|
| 159 |
-
if (textareaRef && textareaRef.current) {
|
| 160 |
-
textareaRef.current.style.height = 'inherit';
|
| 161 |
-
textareaRef.current.style.height = `${textareaRef.current?.scrollHeight}px`;
|
| 162 |
-
textareaRef.current.style.overflow = `${
|
| 163 |
-
textareaRef?.current?.scrollHeight > 400 ? 'auto' : 'hidden'
|
| 164 |
-
}`;
|
| 165 |
-
}
|
| 166 |
-
}, [content]);
|
| 167 |
-
|
| 168 |
-
useEffect(() => {
|
| 169 |
-
const handleOutsideClick = (e: MouseEvent) => {
|
| 170 |
-
if (
|
| 171 |
-
promptListRef.current &&
|
| 172 |
-
!promptListRef.current.contains(e.target as Node)
|
| 173 |
-
) {
|
| 174 |
-
setShowPromptList(false);
|
| 175 |
-
}
|
| 176 |
-
};
|
| 177 |
-
|
| 178 |
-
window.addEventListener('click', handleOutsideClick);
|
| 179 |
-
|
| 180 |
-
return () => {
|
| 181 |
-
window.removeEventListener('click', handleOutsideClick);
|
| 182 |
-
};
|
| 183 |
-
}, []);
|
| 184 |
-
|
| 185 |
-
return (
|
| 186 |
-
<div className="absolute bottom-0 left-0 w-full border-transparent bg-gradient-to-b from-transparent via-white to-white pt-6 dark:border-white/20 dark:via-[#343541] dark:to-[#343541] md:pt-2">
|
| 187 |
-
<div className="stretch mx-2 mt-4 flex flex-row gap-3 last:mb-2 md:mx-4 md:mt-[52px] md:last:mb-6 lg:mx-auto lg:max-w-3xl">
|
| 188 |
-
{messageIsStreaming && (
|
| 189 |
-
<button
|
| 190 |
-
className="absolute top-0 left-0 right-0 mx-auto mb-3 flex w-fit items-center gap-3 rounded border border-neutral-200 bg-white py-2 px-4 text-black hover:opacity-50 dark:border-neutral-600 dark:bg-[#343541] dark:text-white md:mb-0 md:mt-2"
|
| 191 |
-
onClick={handleStopConversation}
|
| 192 |
-
>
|
| 193 |
-
<IconPlayerStop size={16} /> {'Stop Generating'}
|
| 194 |
-
</button>
|
| 195 |
-
)}
|
| 196 |
-
|
| 197 |
-
{!messageIsStreaming &&
|
| 198 |
-
selectedConversation &&
|
| 199 |
-
selectedConversation.messages.length > 0 && (
|
| 200 |
-
<button
|
| 201 |
-
className="absolute top-0 left-0 right-0 mx-auto mb-3 flex w-fit items-center gap-3 rounded border border-neutral-200 bg-white py-2 px-4 text-black hover:opacity-50 dark:border-neutral-600 dark:bg-[#343541] dark:text-white md:mb-0 md:mt-2"
|
| 202 |
-
onClick={onRegenerate}
|
| 203 |
-
>
|
| 204 |
-
<IconRepeat size={16} /> {'Regenerate response'}
|
| 205 |
-
</button>
|
| 206 |
-
)}
|
| 207 |
-
|
| 208 |
-
<div className="relative mx-2 flex w-full flex-grow flex-col rounded-md border border-black/10 bg-white shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:border-gray-900/50 dark:bg-[#40414F] dark:text-white dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] sm:mx-4">
|
| 209 |
-
|
| 210 |
-
<textarea
|
| 211 |
-
ref={textareaRef}
|
| 212 |
-
className="m-0 w-full resize-none border-0 bg-transparent p-0 py-2 pr-8 pl-10 text-black dark:bg-transparent dark:text-white md:py-3 md:pl-10"
|
| 213 |
-
style={{
|
| 214 |
-
resize: 'none',
|
| 215 |
-
bottom: `${textareaRef?.current?.scrollHeight}px`,
|
| 216 |
-
maxHeight: '400px',
|
| 217 |
-
overflow: `${
|
| 218 |
-
textareaRef.current && textareaRef.current.scrollHeight > 400
|
| 219 |
-
? 'auto'
|
| 220 |
-
: 'hidden'
|
| 221 |
-
}`,
|
| 222 |
-
}}
|
| 223 |
-
placeholder={
|
| 224 |
-
'Type a message or type "/" to select a prompt...' || ''
|
| 225 |
-
}
|
| 226 |
-
value={content}
|
| 227 |
-
rows={1}
|
| 228 |
-
onCompositionStart={() => setIsTyping(true)}
|
| 229 |
-
onCompositionEnd={() => setIsTyping(false)}
|
| 230 |
-
onChange={handleChange}
|
| 231 |
-
onKeyDown={handleKeyDown}
|
| 232 |
-
/>
|
| 233 |
-
|
| 234 |
-
<button
|
| 235 |
-
className="absolute right-2 top-2 rounded-sm p-1 text-neutral-800 opacity-60 hover:bg-neutral-200 hover:text-neutral-900 dark:bg-opacity-50 dark:text-neutral-100 dark:hover:text-neutral-200"
|
| 236 |
-
onClick={handleSend}
|
| 237 |
-
>
|
| 238 |
-
{messageIsStreaming ? (
|
| 239 |
-
<div className="h-4 w-4 animate-spin rounded-full border-t-2 border-neutral-800 opacity-60 dark:border-neutral-100"></div>
|
| 240 |
-
) : (
|
| 241 |
-
<IconSend size={18} />
|
| 242 |
-
)}
|
| 243 |
-
</button>
|
| 244 |
-
</div>
|
| 245 |
-
</div>
|
| 246 |
-
<div className="px-3 pt-2 pb-3 text-center text-[12px] text-black/50 dark:text-white/50 md:px-4 md:pt-3 md:pb-6">
|
| 247 |
-
|
| 248 |
-
</div>
|
| 249 |
-
</div>
|
| 250 |
-
);
|
| 251 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/components/Chat/ChatLoader.tsx
DELETED
|
@@ -1,20 +0,0 @@
|
|
| 1 |
-
import { IconRobot } from '@tabler/icons-react';
|
| 2 |
-
import { FC } from 'react';
|
| 3 |
-
|
| 4 |
-
interface Props { }
|
| 5 |
-
|
| 6 |
-
export const ChatLoader: FC<Props> = () => {
|
| 7 |
-
return (
|
| 8 |
-
<div
|
| 9 |
-
className="group border-b border-black/10 bg-gray-50 text-gray-800 dark:border-gray-900/50 dark:bg-[#444654] dark:text-gray-100"
|
| 10 |
-
style={{ overflowWrap: 'anywhere' }}
|
| 11 |
-
>
|
| 12 |
-
<div className="m-auto flex gap-4 p-4 text-base md:max-w-2xl md:gap-6 md:py-6 lg:max-w-2xl lg:px-0 xl:max-w-3xl">
|
| 13 |
-
<div className="min-w-[40px] items-end">
|
| 14 |
-
<IconRobot size={30} />
|
| 15 |
-
</div>
|
| 16 |
-
<span className="animate-pulse cursor-default mt-1">β</span>
|
| 17 |
-
</div>
|
| 18 |
-
</div>
|
| 19 |
-
);
|
| 20 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/components/ChatWindow.jsx
CHANGED
|
@@ -54,8 +54,8 @@ function ChatWindow({
|
|
| 54 |
<div className="window-content w-full">
|
| 55 |
<div className="flex flex-col w-full">
|
| 56 |
<MessageList
|
| 57 |
-
screenName={"
|
| 58 |
-
assistantScreenName={"
|
| 59 |
/>
|
| 60 |
{/* <Separator /> */}
|
| 61 |
<div className="h-4" />
|
|
|
|
| 54 |
<div className="window-content w-full">
|
| 55 |
<div className="flex flex-col w-full">
|
| 56 |
<MessageList
|
| 57 |
+
screenName={"me"}
|
| 58 |
+
assistantScreenName={"vicuna"}
|
| 59 |
/>
|
| 60 |
{/* <Separator /> */}
|
| 61 |
<div className="h-4" />
|
src/components/Loader.jsx
CHANGED
|
@@ -28,11 +28,13 @@ const Loader = () => {
|
|
| 28 |
alignItems: "center",
|
| 29 |
flexDirection: "column",
|
| 30 |
gap: "10px",
|
|
|
|
|
|
|
| 31 |
}}
|
| 32 |
>
|
| 33 |
<div className="lg:hidden">
|
| 34 |
<p className="p-1">
|
| 35 |
-
web-llm-embed
|
| 36 |
</p>
|
| 37 |
<p className="p-1">
|
| 38 |
No data is sent to the server. Conversations are cached in local
|
|
@@ -44,6 +46,9 @@ const Loader = () => {
|
|
| 44 |
<p className="p-1">
|
| 45 |
Powered by Apache TVM and MLC Relax Runtime.
|
| 46 |
</p>
|
|
|
|
|
|
|
|
|
|
| 47 |
</div>
|
| 48 |
<div>
|
| 49 |
This will download the model and may take a few minutes. After the
|
|
|
|
| 28 |
alignItems: "center",
|
| 29 |
flexDirection: "column",
|
| 30 |
gap: "10px",
|
| 31 |
+
fontSize: "24px",
|
| 32 |
+
textAlign: "center"
|
| 33 |
}}
|
| 34 |
>
|
| 35 |
<div className="lg:hidden">
|
| 36 |
<p className="p-1">
|
| 37 |
+
<b>web-llm-embed</b>
|
| 38 |
</p>
|
| 39 |
<p className="p-1">
|
| 40 |
No data is sent to the server. Conversations are cached in local
|
|
|
|
| 46 |
<p className="p-1">
|
| 47 |
Powered by Apache TVM and MLC Relax Runtime.
|
| 48 |
</p>
|
| 49 |
+
<p className="p-1">
|
| 50 |
+
Model is currently Vicuna 13B trained by LLMSys
|
| 51 |
+
</p>
|
| 52 |
</div>
|
| 53 |
<div>
|
| 54 |
This will download the model and may take a few minutes. After the
|
src/components/MessageList.jsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import useLLM from "@react-llm/headless";
|
| 2 |
import { useEffect, useRef } from "react";
|
|
|
|
| 3 |
|
| 4 |
function MessageList({
|
| 5 |
screenName = "endlessbox5",
|
|
@@ -20,7 +21,7 @@ function MessageList({
|
|
| 20 |
}, [conversation, messages.length]);
|
| 21 |
|
| 22 |
return (
|
| 23 |
-
<
|
| 24 |
<div className="p-2 leading-6 w-full min-h-full">
|
| 25 |
{conversation?.messages.map((m) => (
|
| 26 |
<div key={m.id} style={{ display: "flex" }}>
|
|
@@ -44,7 +45,7 @@ function MessageList({
|
|
| 44 |
))}
|
| 45 |
<div ref={scrollRef}></div>
|
| 46 |
</div>
|
| 47 |
-
</
|
| 48 |
);
|
| 49 |
}
|
| 50 |
|
|
|
|
| 1 |
import useLLM from "@react-llm/headless";
|
| 2 |
import { useEffect, useRef } from "react";
|
| 3 |
+
import { ScrollView } from "react95";
|
| 4 |
|
| 5 |
function MessageList({
|
| 6 |
screenName = "endlessbox5",
|
|
|
|
| 21 |
}, [conversation, messages.length]);
|
| 22 |
|
| 23 |
return (
|
| 24 |
+
<ScrollView style={{ height: "350px" }} className="w-full">
|
| 25 |
<div className="p-2 leading-6 w-full min-h-full">
|
| 26 |
{conversation?.messages.map((m) => (
|
| 27 |
<div key={m.id} style={{ display: "flex" }}>
|
|
|
|
| 45 |
))}
|
| 46 |
<div ref={scrollRef}></div>
|
| 47 |
</div>
|
| 48 |
+
</ScrollView>
|
| 49 |
);
|
| 50 |
}
|
| 51 |
|
src/pages/api/home.context.tsx
DELETED
|
@@ -1,23 +0,0 @@
|
|
| 1 |
-
import { Dispatch, createContext } from 'react';
|
| 2 |
-
|
| 3 |
-
import { ActionType } from '@/utils/';
|
| 4 |
-
|
| 5 |
-
import { Conversation } from '@/types/chat';
|
| 6 |
-
import { KeyValuePair } from '@/types/data';
|
| 7 |
-
|
| 8 |
-
import { HomeInitialState } from './home.state';
|
| 9 |
-
|
| 10 |
-
export interface HomeContextProps {
|
| 11 |
-
state: HomeInitialState;
|
| 12 |
-
dispatch: Dispatch<ActionType<HomeInitialState>>;
|
| 13 |
-
handleNewConversation: () => void;
|
| 14 |
-
handleSelectConversation: (conversation: Conversation) => void;
|
| 15 |
-
handleUpdateConversation: (
|
| 16 |
-
conversation: Conversation,
|
| 17 |
-
data: KeyValuePair,
|
| 18 |
-
) => void;
|
| 19 |
-
}
|
| 20 |
-
|
| 21 |
-
const HomeContext = createContext<HomeContextProps>(undefined!);
|
| 22 |
-
|
| 23 |
-
export default HomeContext;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/pages/api/home.state.tsx
DELETED
|
@@ -1,42 +0,0 @@
|
|
| 1 |
-
import { Conversation, Message } from '@/types/chat';
|
| 2 |
-
import { Prompt } from '@/types/prompt';
|
| 3 |
-
|
| 4 |
-
export interface HomeInitialState {
|
| 5 |
-
loading: boolean;
|
| 6 |
-
lightMode: 'light' | 'dark';
|
| 7 |
-
messageIsStreaming: boolean;
|
| 8 |
-
modelError: any | null;
|
| 9 |
-
models: any[];
|
| 10 |
-
conversations: Conversation[];
|
| 11 |
-
selectedConversation: Conversation | undefined;
|
| 12 |
-
currentMessage: Message | undefined;
|
| 13 |
-
prompts: Prompt[];
|
| 14 |
-
temperature: number;
|
| 15 |
-
showChatbar: boolean;
|
| 16 |
-
showPromptbar: boolean;
|
| 17 |
-
messageError: boolean;
|
| 18 |
-
searchTerm: string;
|
| 19 |
-
defaultModelId: any | undefined;
|
| 20 |
-
serverSideApiKeyIsSet: boolean;
|
| 21 |
-
serverSidePluginKeysSet: boolean;
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
export const initialState: HomeInitialState = {
|
| 25 |
-
loading: false,
|
| 26 |
-
lightMode: 'dark',
|
| 27 |
-
messageIsStreaming: false,
|
| 28 |
-
modelError: null,
|
| 29 |
-
models: [],
|
| 30 |
-
conversations: [],
|
| 31 |
-
selectedConversation: undefined,
|
| 32 |
-
currentMessage: undefined,
|
| 33 |
-
prompts: [],
|
| 34 |
-
temperature: 1,
|
| 35 |
-
showPromptbar: true,
|
| 36 |
-
showChatbar: true,
|
| 37 |
-
messageError: false,
|
| 38 |
-
searchTerm: '',
|
| 39 |
-
defaultModelId: undefined,
|
| 40 |
-
serverSideApiKeyIsSet: false,
|
| 41 |
-
serverSidePluginKeysSet: false,
|
| 42 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/pages/api/home.tsx
DELETED
|
@@ -1,157 +0,0 @@
|
|
| 1 |
-
import { useEffect, useRef } from 'react';
|
| 2 |
-
|
| 3 |
-
import { GetServerSideProps } from 'next';
|
| 4 |
-
import Head from 'next/head';
|
| 5 |
-
import {
|
| 6 |
-
DEFAULT_SYSTEM_PROMPT,
|
| 7 |
-
DEFAULT_TEMPERATURE,
|
| 8 |
-
saveConversation,
|
| 9 |
-
saveConversations,
|
| 10 |
-
updateConversation,
|
| 11 |
-
useCreateReducer
|
| 12 |
-
} from '@/utils';
|
| 13 |
-
|
| 14 |
-
import { Chat } from '@/components/Chat/Chat';
|
| 15 |
-
|
| 16 |
-
import HomeContext from './home.context';
|
| 17 |
-
import { HomeInitialState, initialState } from './home.state';
|
| 18 |
-
|
| 19 |
-
import { v4 as uuidv4 } from 'uuid';
|
| 20 |
-
|
| 21 |
-
interface Props {
|
| 22 |
-
serverSideApiKeyIsSet: boolean;
|
| 23 |
-
serverSidePluginKeysSet: boolean;
|
| 24 |
-
defaultModelId: any;
|
| 25 |
-
}
|
| 26 |
-
|
| 27 |
-
const Home = ({
|
| 28 |
-
defaultModelId,
|
| 29 |
-
}: Props) => {
|
| 30 |
-
const contextValue = useCreateReducer<HomeInitialState>({
|
| 31 |
-
initialState,
|
| 32 |
-
});
|
| 33 |
-
|
| 34 |
-
const {
|
| 35 |
-
state: {
|
| 36 |
-
lightMode,
|
| 37 |
-
conversations,
|
| 38 |
-
selectedConversation
|
| 39 |
-
},
|
| 40 |
-
dispatch,
|
| 41 |
-
} = contextValue;
|
| 42 |
-
|
| 43 |
-
const stopConversationRef = useRef<boolean>(false);
|
| 44 |
-
|
| 45 |
-
// FETCH MODELS ----------------------------------------------
|
| 46 |
-
|
| 47 |
-
const handleSelectConversation = (conversation: any) => {
|
| 48 |
-
dispatch({
|
| 49 |
-
field: 'selectedConversation',
|
| 50 |
-
value: conversation,
|
| 51 |
-
});
|
| 52 |
-
|
| 53 |
-
saveConversation(conversation);
|
| 54 |
-
};
|
| 55 |
-
|
| 56 |
-
// CONVERSATION OPERATIONS --------------------------------------------
|
| 57 |
-
|
| 58 |
-
const handleNewConversation = () => {
|
| 59 |
-
const lastConversation = conversations[conversations.length - 1];
|
| 60 |
-
|
| 61 |
-
const newConversation: any = {
|
| 62 |
-
id: uuidv4(),
|
| 63 |
-
name: 'New Conversation',
|
| 64 |
-
messages: [],
|
| 65 |
-
model: lastConversation?.model || {
|
| 66 |
-
id: "OpenAIModels[defaultModelId].id",
|
| 67 |
-
name: "OpenAIModels[defaultModelId].name",
|
| 68 |
-
maxLength: "OpenAIModels[defaultModelId].maxLength",
|
| 69 |
-
tokenLimit: "OpenAIModels[defaultModelId].tokenLimit",
|
| 70 |
-
},
|
| 71 |
-
prompt: DEFAULT_SYSTEM_PROMPT,
|
| 72 |
-
temperature: lastConversation?.temperature ?? DEFAULT_TEMPERATURE,
|
| 73 |
-
folderId: null,
|
| 74 |
-
};
|
| 75 |
-
|
| 76 |
-
const updatedConversations = [...conversations, newConversation];
|
| 77 |
-
|
| 78 |
-
dispatch({ field: 'selectedConversation', value: newConversation });
|
| 79 |
-
dispatch({ field: 'conversations', value: updatedConversations });
|
| 80 |
-
|
| 81 |
-
saveConversation(newConversation);
|
| 82 |
-
saveConversations(updatedConversations);
|
| 83 |
-
|
| 84 |
-
dispatch({ field: 'loading', value: false });
|
| 85 |
-
};
|
| 86 |
-
|
| 87 |
-
const handleUpdateConversation = (
|
| 88 |
-
conversation: any,
|
| 89 |
-
data: any,
|
| 90 |
-
) => {
|
| 91 |
-
const updatedConversation = {
|
| 92 |
-
...conversation,
|
| 93 |
-
[data.key]: data.value,
|
| 94 |
-
};
|
| 95 |
-
|
| 96 |
-
const { single, all } = updateConversation(
|
| 97 |
-
updatedConversation,
|
| 98 |
-
conversations,
|
| 99 |
-
);
|
| 100 |
-
|
| 101 |
-
dispatch({ field: 'selectedConversation', value: single });
|
| 102 |
-
dispatch({ field: 'conversations', value: all });
|
| 103 |
-
};
|
| 104 |
-
|
| 105 |
-
// EFFECTS --------------------------------------------
|
| 106 |
-
|
| 107 |
-
useEffect(() => {
|
| 108 |
-
if (window.innerWidth < 640) {
|
| 109 |
-
dispatch({ field: 'showChatbar', value: false });
|
| 110 |
-
}
|
| 111 |
-
}, [selectedConversation]);
|
| 112 |
-
|
| 113 |
-
// ON LOAD --------------------------------------------
|
| 114 |
-
|
| 115 |
-
return (
|
| 116 |
-
<HomeContext.Provider
|
| 117 |
-
value={{
|
| 118 |
-
...contextValue,
|
| 119 |
-
handleNewConversation,
|
| 120 |
-
handleSelectConversation,
|
| 121 |
-
handleUpdateConversation,
|
| 122 |
-
}}
|
| 123 |
-
>
|
| 124 |
-
<Head>
|
| 125 |
-
<title>Web LLM Embed</title>
|
| 126 |
-
<meta name="description" content="Web LLM Embed" />
|
| 127 |
-
<meta
|
| 128 |
-
name="viewport"
|
| 129 |
-
content="height=device-height ,width=device-width, initial-scale=1, user-scalable=no"
|
| 130 |
-
/>
|
| 131 |
-
<link rel="icon" href="/favicon.ico" />
|
| 132 |
-
</Head>
|
| 133 |
-
<main
|
| 134 |
-
className={`flex h-screen w-screen flex-col text-sm text-white dark:text-white ${lightMode}`}
|
| 135 |
-
>
|
| 136 |
-
<div className="flex h-full w-full pt-[48px] sm:pt-0">
|
| 137 |
-
<div className="flex flex-1">
|
| 138 |
-
<Chat stopConversationRef={stopConversationRef} />
|
| 139 |
-
</div>
|
| 140 |
-
</div>
|
| 141 |
-
</main>
|
| 142 |
-
</HomeContext.Provider>
|
| 143 |
-
);
|
| 144 |
-
};
|
| 145 |
-
export default Home;
|
| 146 |
-
|
| 147 |
-
export const getServerSideProps: GetServerSideProps = async ({ locale }) => {
|
| 148 |
-
const defaultModelId = "fallbackModelID"
|
| 149 |
-
|
| 150 |
-
return {
|
| 151 |
-
props: {
|
| 152 |
-
defaultModelId
|
| 153 |
-
},
|
| 154 |
-
};
|
| 155 |
-
};
|
| 156 |
-
|
| 157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/utils/file-handler.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
|
|
| 1 |
import fs from 'fs-extra';
|
| 2 |
-
import type { OpenAIEmbeddings } from 'langchain/embeddings/openai';
|
| 3 |
import {
|
| 4 |
HNSWLib,
|
| 5 |
type HNSWLib as StoreTypeHNSWLib,
|
|
@@ -26,7 +26,7 @@ const HNSWLibModelFilesName = {
|
|
| 26 |
// looking forward to a better way to transfrom hnswlibStore <=> indexes
|
| 27 |
export async function HNSWLibModelToVectorStore(
|
| 28 |
model: HNSWLibModel,
|
| 29 |
-
embeddings:
|
| 30 |
) {
|
| 31 |
await saveHNSWLibModelToLocal(model);
|
| 32 |
// load from dir
|
|
|
|
| 1 |
+
import { XenovaTransformersEmbeddings } from '@/embed/hf';
|
| 2 |
import fs from 'fs-extra';
|
|
|
|
| 3 |
import {
|
| 4 |
HNSWLib,
|
| 5 |
type HNSWLib as StoreTypeHNSWLib,
|
|
|
|
| 26 |
// looking forward to a better way to transfrom hnswlibStore <=> indexes
|
| 27 |
export async function HNSWLibModelToVectorStore(
|
| 28 |
model: HNSWLibModel,
|
| 29 |
+
embeddings: XenovaTransformersEmbeddings,
|
| 30 |
) {
|
| 31 |
await saveHNSWLibModelToLocal(model);
|
| 32 |
// load from dir
|
src/utils/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
import { Conversation } from '@/types/chat';
|
| 2 |
import { useMemo, useReducer } from 'react';
|
| 3 |
|
| 4 |
// Extracts property names from initial state of reducer to allow typesafe dispatch objects
|
|
@@ -30,35 +29,6 @@ export const useCreateReducer = <T>({ initialState }: { initialState: T }) => {
|
|
| 30 |
return useMemo(() => ({ state, dispatch }), [state, dispatch]);
|
| 31 |
};
|
| 32 |
|
| 33 |
-
export const updateConversation = (
|
| 34 |
-
updatedConversation: Conversation,
|
| 35 |
-
allConversations: Conversation[],
|
| 36 |
-
) => {
|
| 37 |
-
const updatedConversations = allConversations.map((c) => {
|
| 38 |
-
if (c.id === updatedConversation.id) {
|
| 39 |
-
return updatedConversation;
|
| 40 |
-
}
|
| 41 |
-
|
| 42 |
-
return c;
|
| 43 |
-
});
|
| 44 |
-
|
| 45 |
-
saveConversation(updatedConversation);
|
| 46 |
-
saveConversations(updatedConversations);
|
| 47 |
-
|
| 48 |
-
return {
|
| 49 |
-
single: updatedConversation,
|
| 50 |
-
all: updatedConversations,
|
| 51 |
-
};
|
| 52 |
-
};
|
| 53 |
-
|
| 54 |
-
export const saveConversation = (conversation: Conversation) => {
|
| 55 |
-
localStorage.setItem('selectedConversation', JSON.stringify(conversation));
|
| 56 |
-
};
|
| 57 |
-
|
| 58 |
-
export const saveConversations = (conversations: Conversation[]) => {
|
| 59 |
-
localStorage.setItem('conversationHistory', JSON.stringify(conversations));
|
| 60 |
-
};
|
| 61 |
-
|
| 62 |
|
| 63 |
export function throttle<T extends (...args: any[]) => any>(
|
| 64 |
func: T,
|
|
|
|
|
|
|
| 1 |
import { useMemo, useReducer } from 'react';
|
| 2 |
|
| 3 |
// Extracts property names from initial state of reducer to allow typesafe dispatch objects
|
|
|
|
| 29 |
return useMemo(() => ({ state, dispatch }), [state, dispatch]);
|
| 30 |
};
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
export function throttle<T extends (...args: any[]) => any>(
|
| 34 |
func: T,
|