aura-web/src/pages/chat/ChatPage.tsx

160 lines
5.7 KiB
TypeScript

import { useEffect, useRef, useState } from 'react';
import { App as AntApp, Empty } from 'antd';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import type { ModelOverrides } from '../../api';
import AgentSidebar from './components/AgentSidebar';
import ChatHeader from './components/ChatHeader';
import ChatBody from './components/ChatBody';
import ChatInput from './components/ChatInput';
import ChatDrawers from './components/ChatDrawers';
import { useChatScroll } from './hooks/useChatScroll';
import { useChatData } from './hooks/useChatData';
import { useChatSender } from './hooks/useChatSender';
export default function ChatPage() {
const { id } = useParams();
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const { message } = AntApp.useApp();
const [sessionId, setSessionId] = useState<string | null>(null);
const [highlightId, setHighlightId] = useState<string | null>(null);
const [overrides, setOverrides] = useState<ModelOverrides>({});
const [historyDrawerOpen, setHistoryDrawerOpen] = useState(false);
const [mcpDrawerOpen, setMcpDrawerOpen] = useState(false);
const [paramsDrawerOpen, setParamsDrawerOpen] = useState(false);
const [tplDrawerOpen, setTplDrawerOpen] = useState(false);
const abortRef = useRef<AbortController | null>(null);
const { bodyRef, scrollBottom, initialScrollDoneRef } = useChatScroll();
useEffect(() => {
const s = searchParams.get('session');
const m = searchParams.get('msg');
if (s) {
setSessionId(s);
if (m) setHighlightId(m);
const next = new URLSearchParams(searchParams);
next.delete('session');
next.delete('msg');
setSearchParams(next, { replace: true });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchParams]);
const { agent, agentList, messages, setMessages, branches, setBranches, loadMessages } = useChatData({
agentId: id,
sessionId,
highlightId,
setHighlightId,
scrollBottom,
initialScrollDoneRef,
setOverrides: (updater) => setOverrides(updater),
abort: () => abortRef.current?.abort()
});
const sender = useChatSender({
agentId: id,
agent,
sessionId,
overrides,
setOverrides: (updater) => setOverrides(updater),
messages,
setMessages,
setBranches,
loadMessages,
scrollBottom,
notify: { success: (t) => message.success(t), error: (t) => message.error(t) },
abortRef
});
return (
<div className="chat-shell">
<AgentSidebar
agentList={agentList}
activeAgentId={id}
onCreate={() => navigate('/agents/new')}
onSelect={(aid) => navigate(`/chat/${aid}`)}
/>
<section className="chat-main">
{!agent ? (
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Empty description="请在左侧选择一个智能体开始对话" />
</div>
) : (
<>
<ChatHeader
agent={agent}
useStream={sender.useStream}
setUseStream={sender.setUseStream}
onOpenHistory={() => setHistoryDrawerOpen(true)}
onOpenParams={() => setParamsDrawerOpen(true)}
onOpenMcp={() => setMcpDrawerOpen(true)}
onManageAgent={() => navigate(`/agents/${id}`)}
onClear={sender.handleClear}
/>
<ChatBody
bodyRef={bodyRef}
agent={agent}
messages={messages}
branches={branches}
highlightId={highlightId}
sending={sender.sending}
streaming={sender.streaming}
onRegenerate={sender.handleRegenerate}
onSwitchBranch={sender.handleSwitchBranch}
onCopy={(text) => navigator.clipboard?.writeText(text).then(() => message.success('已复制'))}
/>
<ChatInput
input={sender.input}
setInput={sender.setInput}
sending={sender.sending}
attachments={sender.attachments}
setAttachments={(updater) => sender.setAttachments(updater)}
imageUrls={sender.imageUrls}
setImageUrls={(updater) => sender.setImageUrls(updater)}
onSend={sender.handleSend}
onStop={sender.handleStop}
onAttach={sender.handleAttach}
onOpenTpl={() => setTplDrawerOpen(true)}
modelOptions={sender.modelOptions}
activeModelValue={sender.activeModelValue}
onChangeModel={(modelId) => {
const selected = sender.modelOptions.find((m) => m.value === modelId);
setOverrides((o) => ({ ...o, model_id: String(modelId), model: selected?.label || String(modelId) }));
}}
/>
</>
)}
</section>
<ChatDrawers
agentId={id}
agent={agent}
input={sender.input}
setInput={(updater) => sender.setInput(updater(sender.input))}
overrides={overrides}
setOverrides={(updater) => setOverrides(updater)}
sessionId={sessionId}
setSessionId={setSessionId}
setHighlightId={setHighlightId}
sessionRefresh={sender.sessionRefresh}
mcpDrawerOpen={mcpDrawerOpen}
setMcpDrawerOpen={setMcpDrawerOpen}
tplDrawerOpen={tplDrawerOpen}
setTplDrawerOpen={setTplDrawerOpen}
paramsDrawerOpen={paramsDrawerOpen}
setParamsDrawerOpen={setParamsDrawerOpen}
historyDrawerOpen={historyDrawerOpen}
setHistoryDrawerOpen={setHistoryDrawerOpen}
notify={{ success: (t) => message.success(t) }}
/>
</div>
);
}