Use the `MessageStatus` enum to determine when the “Generate AI Response” button should appear. Previously the visibility logic checked the last message’s role or the presence of tool calls, which could be unreliable since we moved to combined tool call/reasoning block. By checking that the last message exists and its status is not `Ready` which is that the message is finished generating when an eot token is found, the button is shown only while the AI has stopped generating before it could end properly, improving UX and aligning the UI state with the underlying message state. The change also imports `MessageStatus` and cleans up formatting for readability.
67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
import { useThreadScrolling } from '@/hooks/useThreadScrolling'
|
|
import { memo } from 'react'
|
|
import { GenerateResponseButton } from './GenerateResponseButton'
|
|
import { useMessages } from '@/hooks/useMessages'
|
|
import { useShallow } from 'zustand/react/shallow'
|
|
import { useAppearance } from '@/hooks/useAppearance'
|
|
import { cn } from '@/lib/utils'
|
|
import { ArrowDown } from 'lucide-react'
|
|
import { useTranslation } from '@/i18n/react-i18next-compat'
|
|
import { useAppState } from '@/hooks/useAppState'
|
|
import { MessageStatus } from '@janhq/core'
|
|
|
|
const ScrollToBottom = ({
|
|
threadId,
|
|
scrollContainerRef,
|
|
}: {
|
|
threadId: string
|
|
scrollContainerRef: React.RefObject<HTMLDivElement | null>
|
|
}) => {
|
|
const { t } = useTranslation()
|
|
const appMainViewBgColor = useAppearance((state) => state.appMainViewBgColor)
|
|
const { showScrollToBottomBtn, scrollToBottom } = useThreadScrolling(
|
|
threadId,
|
|
scrollContainerRef
|
|
)
|
|
const { messages } = useMessages(
|
|
useShallow((state) => ({
|
|
messages: state.messages[threadId],
|
|
}))
|
|
)
|
|
|
|
const streamingContent = useAppState((state) => state.streamingContent)
|
|
|
|
const lastMsg = messages[messages.length - 1]
|
|
const showGenerateAIResponseBtn =
|
|
!!lastMsg && lastMsg.status !== MessageStatus.Ready && !streamingContent
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'absolute z-0 -top-6 h-8 py-1 flex w-full justify-center pointer-events-none opacity-0 visibility-hidden',
|
|
appMainViewBgColor.a === 1
|
|
? 'from-main-view/20 bg-gradient-to-b to-main-view backdrop-blur'
|
|
: 'bg-transparent',
|
|
(showScrollToBottomBtn || showGenerateAIResponseBtn) &&
|
|
'visibility-visible opacity-100'
|
|
)}
|
|
>
|
|
{showScrollToBottomBtn && (
|
|
<div
|
|
className="bg-main-view-fg/10 px-2 border border-main-view-fg/5 flex items-center justify-center rounded-xl gap-x-2 cursor-pointer pointer-events-auto"
|
|
onClick={() => {
|
|
scrollToBottom(true)
|
|
}}
|
|
>
|
|
<p className="text-xs">{t('scrollToBottom')}</p>
|
|
<ArrowDown size={12} />
|
|
</div>
|
|
)}
|
|
{showGenerateAIResponseBtn && (
|
|
<GenerateResponseButton threadId={threadId} />
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default memo(ScrollToBottom)
|