diff --git a/src/chat-manager.ts b/src/chat-manager.ts index f04720d..a4b96e2 100644 --- a/src/chat-manager.ts +++ b/src/chat-manager.ts @@ -2,7 +2,7 @@ import { BroadcastChannel } from 'broadcast-channel'; import EventEmitter from 'events'; import MiniSearch, { SearchResult } from 'minisearch' import { v4 as uuidv4 } from 'uuid'; -import { Chat, getOpenAIMessageFromMessage, Message, UserSubmittedMessage } from './types'; +import { Chat, getOpenAIMessageFromMessage, Message, Parameters, UserSubmittedMessage } from './types'; import { MessageTree } from './message-tree'; import { createStreamingChatCompletion } from './openai'; import { createTitle } from './titles'; @@ -81,18 +81,7 @@ export class ChatManager extends EventEmitter { done: true, }; - const reply: Message = { - id: uuidv4(), - parentID: newMessage.id, - chatID: chat.id, - timestamp: Date.now(), - role: 'assistant', - content: '', - done: false, - }; - chat.messages.addMessage(newMessage); - chat.messages.addMessage(reply); chat.updated = Date.now(); this.emit(chat.id); @@ -103,10 +92,51 @@ export class ChatManager extends EventEmitter { ? chat.messages.getMessageChainTo(message.parentID) : []; messages.push(newMessage); - + + await this.getReply(messages, message.requestedParameters); + } + + public async regenerate(message: Message, requestedParameters: Parameters) { + const chat = this.chats.get(message.chatID); + + if (!chat) { + throw new Error('Chat not found'); + } + + const messages: Message[] = message.parentID + ? chat.messages.getMessageChainTo(message.parentID) + : []; + + await this.getReply(messages, requestedParameters); + } + + private async getReply(messages: Message[], requestedParameters: Parameters) { + const latestMessage = messages[messages.length - 1]; + const chat = this.chats.get(latestMessage.chatID); + + if (!chat) { + throw new Error('Chat not found'); + } + + const reply: Message = { + id: uuidv4(), + parentID: latestMessage.id, + chatID: latestMessage.chatID, + timestamp: Date.now(), + role: 'assistant', + content: '', + done: false, + }; + + chat.messages.addMessage(reply); + chat.updated = Date.now(); + + this.emit(chat.id); + channel.postMessage({ type: 'chat-update', data: chat }); + const messagesToSend = selectMessagesToSendSafely(messages.map(getOpenAIMessageFromMessage)); - const response = await createStreamingChatCompletion(messagesToSend, message.requestedParameters); + const response = await createStreamingChatCompletion(messagesToSend, requestedParameters); response.on('error', () => { if (!reply.content) { @@ -139,7 +169,7 @@ export class ChatManager extends EventEmitter { setTimeout(() => this.search.update(chat), 500); if (!chat.title) { - chat.title = await createTitle(chat, message.requestedParameters.apiKey); + chat.title = await createTitle(chat, requestedParameters.apiKey); if (chat.title) { this.emit(chat.id); this.emit('title', chat.id, chat.title); diff --git a/src/components/message.tsx b/src/components/message.tsx index b3489bb..f8ac335 100644 --- a/src/components/message.tsx +++ b/src/components/message.tsx @@ -1,10 +1,12 @@ import styled from '@emotion/styled'; -import { Button, CopyButton, Loader } from '@mantine/core'; +import { Button, CopyButton, Loader, Textarea } from '@mantine/core'; +import { useState } from 'react'; import { Message } from "../types"; import { share } from '../utils'; import { ElevenLabsReaderButton } from '../elevenlabs'; import { Markdown } from './markdown'; +import { useAppContext } from '../context'; // hide for everyone but screen readers const SROnly = styled.span` @@ -169,6 +171,17 @@ const EndOfChatMarker = styled.div` background: rgba(255, 255, 255, 0.1); `; +const Editor = styled.div` + max-width: 50rem; + margin-left: auto; + margin-right: auto; + margin-top: 0.5rem; + + .mantine-Button-root { + margin-top: 1rem; + } +`; + function getRoleName(role: string, share = false) { switch (role) { case 'user': @@ -193,6 +206,10 @@ function InlineLoader() { } export default function MessageComponent(props: { message: Message, last: boolean, share?: boolean }) { + const context = useAppContext(); + const [editing, setEditing] = useState(false); + const [content, setContent] = useState(''); + if (props.message.role === 'system') { return null; } @@ -222,8 +239,30 @@ export default function MessageComponent(props: { message: Message, last: boolea Share )} + {!context.isShare && props.message.role === 'user' && ( + + )} + {!context.isShare && props.message.role === 'assistant' && ( + + )} - + {!editing && } + {editing && ( +