import * as React from 'react'; import { useState, useEffect, useRef } from 'react'; import { Avatar, Center, Container, Divider, Flex, Grid, Stack, TypographyStylesProvider } from '@mantine/core'; import { Input, Tooltip } from '@mantine/core'; import { List } from '@mantine/core'; import { ScrollArea } from '@mantine/core'; import { createStyles, keyframes } from '@mantine/core'; import { ActionIcon } from '@mantine/core'; import { Menu, Button, Text } from '@mantine/core'; import { useListState, useViewportSize } from '@mantine/hooks'; import { IconClick, IconEdit, IconFolder, IconGitCompare, IconMessageDots, IconRobot, IconSend, IconSquareRoundedPlus, IconUser } from '@tabler/icons-react'; import { IconSettings, IconSearch, IconPhoto, IconMessageCircle, IconTrash, IconArrowsLeftRight } from '@tabler/icons-react'; import { Prism } from '@mantine/prism'; import ReactMarkdown from 'react-markdown'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import okaidia from 'react-syntax-highlighter/dist/esm/styles/prism/okaidia'; import messageUtil from '../util/MessageUtil'; const blink = keyframes({ '50%': { opacity: 0 }, }); const useStyles = createStyles((theme, _params, classNames) => ({ panel: { height: '100%', backgroundColor: theme.colors.gray[0], }, plusMenu: { top: 'unset !important', left: '31px !important', bottom: 60, }, commandMenu: { top: 'unset !important', left: '31px !important', bottom: 60, }, commandText: { fontSize: '1.0rem', fontWeight: 'bolder', }, commandDesc: { fontSize: '0.8rem', color: theme.colors.gray[6], }, responseContent: { marginTop: 0, marginLeft: 0, marginRight: 0, paddingLeft: 0, paddingRight: 0, width: 'calc(100% - 62px)', }, icon: { pointerEvents: 'all', }, avatar: { marginTop: 8, marginLeft: 8, }, messageBody: { }, cursor: { animation: `${blink} 0.5s infinite;` } })); const chatPanel = () => { const inputRef = useRef(null); const [messages, handlers] = useListState<{ type: string; message: string; }>([]); const [showCursor, setShowCursor] = useState(false); const [registed, setRegisted] = useState(false); const [opened, setOpened] = useState(false); const [input, setInput] = useState(''); const [commandOpened, setCommandOpened] = useState(false); const { classes } = useStyles(); const { height, width } = useViewportSize(); const handlePlusClick = (event: React.MouseEvent) => { setOpened(!opened); event.stopPropagation(); }; const handleContainerClick = (event: React.MouseEvent) => { if (opened) { setOpened(false); } }; const handleSendClick = (event: React.MouseEvent) => { if (input) { // Add the user's message to the chat UI handlers.append({ type: 'user', message: input }); // Clear the input field setInput(''); setShowCursor(true); // Process and send the message to the extension messageUtil.sendMessage({ command: 'sendMessage', text: input }); } }; useEffect(() => { // @ts-ignore inputRef?.current?.focus(); }, []); useEffect(() => { if (registed) return; // Add the received message to the chat UI as a bot message messageUtil.registerHandler('receiveMessagePartial', (message: { text: string; }) => { console.log(`receiveMessagePartial: ${message.text}`); handlers.append({ type: 'bot', message: message.text }); setRegisted(true); }); }, [registed]); // 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) => { const value = event.target.value; // if value start with '/' command show menu if (value === '/') { setCommandOpened(true); } else { setCommandOpened(false); } setInput(value); }; const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { handleSendClick(event as any); } }; const defaultMessages = (
No messages yet
); const messageList = messages.map(({ message: messageText, type: messageType }, index) => { // setMessage(messageText); return (<> { messageType === 'bot' ? : } ) : ( {children} ); } }} > {messageText} {/* {markdown}{showCursor && |} */} {index !== messages.length - 1 && } ); }); return ( {messageList.length > 0 ? messageList : defaultMessages} }>Select code or file & right click }>Add `git diff --cached` }>Add `git diff HEAD` }>Add folder structure }>Select previous chat Context Commands { setInput('/ref ') }}> /ref Run a local CLI and add its output to the context (e.g., pytest .). { setInput('/local ') }}> /local Bypass AI and run a local CLI to check its output (e.g., git status). DevChat Bots { setInput('/code ') }}> /code Generate or update code. { setInput('/commit_message ') }}> /commit_message Write a commit message. { setInput('/doc ') }}> /doc Write a doc for reference, wiki, or discussion. } rightSection={ } value={input} onKeyDown={handleKeyDown} onChange={handleInputChange} /> ); }; export default chatPanel;