* fix: update new api from cortex to support 0.5.0 Signed-off-by: James <namnh0122@gmail.com> * fix stop button for streaming Signed-off-by: James <namnh0122@gmail.com> * fix stop inference for nonstreaming Signed-off-by: James <namnh0122@gmail.com> * chore: remove umami prevent tracking call to vercel Signed-off-by: James <namnh0122@gmail.com> * add warning modal when running more than 2 model concurrently Signed-off-by: James <namnh0122@gmail.com> * fix: skip summarize if abort Signed-off-by: James <namnh0122@gmail.com> * 0.5.0-3 * add inference error popup Signed-off-by: James <namnh0122@gmail.com> * add back import local model Signed-off-by: James <namnh0122@gmail.com> * fix: max token issue (#3225) Signed-off-by: James <namnh0122@gmail.com> * format status Signed-off-by: James <namnh0122@gmail.com> * fix migration missing instructions Signed-off-by: James <namnh0122@gmail.com> * fix: wait for cortex process overlay should be on top (#3224) * fix: wait for cortex process overlay should be on top * chore: update cortex.js * Cortex 0.5.0-5 * add import model to my model screen Signed-off-by: James <namnh0122@gmail.com> * fix: should migrate symlink models (#3226) * fix import on windows (#3229) Signed-off-by: James <namnh0122@gmail.com> * fix yarn lint Signed-off-by: James <namnh0122@gmail.com> * fix: clean up port before start jan (#3232) Signed-off-by: James <namnh0122@gmail.com> --------- Signed-off-by: James <namnh0122@gmail.com> Co-authored-by: Van Pham <64197333+Van-QA@users.noreply.github.com> Co-authored-by: Louis <louis@jan.ai>
147 lines
3.9 KiB
TypeScript
147 lines
3.9 KiB
TypeScript
import { useCallback, useMemo } from 'react'
|
|
|
|
import { Message, TextContentBlock } from '@janhq/core'
|
|
import { Tooltip } from '@janhq/joi'
|
|
import { useSetAtom } from 'jotai'
|
|
import {
|
|
CopyIcon,
|
|
Trash2Icon,
|
|
CheckIcon,
|
|
PencilIcon,
|
|
RefreshCcw,
|
|
} from 'lucide-react'
|
|
|
|
import { useClipboard } from '@/hooks/useClipboard'
|
|
|
|
import useMessageDeleteMutation from '@/hooks/useMessageDeleteMutation'
|
|
|
|
import {
|
|
deleteMessageAtom,
|
|
editMessageAtom,
|
|
} from '@/helpers/atoms/ChatMessage.atom'
|
|
|
|
type Props = {
|
|
isLastMessage: boolean
|
|
message: Message
|
|
onResendMessage: () => void
|
|
}
|
|
|
|
const MessageToolbar: React.FC<Props> = ({
|
|
isLastMessage,
|
|
message,
|
|
onResendMessage,
|
|
}) => {
|
|
const deleteMessage = useSetAtom(deleteMessageAtom)
|
|
const setEditMessage = useSetAtom(editMessageAtom)
|
|
const clipboard = useClipboard({ timeout: 1000 })
|
|
const deleteCortexMessage = useMessageDeleteMutation()
|
|
|
|
const onDeleteClick = useCallback(
|
|
async (threadId: string, messageId: string) => {
|
|
await deleteCortexMessage.mutateAsync({
|
|
threadId,
|
|
messageId,
|
|
})
|
|
deleteMessage(messageId)
|
|
},
|
|
[deleteMessage, deleteCortexMessage]
|
|
)
|
|
|
|
const onCopyClick = useCallback(() => {
|
|
const messageContent = message.content[0]
|
|
if (!messageContent) return
|
|
if (messageContent.type === 'text') {
|
|
const textContentBlock = messageContent as TextContentBlock
|
|
clipboard.copy(textContentBlock.text.value)
|
|
}
|
|
}, [clipboard, message])
|
|
|
|
const onRegenerateClick = useCallback(async () => {
|
|
// current message must be from assistant
|
|
if (message.role !== 'assistant') return
|
|
await deleteCortexMessage.mutateAsync({
|
|
threadId: message.thread_id,
|
|
messageId: message.id,
|
|
})
|
|
deleteMessage(message.id)
|
|
onResendMessage()
|
|
}, [deleteCortexMessage, deleteMessage, onResendMessage, message])
|
|
|
|
const allowRegenerate = useMemo(
|
|
() => isLastMessage && message.role === 'assistant',
|
|
[isLastMessage, message]
|
|
)
|
|
|
|
const allowEditMessage = useMemo(
|
|
() => message.role === 'user' && message.content[0]?.type === 'text',
|
|
[message]
|
|
)
|
|
|
|
if (message.status === 'in_progress') return null
|
|
|
|
return (
|
|
<div className="flex flex-row items-center">
|
|
<div className="flex gap-1 bg-[hsla(var(--app-bg))]">
|
|
{allowEditMessage && (
|
|
<div
|
|
className="cursor-pointer rounded-lg border border-[hsla(var(--app-border))] p-2"
|
|
onClick={() => setEditMessage(message.id)}
|
|
>
|
|
<PencilIcon
|
|
size={14}
|
|
className="text-[hsla(var(--text-secondary))]"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{allowRegenerate && (
|
|
<div
|
|
className="cursor-pointer rounded-lg border border-[hsla(var(--app-border))] p-2"
|
|
onClick={onRegenerateClick}
|
|
>
|
|
<RefreshCcw
|
|
size={14}
|
|
className="text-[hsla(var(--text-secondary))]"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<div
|
|
className="cursor-pointer rounded-lg border border-[hsla(var(--app-border))] p-2"
|
|
onClick={onCopyClick}
|
|
>
|
|
{clipboard.copied ? (
|
|
<CheckIcon size={14} className="text-[hsla(var(--success-bg))]" />
|
|
) : (
|
|
<Tooltip
|
|
trigger={
|
|
<CopyIcon
|
|
size={14}
|
|
className="text-[hsla(var(--text-secondary))]"
|
|
/>
|
|
}
|
|
content="Copy"
|
|
/>
|
|
)}
|
|
</div>
|
|
<div
|
|
className="cursor-pointer rounded-lg border border-[hsla(var(--app-border))] p-2"
|
|
onClick={() => onDeleteClick(message.thread_id, message.id)}
|
|
>
|
|
<Tooltip
|
|
trigger={
|
|
<Trash2Icon
|
|
size={14}
|
|
className="text-[hsla(var(--text-secondary))]"
|
|
/>
|
|
}
|
|
content="Delete"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default MessageToolbar
|