feat(chat): add session messages view and active state tracking

Implement agent session messages display component and track active topic/session state
Add AgentSessionMessages component and integrate with chat view
Update topic and session selection to set active state in store
This commit is contained in:
icarus 2025-09-19 16:39:02 +08:00
parent 64e3de9ada
commit 5ddf9683b4
4 changed files with 99 additions and 18 deletions

View File

@ -6,6 +6,7 @@ import PromptPopup from '@renderer/components/Popups/PromptPopup'
import { QuickPanelProvider } from '@renderer/components/QuickPanel'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useChatContext } from '@renderer/hooks/useChatContext'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
import { useShortcut } from '@renderer/hooks/useShortcuts'
import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore'
@ -23,6 +24,7 @@ import styled from 'styled-components'
import ChatNavbar from './ChatNavbar'
import Inputbar from './Inputbar/Inputbar'
import AgentSessionMessages from './Messages/AgentSessionMessages'
import ChatNavigation from './Messages/ChatNavigation'
import Messages from './Messages/Messages'
import Tabs from './Tabs'
@ -44,6 +46,8 @@ const Chat: FC<Props> = (props) => {
const { isMultiSelectMode } = useChatContext(props.activeTopic)
const { isTopNavbar } = useNavbarPosition()
const chatMaxWidth = useChatMaxWidth()
const { chat } = useRuntime()
const { activeTopicOrSession, activeAgentId, activeSessionId } = chat
const mainRef = React.useRef<HTMLDivElement>(null)
const contentSearchRef = React.useRef<ContentSearchRef>(null)
@ -136,6 +140,17 @@ const Chat: FC<Props> = (props) => {
? 'calc(100vh - var(--navbar-height) - var(--navbar-height) - 12px)'
: 'calc(100vh - var(--navbar-height))'
const SessionMessages = () => {
if (activeAgentId === null) {
return <div> Active Agent ID is invalid.</div>
}
const sessionId = activeSessionId[activeAgentId]
if (!sessionId) {
return <div> Active Session ID is invalid.</div>
}
return <AgentSessionMessages agentId={activeAgentId} sessionId={sessionId} />
}
return (
<Container id="chat" className={classNames([messageStyle, { 'multi-select-mode': isMultiSelectMode }])}>
{isTopNavbar && (
@ -156,23 +171,28 @@ const Chat: FC<Props> = (props) => {
justify="space-between"
style={{ maxWidth: chatMaxWidth, height: mainHeight }}>
<QuickPanelProvider>
<Messages
key={props.activeTopic.id}
assistant={assistant}
topic={props.activeTopic}
setActiveTopic={props.setActiveTopic}
onComponentUpdate={messagesComponentUpdateHandler}
onFirstUpdate={messagesComponentFirstUpdateHandler}
/>
<ContentSearch
ref={contentSearchRef}
searchTarget={mainRef as React.RefObject<HTMLElement>}
filter={contentSearchFilter}
includeUser={filterIncludeUser}
onIncludeUserChange={userOutlinedItemClickHandler}
/>
{messageNavigation === 'buttons' && <ChatNavigation containerId="messages" />}
<Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} topic={props.activeTopic} />
{activeTopicOrSession === 'topic' && (
<>
<Messages
key={props.activeTopic.id}
assistant={assistant}
topic={props.activeTopic}
setActiveTopic={props.setActiveTopic}
onComponentUpdate={messagesComponentUpdateHandler}
onFirstUpdate={messagesComponentFirstUpdateHandler}
/>
<ContentSearch
ref={contentSearchRef}
searchTarget={mainRef as React.RefObject<HTMLElement>}
filter={contentSearchFilter}
includeUser={filterIncludeUser}
onIncludeUserChange={userOutlinedItemClickHandler}
/>
{messageNavigation === 'buttons' && <ChatNavigation containerId="messages" />}
<Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} topic={props.activeTopic} />
</>
)}
{activeTopicOrSession === 'session' && <SessionMessages />}
{isMultiSelectMode && <MultiSelectActionPopup topic={props.activeTopic} />}
</QuickPanelProvider>
</Main>

View File

@ -5,6 +5,7 @@ import { useActiveTopic } from '@renderer/hooks/useTopic'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import NavigationService from '@renderer/services/NavigationService'
import { newMessagesActions } from '@renderer/store/newMessage'
import { setActiveTopicOrSessionAction } from '@renderer/store/runtime'
import { Assistant, Topic } from '@renderer/types'
import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH, SECOND_MIN_WINDOW_WIDTH } from '@shared/config/constant'
import { AnimatePresence, motion } from 'motion/react'
@ -52,6 +53,7 @@ const HomePage: FC = () => {
startTransition(() => {
_setActiveTopic((prev) => (newTopic?.id === prev.id ? prev : newTopic))
dispatch(newMessagesActions.setTopicFulfilled({ topicId: newTopic.id, fulfilled: false }))
dispatch(setActiveTopicOrSessionAction('topic'))
})
},
[_setActiveTopic, dispatch]

View File

@ -0,0 +1,58 @@
import ContextMenu from '@renderer/components/ContextMenu'
import Scrollbar from '@renderer/components/Scrollbar'
import { useSession } from '@renderer/hooks/agents/useSession'
import { memo } from 'react'
import styled from 'styled-components'
import NarrowLayout from './NarrowLayout'
type Props = {
agentId: string
sessionId: string
}
const AgentSessionMessages: React.FC<Props> = ({ agentId, sessionId }) => {
const { messages } = useSession(agentId, sessionId)
return (
<MessagesContainer id="messages" className="messages-container">
<NarrowLayout style={{ display: 'flex', flexDirection: 'column-reverse' }}>
<ContextMenu>
<ScrollContainer>
{messages.map((message) => {
const content = message.content.content
if (typeof content === 'string') {
return <div key={message.id}>{content}</div>
} else {
return 'Not string content'
}
})}
</ScrollContainer>
</ContextMenu>
</NarrowLayout>
</MessagesContainer>
)
}
const ScrollContainer = styled.div`
display: flex;
flex-direction: column-reverse;
padding: 10px 10px 20px;
.multi-select-mode & {
padding-bottom: 60px;
}
`
interface ContainerProps {
$right?: boolean
}
const MessagesContainer = styled(Scrollbar)<ContainerProps>`
display: flex;
flex-direction: column-reverse;
overflow-x: hidden;
z-index: 1;
position: relative;
`
export default memo(AgentSessionMessages)

View File

@ -2,7 +2,7 @@ import { Button, Spinner } from '@heroui/react'
import { SessionModal } from '@renderer/components/Popups/agent/SessionModal'
import { useSessions } from '@renderer/hooks/agents/useSessions'
import { useAppDispatch } from '@renderer/store'
import { setActiveSessionIdAction } from '@renderer/store/runtime'
import { setActiveSessionIdAction, setActiveTopicOrSessionAction } from '@renderer/store/runtime'
import { Plus } from 'lucide-react'
import { memo, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
@ -23,6 +23,7 @@ const Sessions: React.FC<SessionsProps> = ({ agentId }) => {
const setActiveSessionId = useCallback(
(agentId: string, sessionId: string | null) => {
dispatch(setActiveSessionIdAction({ agentId, sessionId }))
dispatch(setActiveTopicOrSessionAction('session'))
},
[dispatch]
)