Merge pull request #21 from runjinz/message-handler

Message handler
This commit is contained in:
Rankin Zheng 2023-05-07 00:54:09 +08:00 committed by GitHub
commit 67bdf57d2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 188 additions and 137 deletions

126
package-lock.json generated
View File

@ -1969,21 +1969,21 @@
} }
}, },
"node_modules/@emotion/babel-plugin": { "node_modules/@emotion/babel-plugin": {
"version": "11.10.8", "version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.8.tgz", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
"integrity": "sha512-gxNky50AJL3AlkbjvTARiwAqei6/tNUxDZPSKd+3jqWVM3AmdVTTdpjHorR/an/M0VJqdsuq5oGcFH+rjtyujQ==", "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
"dependencies": { "dependencies": {
"@babel/helper-module-imports": "^7.16.7", "@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.0", "@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.0", "@emotion/memoize": "^0.8.1",
"@emotion/serialize": "^1.1.1", "@emotion/serialize": "^1.1.2",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0", "convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
"find-root": "^1.1.0", "find-root": "^1.1.0",
"source-map": "^0.5.7", "source-map": "^0.5.7",
"stylis": "4.1.4" "stylis": "4.2.0"
} }
}, },
"node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
@ -1999,6 +1999,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/@emotion/babel-plugin/node_modules/stylis": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
},
"node_modules/@emotion/cache": { "node_modules/@emotion/cache": {
"version": "11.10.8", "version": "11.10.8",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.8.tgz", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.8.tgz",
@ -2012,14 +2017,14 @@
} }
}, },
"node_modules/@emotion/hash": { "node_modules/@emotion/hash": {
"version": "0.9.0", "version": "0.9.1",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
"integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
}, },
"node_modules/@emotion/memoize": { "node_modules/@emotion/memoize": {
"version": "0.8.0", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
}, },
"node_modules/@emotion/react": { "node_modules/@emotion/react": {
"version": "11.10.8", "version": "11.10.8",
@ -2045,14 +2050,14 @@
} }
}, },
"node_modules/@emotion/serialize": { "node_modules/@emotion/serialize": {
"version": "1.1.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz",
"integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==", "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==",
"dependencies": { "dependencies": {
"@emotion/hash": "^0.9.0", "@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.0", "@emotion/memoize": "^0.8.1",
"@emotion/unitless": "^0.8.0", "@emotion/unitless": "^0.8.1",
"@emotion/utils": "^1.2.0", "@emotion/utils": "^1.2.1",
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
@ -2062,22 +2067,22 @@
"integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==" "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA=="
}, },
"node_modules/@emotion/unitless": { "node_modules/@emotion/unitless": {
"version": "0.8.0", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
"integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
}, },
"node_modules/@emotion/use-insertion-effect-with-fallbacks": { "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
"version": "1.0.0", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
"integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
"peerDependencies": { "peerDependencies": {
"react": ">=16.8.0" "react": ">=16.8.0"
} }
}, },
"node_modules/@emotion/utils": { "node_modules/@emotion/utils": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
"integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
}, },
"node_modules/@emotion/weak-memoize": { "node_modules/@emotion/weak-memoize": {
"version": "0.3.0", "version": "0.3.0",
@ -13811,21 +13816,21 @@
"dev": true "dev": true
}, },
"@emotion/babel-plugin": { "@emotion/babel-plugin": {
"version": "11.10.8", "version": "11.11.0",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.8.tgz", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
"integrity": "sha512-gxNky50AJL3AlkbjvTARiwAqei6/tNUxDZPSKd+3jqWVM3AmdVTTdpjHorR/an/M0VJqdsuq5oGcFH+rjtyujQ==", "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
"requires": { "requires": {
"@babel/helper-module-imports": "^7.16.7", "@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.0", "@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.0", "@emotion/memoize": "^0.8.1",
"@emotion/serialize": "^1.1.1", "@emotion/serialize": "^1.1.2",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0", "convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
"find-root": "^1.1.0", "find-root": "^1.1.0",
"source-map": "^0.5.7", "source-map": "^0.5.7",
"stylis": "4.1.4" "stylis": "4.2.0"
}, },
"dependencies": { "dependencies": {
"convert-source-map": { "convert-source-map": {
@ -13837,6 +13842,11 @@
"version": "0.5.7", "version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
},
"stylis": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
} }
} }
}, },
@ -13853,14 +13863,14 @@
} }
}, },
"@emotion/hash": { "@emotion/hash": {
"version": "0.9.0", "version": "0.9.1",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
"integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
}, },
"@emotion/memoize": { "@emotion/memoize": {
"version": "0.8.0", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
}, },
"@emotion/react": { "@emotion/react": {
"version": "11.10.8", "version": "11.10.8",
@ -13878,14 +13888,14 @@
} }
}, },
"@emotion/serialize": { "@emotion/serialize": {
"version": "1.1.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz",
"integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==", "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==",
"requires": { "requires": {
"@emotion/hash": "^0.9.0", "@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.0", "@emotion/memoize": "^0.8.1",
"@emotion/unitless": "^0.8.0", "@emotion/unitless": "^0.8.1",
"@emotion/utils": "^1.2.0", "@emotion/utils": "^1.2.1",
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
@ -13895,20 +13905,20 @@
"integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==" "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA=="
}, },
"@emotion/unitless": { "@emotion/unitless": {
"version": "0.8.0", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
"integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
}, },
"@emotion/use-insertion-effect-with-fallbacks": { "@emotion/use-insertion-effect-with-fallbacks": {
"version": "1.0.0", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
"integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
"requires": {} "requires": {}
}, },
"@emotion/utils": { "@emotion/utils": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
"integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
}, },
"@emotion/weak-memoize": { "@emotion/weak-memoize": {
"version": "0.3.0", "version": "0.3.0",

View File

@ -2,7 +2,7 @@ import * as React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import { MantineProvider } from '@mantine/core'; import { MantineProvider } from '@mantine/core';
import App from './App'; import App from './views/App';
const container = document.getElementById('app')!; const container = document.getElementById('app')!;
const root = createRoot(container); // createRoot(container!) if you use TypeScript const root = createRoot(container); // createRoot(container!) if you use TypeScript

View File

@ -25,7 +25,8 @@ export default class WebviewManager {
} }
private _getHtmlContent(): string { private _getHtmlContent(): string {
const htmlPath = vscode.Uri.joinPath(this._extensionUri, 'dist', 'assets', 'chatPanel.html'); // const htmlPath = vscode.Uri.joinPath(this._extensionUri, 'dist', 'assets', 'chatPanel.html');
const htmlPath = vscode.Uri.joinPath(this._extensionUri, 'dist', 'index.html');
const htmlContent = fs.readFileSync(htmlPath.fsPath, 'utf8'); const htmlContent = fs.readFileSync(htmlPath.fsPath, 'utf8');
return htmlContent.replace(/<vscode-resource:(\/.+?)>/g, (_, resourcePath) => { return htmlContent.replace(/<vscode-resource:(\/.+?)>/g, (_, resourcePath) => {

View File

@ -3,9 +3,32 @@
const vscodeApi = window.acquireVsCodeApi(); const vscodeApi = window.acquireVsCodeApi();
class MessageUtil { class MessageUtil {
private static instance: MessageUtil;
handlers: { [x: string]: any; }; handlers: { [x: string]: any; };
messageListener: any;
constructor() { constructor() {
this.handlers = {}; this.handlers = {};
this.messageListener = null;
if (!this.messageListener) {
this.messageListener = (event: { data: any; }) => {
const message = event.data;
this.handleMessage(message);
};
window.addEventListener('message', this.messageListener);
} else {
console.log('Message listener has already been bound.');
}
}
public static getInstance(): MessageUtil {
if (!MessageUtil.instance) {
MessageUtil.instance = new MessageUtil();
}
return MessageUtil.instance;
} }
// Register a message handler for a specific message type // Register a message handler for a specific message type
@ -42,4 +65,4 @@ class MessageUtil {
} }
// Export the MessageUtil class as a module // Export the MessageUtil class as a module
export default MessageUtil; export default MessageUtil.getInstance();

View File

@ -1,18 +1,23 @@
import * as React from 'react'; import * as React from 'react';
import { useState } from 'react'; import { useState, useEffect } from 'react';
import { Avatar, Container, Divider, Flex, Grid, Stack, TypographyStylesProvider } from '@mantine/core'; import { Avatar, Center, Container, Divider, Flex, Grid, Stack, TypographyStylesProvider } from '@mantine/core';
import { Input, Tooltip } from '@mantine/core'; import { Input, Tooltip } from '@mantine/core';
import { List } from '@mantine/core'; import { List } from '@mantine/core';
import { ScrollArea } from '@mantine/core'; import { ScrollArea } from '@mantine/core';
import { createStyles } from '@mantine/core'; import { createStyles, keyframes } from '@mantine/core';
import { ActionIcon } from '@mantine/core'; import { ActionIcon } from '@mantine/core';
import { Menu, Button, Text } from '@mantine/core'; import { Menu, Button, Text } from '@mantine/core';
import { useViewportSize } from '@mantine/hooks'; import { useListState, useViewportSize } from '@mantine/hooks';
import { IconEdit, IconRobot, IconSend, IconSquareRoundedPlus, IconUser } from '@tabler/icons-react'; import { IconEdit, IconRobot, IconSend, IconSquareRoundedPlus, IconUser } from '@tabler/icons-react';
import { IconSettings, IconSearch, IconPhoto, IconMessageCircle, IconTrash, IconArrowsLeftRight } from '@tabler/icons-react'; import { IconSettings, IconSearch, IconPhoto, IconMessageCircle, IconTrash, IconArrowsLeftRight } from '@tabler/icons-react';
import { Prism } from '@mantine/prism'; import { Prism } from '@mantine/prism';
import { useRemark } from 'react-remark'; import { useRemark, Remark } from 'react-remark';
import MessageUtil from '../utils/MessageUtil'; import messageUtil from '../util/MessageUtil';
const blink = keyframes({
'50%': { opacity: 0 },
});
const useStyles = createStyles((theme, _params, classNames) => ({ const useStyles = createStyles((theme, _params, classNames) => ({
panel: { panel: {
@ -38,9 +43,12 @@ const useStyles = createStyles((theme, _params, classNames) => ({
color: theme.colors.gray[6], color: theme.colors.gray[6],
}, },
responseContent: { responseContent: {
marginTop: 8, marginTop: 0,
marginLeft: 0, marginLeft: 0,
marginRight: 0, marginRight: 0,
paddingLeft: 0,
paddingRight: 0,
width: 'calc(100% - 62px)',
}, },
icon: { icon: {
pointerEvents: 'all', pointerEvents: 'all',
@ -51,28 +59,25 @@ const useStyles = createStyles((theme, _params, classNames) => ({
}, },
messageBody: { messageBody: {
}, },
cursor: {
animation: `${blink} 0.5s infinite;`
}
})); }));
const chatPanel = () => { const chatPanel = () => {
const [reactContent, setMarkdownSource] = useRemark(); const [messages, handlers] = useListState<{ type: string; message: string; }>([]);
const [showCursor, setShowCursor] = useState(false);
const [registed, setRegisted] = useState(false);
const [opened, setOpened] = useState(false); const [opened, setOpened] = useState(false);
// const [markdown, setMarkdown] = useRemark();
// const [message, setMessage] = useState('');
const [input, setInput] = useState(''); const [input, setInput] = useState('');
const [commandOpened, setCommandOpened] = useState(false); const [commandOpened, setCommandOpened] = useState(false);
const { classes } = useStyles(); const { classes } = useStyles();
const { height, width } = useViewportSize(); const { height, width } = useViewportSize();
const messageUtil = new MessageUtil();
const demoCode = `import { Button } from '@mantine/core'; const handlePlusClick = (event: React.MouseEvent<HTMLButtonElement>) => {
function Demo() {
return <Button>Hello</Button>
}`;
setMarkdownSource(`# code block
print '3 backticks or'
print 'indent 4 spaces'`);
const handlePlusBottonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setOpened(!opened); setOpened(!opened);
event.stopPropagation(); event.stopPropagation();
}; };
@ -80,35 +85,45 @@ const chatPanel = () => {
if (opened) { setOpened(false); } if (opened) { setOpened(false); }
}; };
const handleSendClick = (event: React.MouseEvent<HTMLButtonElement>) => { const handleSendClick = (event: React.MouseEvent<HTMLButtonElement>) => {
const message = input; if (input) {
if (message) {
// Add the user's message to the chat UI // Add the user's message to the chat UI
// addMessageToUI('user', message); handlers.append({ type: 'user', message: input });
// Clear the input field // Clear the input field
event.currentTarget.value = ''; setInput('');
setShowCursor(true);
// Process and send the message to the extension // Process and send the message to the extension
messageUtil.sendMessage({ messageUtil.sendMessage({
command: 'sendMessage', command: 'sendMessage',
text: message text: input
}); });
} }
}; };
// Register message handlers for receiving messages from the extension useEffect(() => {
messageUtil.registerHandler('receiveMessage', (message: { text: string; }) => { if (registed) return;
console.log(`receiveMessage: ${message.text}`);
// Add the received message to the chat UI as a bot message // Add the received message to the chat UI as a bot message
setMarkdownSource(message.text); messageUtil.registerHandler('receiveMessage', (message: { text: string; }) => {
}); console.log(`receiveMessage: ${message.text}`);
handlers.append({ type: 'bot', message: message.text });
messageUtil.registerHandler('receiveMessagePartial', (message: { text: string; }) => { setRegisted(true);
console.log(`receiveMessagePartial: ${message.text}`); });
// Add the received message to the chat UI as a bot message }, [registed]);
setMarkdownSource(message.text);
});
// useEffect(() => {
// let current = 0;
// const interval = setInterval(() => {
// if (current >= message.length) {
// clearInterval(interval);
// setShowCursor(false);
// return;
// }
// setMarkdown(message.slice(0, current + 1));
// current++;
// }, 25);
// return () => clearInterval(interval);
// }, [message]);
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value; const value = event.target.value;
@ -121,47 +136,47 @@ const chatPanel = () => {
setInput(value); setInput(value);
}; };
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
handleSendClick(event as any);
}
};
const defaultMessages = (<Center>
<Text size="lg" color="gray" weight={500}>No messages yet</Text>
</Center>);
const messageList = messages.map(({ message: messageText, type: messageType }, index) => {
// setMessage(messageText);
return (<>
<Flex
mih={50}
gap="md"
justify="flex-start"
align="flex-start"
direction="row"
wrap="wrap"
className={classes.messageBody}
>
{
messageType === 'bot'
? <Avatar color="indigo" size='md' radius="xl" className={classes.avatar}><IconRobot size="1.5rem" /></Avatar>
: <Avatar color="cyan" size='md' radius="xl" className={classes.avatar}><IconUser size="1.5rem" /></Avatar>
}
<Container className={classes.responseContent}>
<Remark>{messageText}</Remark>
{/* {markdown}{showCursor && <span className={classes.cursor}>|</span>} */}
</Container>
</Flex>
{index !== messages.length - 1 && <Divider my="sm" />}
</>);
});
return ( return (
<Container className={classes.panel} onClick={handleContainerClick}> <Container className={classes.panel} onClick={handleContainerClick}>
<ScrollArea h={height - 70} type="never"> <ScrollArea h={height - 70} type="never">
<Flex {messageList.length > 0 ? messageList : defaultMessages}
mih={50}
gap="md"
justify="flex-start"
align="flex-start"
direction="row"
wrap="wrap"
className={classes.messageBody}
>
<Avatar color="indigo" size='md' radius="xl" className={classes.avatar}>
<IconUser size="1.5rem" />
</Avatar>
<Container className={classes.responseContent}>
<Text>
Write a hello world, and explain it.
</Text>
</Container>
{/* <ActionIcon>
<IconEdit size="1.5rem" />
</ActionIcon> */}
</Flex>
<Divider my="sm" label="Mar 4, 2023" labelPosition="center" />
<Flex
mih={50}
gap="md"
justify="flex-start"
align="flex-start"
direction="row"
wrap="wrap"
className={classes.messageBody}
>
<Avatar color="blue" size='md' radius="xl" className={classes.avatar}>
<IconRobot size="1.5rem" />
</Avatar>
<Container className={classes.responseContent}>
{reactContent}
</Container>
</Flex>
</ScrollArea> </ScrollArea>
<Menu id='plusMenu' shadow="md" width={200} opened={opened} onChange={setOpened} > <Menu id='plusMenu' shadow="md" width={200} opened={opened} onChange={setOpened} >
<Menu.Dropdown className={classes.plusMenu}> <Menu.Dropdown className={classes.plusMenu}>
@ -238,7 +253,7 @@ const chatPanel = () => {
radius="md" radius="md"
placeholder="Send a message." placeholder="Send a message."
icon={ icon={
<ActionIcon className={classes.icon} onClick={handlePlusBottonClick}> <ActionIcon className={classes.icon} onClick={handlePlusClick}>
<IconSquareRoundedPlus size="1rem" /> <IconSquareRoundedPlus size="1rem" />
</ActionIcon> </ActionIcon>
} }
@ -247,6 +262,8 @@ const chatPanel = () => {
<IconSend size="1rem" /> <IconSend size="1rem" />
</ActionIcon> </ActionIcon>
} }
value={input}
onKeyDown={handleKeyDown}
onChange={handleInputChange} onChange={handleInputChange}
/> />
</Container> </Container>

View File

@ -69,7 +69,7 @@ const webviewConfig = {
target: 'web', target: 'web',
mode: 'development', mode: 'development',
entry: './src/views/index.tsx', entry: './src/index.tsx',
output: { output: {
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, 'dist'),
filename: 'index.js', filename: 'index.js',
@ -155,7 +155,7 @@ const webviewConfig = {
plugins: [ plugins: [
// generate an HTML file that includes the extension's JavaScript file // generate an HTML file that includes the extension's JavaScript file
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src', 'views', 'index.html'), template: path.resolve(__dirname, 'src', 'index.html'),
filename: 'index.html', filename: 'index.html',
chunks: ['index'] chunks: ['index']
}), }),