feat: message actions tooltip
This commit is contained in:
parent
26bd092f03
commit
5ae319db33
@ -3,6 +3,7 @@ import { atom } from 'jotai'
|
||||
|
||||
import {
|
||||
conversationStatesAtom,
|
||||
currentConversationAtom,
|
||||
getActiveConvoIdAtom,
|
||||
updateThreadStateLastMessageAtom,
|
||||
} from './Conversation.atom'
|
||||
@ -102,6 +103,17 @@ export const cleanConversationMessages = atom(null, (get, set, id: string) => {
|
||||
set(chatMessages, newData)
|
||||
})
|
||||
|
||||
export const deleteMessage = atom(null, (get, set, id: string) => {
|
||||
const newData: Record<string, ThreadMessage[]> = {
|
||||
...get(chatMessages),
|
||||
}
|
||||
const threadId = get(currentConversationAtom)?.id
|
||||
if (threadId) {
|
||||
newData[threadId] = newData[threadId].filter((e) => e.id !== id)
|
||||
set(chatMessages, newData)
|
||||
}
|
||||
})
|
||||
|
||||
export const updateMessageAtom = atom(
|
||||
null,
|
||||
(
|
||||
|
||||
85
web/screens/Chat/MessageToolbar/index.tsx
Normal file
85
web/screens/Chat/MessageToolbar/index.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import {
|
||||
ChatCompletionRole,
|
||||
EventName,
|
||||
MessageStatus,
|
||||
PluginType,
|
||||
ThreadMessage,
|
||||
events,
|
||||
} from '@janhq/core'
|
||||
import { ConversationalPlugin, InferencePlugin } from '@janhq/core/lib/plugins'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { RefreshCcw, ClipboardCopy, Trash2Icon, StopCircle } from 'lucide-react'
|
||||
|
||||
import { toaster } from '@/containers/Toast'
|
||||
|
||||
import {
|
||||
deleteMessage,
|
||||
getCurrentChatMessagesAtom,
|
||||
} from '@/helpers/atoms/ChatMessage.atom'
|
||||
import { currentConversationAtom } from '@/helpers/atoms/Conversation.atom'
|
||||
import { pluginManager } from '@/plugin'
|
||||
|
||||
const MessageToolbar = ({ message }: { message: ThreadMessage }) => {
|
||||
const deleteAMessage = useSetAtom(deleteMessage)
|
||||
const thread = useAtomValue(currentConversationAtom)
|
||||
const messages = useAtomValue(getCurrentChatMessagesAtom)
|
||||
const stopInference = async () => {
|
||||
await pluginManager
|
||||
.get<InferencePlugin>(PluginType.Inference)
|
||||
?.stopInference()
|
||||
setTimeout(() => {
|
||||
events.emit(EventName.OnMessageResponseFinished, message)
|
||||
}, 300)
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-row items-center">
|
||||
{message.status === MessageStatus.Pending && (
|
||||
<StopCircle
|
||||
className="mx-1 cursor-pointer rounded-sm bg-gray-800 px-[3px]"
|
||||
size={20}
|
||||
onClick={() => stopInference()}
|
||||
/>
|
||||
)}
|
||||
{message.status !== MessageStatus.Pending &&
|
||||
message.id === messages[0]?.id && (
|
||||
<RefreshCcw
|
||||
className="mx-1 cursor-pointer rounded-sm bg-gray-800 px-[3px]"
|
||||
size={20}
|
||||
onClick={() => {
|
||||
const messageRequest = messages[1]
|
||||
if (message.role === ChatCompletionRole.Assistant) {
|
||||
deleteAMessage(message.id ?? '')
|
||||
}
|
||||
events.emit(EventName.OnNewMessageRequest, messageRequest)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<ClipboardCopy
|
||||
className="mx-1 cursor-pointer rounded-sm bg-gray-800 px-[3px]"
|
||||
size={20}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(message.content ?? '')
|
||||
toaster({
|
||||
title: 'Copied to clipboard',
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<Trash2Icon
|
||||
className="mx-1 cursor-pointer rounded-sm bg-gray-800 px-[3px]"
|
||||
size={20}
|
||||
onClick={async () => {
|
||||
deleteAMessage(message.id ?? '')
|
||||
if (thread)
|
||||
await pluginManager
|
||||
.get<ConversationalPlugin>(PluginType.Conversational)
|
||||
?.saveConversation({
|
||||
...thread,
|
||||
messages: messages.filter((e) => e.id !== message.id),
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MessageToolbar
|
||||
@ -1,9 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import React from 'react'
|
||||
import React, { useContext } from 'react'
|
||||
|
||||
import { ChatCompletionRole, MessageStatus, ThreadMessage } from '@janhq/core'
|
||||
|
||||
import { ChatCompletionRole, ThreadMessage } from '@janhq/core'
|
||||
import hljs from 'highlight.js'
|
||||
|
||||
import { MoreVertical } from 'lucide-react'
|
||||
import { Marked } from 'marked'
|
||||
|
||||
import { markedHighlight } from 'marked-highlight'
|
||||
@ -14,8 +16,12 @@ import LogoMark from '@/containers/Brand/Logo/Mark'
|
||||
|
||||
import BubbleLoader from '@/containers/Loader/Bubble'
|
||||
|
||||
import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
|
||||
import { displayDate } from '@/utils/datetime'
|
||||
|
||||
import MessageToolbar from '../MessageToolbar'
|
||||
|
||||
const marked = new Marked(
|
||||
markedHighlight({
|
||||
langPrefix: 'hljs',
|
||||
@ -42,12 +48,13 @@ const marked = new Marked(
|
||||
)
|
||||
|
||||
const SimpleTextMessage: React.FC<ThreadMessage> = (props) => {
|
||||
const { experimentalFeatureEnabed } = useContext(FeatureToggleContext)
|
||||
const parsedText = marked.parse(props.content ?? '')
|
||||
const isUser = props.role === ChatCompletionRole.User
|
||||
const isSystem = props.role === ChatCompletionRole.System
|
||||
|
||||
return (
|
||||
<div className="mx-auto rounded-xl px-4 lg:w-3/4">
|
||||
<div className="group mx-auto rounded-xl px-4 lg:w-3/4">
|
||||
<div
|
||||
className={twMerge(
|
||||
'mb-1 flex items-center justify-start gap-2',
|
||||
@ -57,10 +64,17 @@ const SimpleTextMessage: React.FC<ThreadMessage> = (props) => {
|
||||
{!isUser && !isSystem && <LogoMark width={20} />}
|
||||
<div className="text-sm font-extrabold capitalize">{props.role}</div>
|
||||
<p className="text-xs font-medium">{displayDate(props.createdAt)}</p>
|
||||
|
||||
{experimentalFeatureEnabed && (
|
||||
<div className="hidden cursor-pointer group-hover:flex">
|
||||
<MessageToolbar message={props} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={twMerge('w-full')}>
|
||||
{!props.content || props.content === '' ? (
|
||||
{props.status === MessageStatus.Pending &&
|
||||
(!props.content || props.content === '') ? (
|
||||
<BubbleLoader />
|
||||
) : (
|
||||
<>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user