import * as React from 'react';
import { useState, useEffect, useRef } from 'react';
import { Accordion, Avatar, Box, Center, Container, Divider, Flex, Stack, px } from '@mantine/core';
import { ScrollArea } from '@mantine/core';
import { keyframes } from '@mantine/core';
import { Button, Text } from '@mantine/core';
import { useListState, useResizeObserver, useTimeout, useViewportSize } from '@mantine/hooks';
import { IconPlayerStop, IconRotateDot } from '@tabler/icons-react';
import messageUtil from '../../util/MessageUtil';
// @ts-ignore
import SvgAvatarDevChat from './avatar_devchat.svg';
// @ts-ignore
import SvgAvatarUser from './avatar_spaceman.png';
import InputMessage from './InputMessage';
import CodeBlock from './CodeBlock';
const MessageContainer = (props: any) => {
const { generating, messages, chatContainerRect, responsed } = props;
const DefaultMessage = (
No messages yet
);
const MessageAvatar = (props: any) => {
const { type } = props;
return (
{
type === 'bot'
?
:
}
{type === 'bot' ? 'DevChat' : 'User'}
);
};
const MessageContext = (props: any) => {
const { contexts } = props;
return (contexts &&
{
contexts?.map((item: any, index: number) => {
const { context } = item;
return (
{'command' in context ? context.command : context.path}
{
context.content
? context.content
:
No content
}
);
})
}
);
};
const MessageBlink = (props: any) => {
const { generating, messageType, lastMessage } = props;
const blink = keyframes({
'50%': { opacity: 0 },
});
return (generating && messageType === 'bot' && lastMessage
? |
: <>>);
};
const messageList = messages.map((item: any, index: number) => {
const { message: messageText, type: messageType, contexts } = item;
// setMessage(messageText);
return (<>
{index !== messages.length - 1 && }
>);
});
return (messageList.length > 0 ? messageList : DefaultMessage);
};
const chatPanel = () => {
const [chatContainerRef, chatContainerRect] = useResizeObserver();
const scrollViewport = useRef(null);
const [messages, messageHandlers] = useListState<{ type: string; message: string; contexts?: any[] }>([]);
const [currentMessage, setCurrentMessage] = useState('');
const [generating, setGenerating] = useState(false);
const [responsed, setResponsed] = useState(false);
const [registed, setRegisted] = useState(false);
const [hasError, setHasError] = useState(false);
const { height, width } = useViewportSize();
const [scrollPosition, onScrollPositionChange] = useState({ x: 0, y: 0 });
const [stopScrolling, setStopScrolling] = useState(false);
const messageCount = 10;
const scrollToBottom = () =>
scrollViewport?.current?.scrollTo({ top: scrollViewport.current.scrollHeight, behavior: 'smooth' });
const timer = useTimeout(() => {
// console.log(`stopScrolling:${stopScrolling}`);
if (!stopScrolling) {
scrollToBottom();
}
}, 1000);
useEffect(() => {
messageUtil.sendMessage({ command: 'regContextList' });
messageUtil.sendMessage({ command: 'regCommandList' });
messageUtil.sendMessage({ command: 'historyMessages' });
timer.start();
return () => {
timer.clear();
};
}, []);
useEffect(() => {
const sh = scrollViewport.current?.scrollHeight || 0;
const vh = scrollViewport.current?.clientHeight || 0;
const isBottom = sh < vh ? true : sh - vh - scrollPosition.y < 3;
if (isBottom) {
setStopScrolling(false);
} else {
setStopScrolling(true);
}
}, [scrollPosition]);
useEffect(() => {
if (generating) {
// new a bot message
messageHandlers.append({ type: 'bot', message: currentMessage });
}
}, [generating]);
// Add the received message to the chat UI as a bot message
useEffect(() => {
const lastIndex = messages?.length - 1;
const lastMessage = messages[lastIndex];
if (currentMessage && lastMessage?.type === 'bot') {
// update the last one bot message
messageHandlers.setItem(lastIndex, { type: 'bot', message: currentMessage });
}
timer.start();
}, [currentMessage]);
useEffect(() => {
if (messages.length > messageCount * 2) {
messageHandlers.remove(0, 1);
}
timer.start();
}, [messages]);
// Add the received message to the chat UI as a bot message
useEffect(() => {
if (registed) return;
setRegisted(true);
messageUtil.registerHandler('receiveMessagePartial', (message: { text: string; }) => {
setCurrentMessage(message.text);
setResponsed(true);
});
messageUtil.registerHandler('receiveMessage', (message: { text: string; isError: boolean }) => {
setCurrentMessage(message.text);
setGenerating(false);
setResponsed(true);
if (message.isError) {
setHasError(true);
}
});
messageUtil.registerHandler('loadHistoryMessages', (message: { command: string; entries: [{ hash: '', user: '', date: '', request: '', response: '', context: [{ content: '', role: '' }] }] }) => {
message.entries?.forEach(({ hash, user, date, request, response, context }, index) => {
if (index < message.entries.length - messageCount) return;
const contexts = context.map(({ content, role }) => ({ context: JSON.parse(content) }));
messageHandlers.append({ type: 'user', message: request, contexts: contexts });
messageHandlers.append({ type: 'bot', message: response });
});
});
}, [registed]);
const RegenerationButton = () => {
return (}
sx={{
backgroundColor: 'var(--vscode-button-background)',
}}
styles={{
icon: {
color: 'var(--vscode-button-foreground)'
},
label: {
color: 'var(--vscode-button-foreground)',
fontSize: 'var(--vscode-editor-font-size)',
}
}}
variant="white"
onClick={() => {
messageUtil.sendMessage({
command: 'regeneration'
});
messageHandlers.pop();
setHasError(false);
setGenerating(true);
setResponsed(false);
setCurrentMessage('');
}}>
Regeneration
);
};
const StopButton = () => {
return (
}
sx={{
backgroundColor: 'var(--vscode-button-background)',
}}
styles={{
icon: {
color: 'var(--vscode-button-foreground)'
},
label: {
color: 'var(--vscode-button-foreground)',
fontSize: 'var(--vscode-editor-font-size)',
}
}}
variant="white"
onClick={() => {
messageUtil.sendMessage({
command: 'stopDevChat'
});
setGenerating(false);
}}>
Stop generating
);
};
return (
{generating &&
}
{hasError &&
}
{
// Add the user's message to the chat UI
messageHandlers.append({ type: 'user', message: input, contexts: contexts ? [...contexts].map((item) => ({ ...item })) : undefined });
// start generating
setGenerating(true);
setResponsed(false);
setCurrentMessage('');
}} />
);
};
export default chatPanel;