diff --git a/web/constants/Threads.ts b/web/constants/Threads.ts new file mode 100644 index 0000000000..a5cb995fa2 --- /dev/null +++ b/web/constants/Threads.ts @@ -0,0 +1 @@ +export const defaultThreadTitle = 'New Thread' diff --git a/web/hooks/useCortex.ts b/web/hooks/useCortex.ts index 72992dd138..a98026825e 100644 --- a/web/hooks/useCortex.ts +++ b/web/hooks/useCortex.ts @@ -17,6 +17,8 @@ import { import { useAtomValue } from 'jotai' +import { defaultThreadTitle } from '@/constants/Threads' + import { UpdateConfigMutationVariables } from './useEngineMutation' import { MessageCreateMutationVariables } from './useMessageCreateMutation' import { MessageDeleteMutationVariables } from './useMessageDeleteMutation' @@ -80,7 +82,7 @@ const useCortex = () => { if (!assistants || assistants.length === 0) continue // @ts-expect-error each thread must have a title, else default to 'New Thread' - const title: string = thread['title'] ?? 'New Thread' + const title: string = thread['title'] ?? defaultThreadTitle threads.push({ ...thread, @@ -227,7 +229,7 @@ const useCortex = () => { const thread: Thread = { ...createThreadResponse, // @ts-expect-error each thread will have a title, else default to 'New Thread' - title: createThreadResponse.title ?? 'New Thread', + title: createThreadResponse.title ?? defaultThreadTitle, assistants: [assistant], } return thread diff --git a/web/hooks/useMigratingData.ts b/web/hooks/useMigratingData.ts index 76e5d9e036..f45688f255 100644 --- a/web/hooks/useMigratingData.ts +++ b/web/hooks/useMigratingData.ts @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { useCallback } from 'react' +import { defaultThreadTitle } from '@/constants/Threads' + import useAssistantQuery from './useAssistantQuery' import useCortex from './useCortex' @@ -43,7 +45,7 @@ const useMigratingData = () => { console.error(`Ignore thread ${thread.id} because modelId is not found`) continue } - const threadTitle: string = thread.title ?? 'New Thread' + const threadTitle: string = thread.title ?? defaultThreadTitle const instructions: string = thread.assistants[0]?.instructions ?? '' // currently, we don't have api support for creating thread with messages const cortexThread = await createThread(modelId, assistants[0]) diff --git a/web/hooks/useSendMessage.ts b/web/hooks/useSendMessage.ts index a06c22c7dd..bd81d367bd 100644 --- a/web/hooks/useSendMessage.ts +++ b/web/hooks/useSendMessage.ts @@ -18,6 +18,8 @@ import { currentPromptAtom, editPromptAtom } from '@/containers/Providers/Jotai' import { toaster } from '@/containers/Toast' +import { defaultThreadTitle } from '@/constants/Threads' + import { inferenceErrorAtom } from '@/screens/HubScreen2/components/InferenceErrorModal' import { showWarningMultipleModelModalAtom } from '@/screens/HubScreen2/components/WarningMultipleModelModal' @@ -512,7 +514,7 @@ const useSendMessage = () => { if (!isValid) return let shouldSummarize = - activeThread!.title === 'New Thread' || + activeThread!.title === defaultThreadTitle || activeThread!.title.trim() === '' const modelId = activeThread!.assistants[0].model diff --git a/web/screens/Thread/ThreadLeftPanel/ModalCleanThread/index.tsx b/web/screens/Thread/ThreadLeftPanel/ModalCleanThread/index.tsx index 7c7c30cc3e..f55836cce3 100644 --- a/web/screens/Thread/ThreadLeftPanel/ModalCleanThread/index.tsx +++ b/web/screens/Thread/ThreadLeftPanel/ModalCleanThread/index.tsx @@ -1,23 +1,35 @@ import { useCallback, memo } from 'react' +import { Thread } from '@janhq/core' import { Button, Modal, ModalClose } from '@janhq/joi' +import { useSetAtom } from 'jotai' import { Paintbrush } from 'lucide-react' +import { defaultThreadTitle } from '@/constants/Threads' + +import useCortex from '@/hooks/useCortex' import useThreads from '@/hooks/useThreads' +import { updateThreadTitleAtom } from '@/helpers/atoms/Thread.atom' + type Props = { - threadId: string + thread: Thread closeContextMenu?: () => void } -const ModalCleanThread = ({ threadId, closeContextMenu }: Props) => { +const ModalCleanThread = ({ thread, closeContextMenu }: Props) => { const { cleanThread } = useThreads() + const updateThreadTitle = useSetAtom(updateThreadTitleAtom) + const { updateThread } = useCortex() + const onCleanThreadClick = useCallback( (e: React.MouseEvent) => { e.stopPropagation() - cleanThread(threadId) + cleanThread(thread.id) + updateThreadTitle(thread.id, defaultThreadTitle) + updateThread({ ...thread, title: defaultThreadTitle }) }, - [cleanThread, threadId] + [cleanThread, thread, updateThread, updateThreadTitle] ) return ( diff --git a/web/screens/Thread/ThreadLeftPanel/ThreadItem/index.tsx b/web/screens/Thread/ThreadLeftPanel/ThreadItem/index.tsx index ceb9442697..42913f1b00 100644 --- a/web/screens/Thread/ThreadLeftPanel/ThreadItem/index.tsx +++ b/web/screens/Thread/ThreadLeftPanel/ThreadItem/index.tsx @@ -6,6 +6,8 @@ import { useAtomValue, useSetAtom } from 'jotai' import { MoreHorizontalIcon } from 'lucide-react' import { twMerge } from 'tailwind-merge' +import { defaultThreadTitle } from '@/constants/Threads' + import useThreads from '@/hooks/useThreads' import ModalCleanThread from '../ModalCleanThread' @@ -29,6 +31,7 @@ const ThreadItem: React.FC = ({ thread }) => { const getThreadIdsShouldAnimateTitle = useAtomValue( getThreadIdsShouldAnimateTitleAtom ) + const setEditMessage = useSetAtom(editMessageAtom) const { setActiveThread } = useThreads() const [contextMenu, setContextMenu] = useState<{ @@ -81,10 +84,13 @@ const ThreadItem: React.FC = ({ thread }) => { >
{getThreadIdsShouldAnimateTitle.includes(thread.id) ? ( - + ) : ( - {thread.title} + {thread.title || defaultThreadTitle} )}
@@ -112,7 +118,7 @@ const ThreadItem: React.FC = ({ thread }) => { closeContextMenu={closeContextMenu} /> = ({ text, speed }) => { const [index, setIndex] = useState(0) useEffect(() => { - if (index < text.length) { - const timeout = setTimeout(() => { - setDisplayedText(displayedText + text[index]) + const typingEffect = setInterval(() => { + if (text.length) { + setDisplayedText(text.substring(0, index + 1)) setIndex(index + 1) - }, speed) + } else { + clearInterval(typingEffect) + } + }, speed) - return () => clearTimeout(timeout) + return () => { + clearInterval(typingEffect) } - }, [index, text, displayedText, speed]) + }, [index, speed, text]) return (