fix: fix fold selected (#4058)
* fix: 修复foldSelected问题 * refactor: 优化布局定位
This commit is contained in:
parent
f1a03916e7
commit
21f1b8b373
@ -56,6 +56,8 @@ const Container = styled.div`
|
|||||||
|
|
||||||
const Main = styled(Flex)`
|
const Main = styled(Flex)`
|
||||||
height: calc(100vh - var(--navbar-height));
|
height: calc(100vh - var(--navbar-height));
|
||||||
|
// 设置为containing block,方便子元素fixed定位
|
||||||
|
transform: translateZ(0);
|
||||||
`
|
`
|
||||||
|
|
||||||
export default Chat
|
export default Chat
|
||||||
|
|||||||
@ -24,7 +24,6 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
|
|||||||
const lastMoveTime = useRef(0)
|
const lastMoveTime = useRef(0)
|
||||||
const { topicPosition, showTopics } = useSettings()
|
const { topicPosition, showTopics } = useSettings()
|
||||||
const showRightTopics = topicPosition === 'right' && showTopics
|
const showRightTopics = topicPosition === 'right' && showTopics
|
||||||
const right = showRightTopics ? 'calc(var(--topic-list-width) + 16px)' : '16px'
|
|
||||||
|
|
||||||
// Reset hide timer and make buttons visible
|
// Reset hide timer and make buttons visible
|
||||||
const resetHideTimer = useCallback(() => {
|
const resetHideTimer = useCallback(() => {
|
||||||
@ -263,24 +262,11 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
|
|||||||
clearTimeout(hideTimer)
|
clearTimeout(hideTimer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [
|
}, [containerId, hideTimer, resetHideTimer, isNearButtons, handleMouseEnter, handleMouseLeave, showRightTopics])
|
||||||
containerId,
|
|
||||||
hideTimer,
|
|
||||||
resetHideTimer,
|
|
||||||
isNearButtons,
|
|
||||||
handleMouseEnter,
|
|
||||||
handleMouseLeave,
|
|
||||||
right,
|
|
||||||
showRightTopics
|
|
||||||
])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NavigationContainer
|
<NavigationContainer $isVisible={isVisible} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
|
||||||
$isVisible={isVisible}
|
|
||||||
$right={right}
|
|
||||||
onMouseEnter={handleMouseEnter}
|
|
||||||
onMouseLeave={handleMouseLeave}>
|
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Tooltip title={t('chat.navigation.prev')} placement="left">
|
<Tooltip title={t('chat.navigation.prev')} placement="left">
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
@ -332,12 +318,11 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
|
|||||||
|
|
||||||
interface NavigationContainerProps {
|
interface NavigationContainerProps {
|
||||||
$isVisible: boolean
|
$isVisible: boolean
|
||||||
$right: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const NavigationContainer = styled.div<NavigationContainerProps>`
|
const NavigationContainer = styled.div<NavigationContainerProps>`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: ${(props) => props.$right};
|
right: 16px;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%) translateX(${(props) => (props.$isVisible ? 0 : '100%')});
|
transform: translateY(-50%) translateX(${(props) => (props.$isVisible ? 0 : '100%')});
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
|
|||||||
@ -33,9 +33,6 @@ const MessageAnchorLine: FC<MessageLineProps> = ({ messages }) => {
|
|||||||
const messageItemsRef = useRef<Map<string, HTMLDivElement>>(new Map())
|
const messageItemsRef = useRef<Map<string, HTMLDivElement>>(new Map())
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
const [mouseY, setMouseY] = useState<number | null>(null)
|
const [mouseY, setMouseY] = useState<number | null>(null)
|
||||||
const { topicPosition, showTopics } = useSettings()
|
|
||||||
const showRightTopics = topicPosition === 'right' && showTopics
|
|
||||||
const right = showRightTopics ? 'calc(var(--topic-list-width) + 15px)' : '15px'
|
|
||||||
|
|
||||||
const [listOffsetY, setListOffsetY] = useState(0)
|
const [listOffsetY, setListOffsetY] = useState(0)
|
||||||
const [containerHeight, setContainerHeight] = useState<number | null>(null)
|
const [containerHeight, setContainerHeight] = useState<number | null>(null)
|
||||||
@ -160,7 +157,6 @@ const MessageAnchorLine: FC<MessageLineProps> = ({ messages }) => {
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
onMouseMove={handleMouseMove}
|
onMouseMove={handleMouseMove}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
$right={right}
|
|
||||||
$height={containerHeight}>
|
$height={containerHeight}>
|
||||||
<MessagesList ref={messagesListRef} style={{ transform: `translateY(${listOffsetY}px)` }}>
|
<MessagesList ref={messagesListRef} style={{ transform: `translateY(${listOffsetY}px)` }}>
|
||||||
{messages.map((message, index) => {
|
{messages.map((message, index) => {
|
||||||
@ -226,11 +222,11 @@ const MessageItemContainer = styled.div`
|
|||||||
transform-origin: right center;
|
transform-origin: right center;
|
||||||
`
|
`
|
||||||
|
|
||||||
const MessageLineContainer = styled.div<{ $right: string; $height: number | null }>`
|
const MessageLineContainer = styled.div<{ $height: number | null }>`
|
||||||
width: 14px;
|
width: 14px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: ${(props) => (props.$height ? `calc(${props.$height / 2}px + var(--status-bar-height))` : '50%')};
|
top: ${(props) => (props.$height ? `calc(${props.$height / 2}px + var(--status-bar-height))` : '50%')};
|
||||||
right: ${(props) => props.$right};
|
right: 13px;
|
||||||
max-height: ${(props) => (props.$height ? `${props.$height}px` : 'calc(100% - var(--status-bar-height) * 2)')};
|
max-height: ${(props) => (props.$height ? `${props.$height}px` : 'calc(100% - var(--status-bar-height) * 2)')};
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { MultiModelMessageStyle } from '@renderer/store/settings'
|
|||||||
import type { Message, Topic } from '@renderer/types'
|
import type { Message, Topic } from '@renderer/types'
|
||||||
import { classNames } from '@renderer/utils'
|
import { classNames } from '@renderer/utils'
|
||||||
import { Popover } from 'antd'
|
import { Popover } from 'antd'
|
||||||
import { memo, useCallback, useEffect, useState } from 'react'
|
import { memo, useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import styled, { css } from 'styled-components'
|
import styled, { css } from 'styled-components'
|
||||||
|
|
||||||
import MessageGroupMenuBar from './MessageGroupMenuBar'
|
import MessageGroupMenuBar from './MessageGroupMenuBar'
|
||||||
@ -27,14 +27,53 @@ const MessageGroup = ({ messages, topic, hidePresetMessages }: Props) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const messageLength = messages.length
|
const messageLength = messages.length
|
||||||
|
const prevMessageLengthRef = useRef(messageLength)
|
||||||
const [selectedIndex, setSelectedIndex] = useState(messageLength - 1)
|
const [selectedIndex, setSelectedIndex] = useState(messageLength - 1)
|
||||||
|
|
||||||
|
const getSelectedMessageId = useCallback(() => {
|
||||||
|
const selectedMessage = messages.find((message) => message.foldSelected)
|
||||||
|
if (selectedMessage) {
|
||||||
|
return selectedMessage.id
|
||||||
|
}
|
||||||
|
return messages[0]?.id
|
||||||
|
}, [messages])
|
||||||
|
|
||||||
|
const setSelectedMessage = useCallback(
|
||||||
|
(message: Message) => {
|
||||||
|
messages.forEach(async (m) => {
|
||||||
|
await editMessage(m.id, { foldSelected: m.id === message.id })
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const messageElement = document.getElementById(`message-${message.id}`)
|
||||||
|
if (messageElement) {
|
||||||
|
messageElement.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
},
|
||||||
|
[editMessage, messages]
|
||||||
|
)
|
||||||
|
|
||||||
const isGrouped = messageLength > 1 && messages.every((m) => m.role === 'assistant')
|
const isGrouped = messageLength > 1 && messages.every((m) => m.role === 'assistant')
|
||||||
const isHorizontal = multiModelMessageStyle === 'horizontal'
|
const isHorizontal = multiModelMessageStyle === 'horizontal'
|
||||||
const isGrid = multiModelMessageStyle === 'grid'
|
const isGrid = multiModelMessageStyle === 'grid'
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedIndex(messageLength - 1)
|
if (messageLength > prevMessageLengthRef.current) {
|
||||||
|
setSelectedIndex(messageLength - 1)
|
||||||
|
const lastMessage = messages[messageLength - 1]
|
||||||
|
if (lastMessage) {
|
||||||
|
setSelectedMessage(lastMessage)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const selectedId = getSelectedMessageId()
|
||||||
|
const newIndex = messages.findIndex((msg) => msg.id === selectedId)
|
||||||
|
if (newIndex !== -1) {
|
||||||
|
setSelectedIndex(newIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prevMessageLengthRef.current = messageLength
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [messageLength])
|
}, [messageLength])
|
||||||
|
|
||||||
// 添加对流程图节点点击事件的监听
|
// 添加对流程图节点点击事件的监听
|
||||||
@ -70,22 +109,6 @@ const MessageGroup = ({ messages, topic, hidePresetMessages }: Props) => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [messages, selectedIndex, isGrouped, messageLength])
|
}, [messages, selectedIndex, isGrouped, messageLength])
|
||||||
|
|
||||||
const setSelectedMessage = useCallback(
|
|
||||||
(message: Message) => {
|
|
||||||
messages.forEach(async (m) => {
|
|
||||||
await editMessage(m.id, { foldSelected: m.id === message.id })
|
|
||||||
})
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
const messageElement = document.getElementById(`message-${message.id}`)
|
|
||||||
if (messageElement) {
|
|
||||||
messageElement.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
||||||
}
|
|
||||||
}, 200)
|
|
||||||
},
|
|
||||||
[editMessage, messages]
|
|
||||||
)
|
|
||||||
|
|
||||||
// 添加对LOCATE_MESSAGE事件的监听
|
// 添加对LOCATE_MESSAGE事件的监听
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 为每个消息注册一个定位事件监听器
|
// 为每个消息注册一个定位事件监听器
|
||||||
@ -146,7 +169,7 @@ const MessageGroup = ({ messages, topic, hidePresetMessages }: Props) => {
|
|||||||
className={classNames({
|
className={classNames({
|
||||||
'group-message-wrapper': message.role === 'assistant' && isHorizontal && isGrouped,
|
'group-message-wrapper': message.role === 'assistant' && isHorizontal && isGrouped,
|
||||||
[multiModelMessageStyle]: isGrouped,
|
[multiModelMessageStyle]: isGrouped,
|
||||||
selected: 'foldSelected' in message ? message.foldSelected : index === 0
|
selected: message.id === getSelectedMessageId()
|
||||||
})}>
|
})}>
|
||||||
<MessageStream {...messageProps} />
|
<MessageStream {...messageProps} />
|
||||||
</MessageWrapper>
|
</MessageWrapper>
|
||||||
@ -183,7 +206,8 @@ const MessageGroup = ({ messages, topic, hidePresetMessages }: Props) => {
|
|||||||
selectedIndex,
|
selectedIndex,
|
||||||
topic,
|
topic,
|
||||||
hidePresetMessages,
|
hidePresetMessages,
|
||||||
gridPopoverTrigger
|
gridPopoverTrigger,
|
||||||
|
getSelectedMessageId
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -210,6 +234,7 @@ const MessageGroup = ({ messages, topic, hidePresetMessages }: Props) => {
|
|||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
messages={messages}
|
messages={messages}
|
||||||
|
selectMessageId={getSelectedMessageId()}
|
||||||
setSelectedMessage={setSelectedMessage}
|
setSelectedMessage={setSelectedMessage}
|
||||||
topic={topic}
|
topic={topic}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -21,6 +21,7 @@ interface Props {
|
|||||||
multiModelMessageStyle: MultiModelMessageStyle
|
multiModelMessageStyle: MultiModelMessageStyle
|
||||||
setMultiModelMessageStyle: (style: MultiModelMessageStyle) => void
|
setMultiModelMessageStyle: (style: MultiModelMessageStyle) => void
|
||||||
messages: Message[]
|
messages: Message[]
|
||||||
|
selectMessageId: string
|
||||||
setSelectedMessage: (message: Message) => void
|
setSelectedMessage: (message: Message) => void
|
||||||
topic: Topic
|
topic: Topic
|
||||||
}
|
}
|
||||||
@ -29,6 +30,7 @@ const MessageGroupMenuBar: FC<Props> = ({
|
|||||||
multiModelMessageStyle,
|
multiModelMessageStyle,
|
||||||
setMultiModelMessageStyle,
|
setMultiModelMessageStyle,
|
||||||
messages,
|
messages,
|
||||||
|
selectMessageId,
|
||||||
setSelectedMessage,
|
setSelectedMessage,
|
||||||
topic
|
topic
|
||||||
}) => {
|
}) => {
|
||||||
@ -75,7 +77,11 @@ const MessageGroupMenuBar: FC<Props> = ({
|
|||||||
))}
|
))}
|
||||||
</LayoutContainer>
|
</LayoutContainer>
|
||||||
{multiModelMessageStyle === 'fold' && (
|
{multiModelMessageStyle === 'fold' && (
|
||||||
<MessageGroupModelList messages={messages} setSelectedMessage={setSelectedMessage} />
|
<MessageGroupModelList
|
||||||
|
messages={messages}
|
||||||
|
selectMessageId={selectMessageId}
|
||||||
|
setSelectedMessage={setSelectedMessage}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{multiModelMessageStyle === 'grid' && <MessageGroupSettings />}
|
{multiModelMessageStyle === 'grid' && <MessageGroupSettings />}
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
@ -12,12 +12,13 @@ import styled from 'styled-components'
|
|||||||
|
|
||||||
interface MessageGroupModelListProps {
|
interface MessageGroupModelListProps {
|
||||||
messages: Message[]
|
messages: Message[]
|
||||||
|
selectMessageId: string
|
||||||
setSelectedMessage: (message: Message) => void
|
setSelectedMessage: (message: Message) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
type DisplayMode = 'compact' | 'expanded'
|
type DisplayMode = 'compact' | 'expanded'
|
||||||
|
|
||||||
const MessageGroupModelList: FC<MessageGroupModelListProps> = ({ messages, setSelectedMessage }) => {
|
const MessageGroupModelList: FC<MessageGroupModelListProps> = ({ messages, selectMessageId, setSelectedMessage }) => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { foldDisplayMode } = useSettings()
|
const { foldDisplayMode } = useSettings()
|
||||||
@ -47,7 +48,7 @@ const MessageGroupModelList: FC<MessageGroupModelListProps> = ({ messages, setSe
|
|||||||
<Tooltip key={index} title={message.model?.name} placement="top" mouseEnterDelay={0.2}>
|
<Tooltip key={index} title={message.model?.name} placement="top" mouseEnterDelay={0.2}>
|
||||||
<AvatarWrapper
|
<AvatarWrapper
|
||||||
className="avatar-wrapper"
|
className="avatar-wrapper"
|
||||||
isSelected={'foldSelected' in message ? message.foldSelected! : index === 0}
|
isSelected={message.id === selectMessageId}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedMessage(message)
|
setSelectedMessage(message)
|
||||||
}}>
|
}}>
|
||||||
@ -59,7 +60,7 @@ const MessageGroupModelList: FC<MessageGroupModelListProps> = ({ messages, setSe
|
|||||||
) : (
|
) : (
|
||||||
/* Expanded style display */
|
/* Expanded style display */
|
||||||
<Segmented
|
<Segmented
|
||||||
value={messages.find((message) => message.foldSelected)?.id || messages[0].id}
|
value={selectMessageId}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
const message = messages.find((message) => message.id === value) as Message
|
const message = messages.find((message) => message.id === value) as Message
|
||||||
setSelectedMessage(message)
|
setSelectedMessage(message)
|
||||||
|
|||||||
@ -278,7 +278,6 @@ export const sendMessage =
|
|||||||
const assistantMessage = getAssistantMessage({ assistant, topic })
|
const assistantMessage = getAssistantMessage({ assistant, topic })
|
||||||
assistantMessage.askId = userMessage.id
|
assistantMessage.askId = userMessage.id
|
||||||
assistantMessage.status = 'sending'
|
assistantMessage.status = 'sending'
|
||||||
assistantMessage.foldSelected = true
|
|
||||||
assistantMessages.push(assistantMessage)
|
assistantMessages.push(assistantMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user