重构组件,优化性能并移除不必要的属性;撤销滚动条修改;

This commit is contained in:
2026-06-03 16:58:10 +08:00
parent fa3e6b6e84
commit 06a3f32d2d
4 changed files with 30 additions and 37 deletions
+15 -1
View File
@@ -85,7 +85,12 @@ const formatToolTitle = (item: ChatProgress) => {
return item.title; return item.title;
}; };
export const AgentProgressTimeline = ({ progress, isAborted }: { progress: ChatProgress[], isAborted?: boolean }) => { type AgentProgressTimelineProps = {
progress: ChatProgress[];
isAborted?: boolean;
};
const AgentProgressTimelineInner = ({ progress, isAborted }: AgentProgressTimelineProps) => {
const theme = useTheme(); const theme = useTheme();
const [nowMs, setNowMs] = useState(() => Date.now()); const [nowMs, setNowMs] = useState(() => Date.now());
@@ -356,3 +361,12 @@ export const AgentProgressTimeline = ({ progress, isAborted }: { progress: ChatP
</Box> </Box>
); );
}; };
export const AgentProgressTimeline = React.memo(
AgentProgressTimelineInner,
(prevProps, nextProps) =>
prevProps.progress === nextProps.progress &&
prevProps.isAborted === nextProps.isAborted,
);
AgentProgressTimeline.displayName = "AgentProgressTimeline";
+15 -9
View File
@@ -1,7 +1,7 @@
"use client"; "use client";
import Image from "next/image"; import Image from "next/image";
import React from "react"; import React, { useMemo } from "react";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm";
import { AnimatePresence, motion } from "framer-motion"; import { AnimatePresence, motion } from "framer-motion";
@@ -85,15 +85,21 @@ export const AgentTurn = React.memo(
const [editDraft, setEditDraft] = React.useState(message.content); const [editDraft, setEditDraft] = React.useState(message.content);
const rootMessageId = message.branchRootId ?? message.id; const rootMessageId = message.branchRootId ?? message.id;
const parsedAssistantSections = const parsedAssistantSections = useMemo(
!isUser && !isErrorMessage () =>
? parseAssistantMessageSections(message.content) !isUser && !isErrorMessage
: null; ? parseAssistantMessageSections(message.content)
: null,
[isErrorMessage, isUser, message.content],
);
const answerContent = parsedAssistantSections?.answer ?? message.content; const answerContent = parsedAssistantSections?.answer ?? message.content;
const contentSegments: ContentSegment[] = const contentSegments: ContentSegment[] = useMemo(
!isUser && !isErrorMessage () =>
? parseContentWithToolCalls(answerContent).segments !isUser && !isErrorMessage
: [{ type: "text", content: answerContent }]; ? parseContentWithToolCalls(answerContent).segments
: [{ type: "text", content: answerContent }],
[answerContent, isErrorMessage, isUser],
);
if (isUser) { if (isUser) {
return ( return (
-6
View File
@@ -23,8 +23,6 @@ type AgentWorkspaceProps = {
branchGroups: BranchGroup[]; branchGroups: BranchGroup[];
branchTransition: BranchTransition | null; branchTransition: BranchTransition | null;
isStreaming: boolean; isStreaming: boolean;
scrollContainerRef: React.RefObject<HTMLDivElement | null>;
onScroll: React.UIEventHandler<HTMLDivElement>;
bottomRef: React.RefObject<HTMLDivElement | null>; bottomRef: React.RefObject<HTMLDivElement | null>;
speakingMessageId: string | null; speakingMessageId: string | null;
speechState: SpeechState; speechState: SpeechState;
@@ -157,8 +155,6 @@ export const AgentWorkspace = ({
branchGroups, branchGroups,
branchTransition, branchTransition,
isStreaming, isStreaming,
scrollContainerRef,
onScroll,
bottomRef, bottomRef,
speakingMessageId, speakingMessageId,
speechState, speechState,
@@ -220,8 +216,6 @@ export const AgentWorkspace = ({
return ( return (
<Box <Box
ref={scrollContainerRef}
onScroll={onScroll}
sx={{ sx={{
flex: 1, flex: 1,
overflowY: "auto", overflowY: "auto",
-21
View File
@@ -30,10 +30,8 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
); );
const bottomRef = useRef<HTMLDivElement>(null); const bottomRef = useRef<HTMLDivElement>(null);
const workspaceRef = useRef<HTMLDivElement | null>(null);
const composerRef = useRef<AgentComposerHandle | null>(null); const composerRef = useRef<AgentComposerHandle | null>(null);
const hasResetForOpenRef = useRef(false); const hasResetForOpenRef = useRef(false);
const shouldAutoScrollRef = useRef(true);
const theme = useTheme(); const theme = useTheme();
const currentProjectId = useProjectStore((state) => state.currentProjectId); const currentProjectId = useProjectStore((state) => state.currentProjectId);
@@ -84,20 +82,11 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
getModel: () => selectedModel, getModel: () => selectedModel,
}); });
const syncAutoScrollState = useCallback(() => {
const container = workspaceRef.current;
if (!container) return;
const distanceFromBottom =
container.scrollHeight - container.scrollTop - container.clientHeight;
shouldAutoScrollRef.current = distanceFromBottom <= 120;
}, []);
const scrollToBottom = useCallback((behavior: ScrollBehavior = "smooth") => { const scrollToBottom = useCallback((behavior: ScrollBehavior = "smooth") => {
bottomRef.current?.scrollIntoView({ behavior }); bottomRef.current?.scrollIntoView({ behavior });
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!shouldAutoScrollRef.current) return;
scrollToBottom(isStreaming ? "auto" : "smooth"); scrollToBottom(isStreaming ? "auto" : "smooth");
}, [isStreaming, messages, scrollToBottom]); }, [isStreaming, messages, scrollToBottom]);
@@ -110,7 +99,6 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
hasResetForOpenRef.current = true; hasResetForOpenRef.current = true;
const timer = window.setTimeout(() => { const timer = window.setTimeout(() => {
shouldAutoScrollRef.current = true;
createSession(); createSession();
composerRef.current?.clear(); composerRef.current?.clear();
setIsHistoryOpen(false); setIsHistoryOpen(false);
@@ -122,14 +110,12 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
const handleSend = useCallback((prompt: string) => { const handleSend = useCallback((prompt: string) => {
if (isStreaming) return; if (isStreaming) return;
shouldAutoScrollRef.current = true;
void sendPrompt(prompt); void sendPrompt(prompt);
}, [isStreaming, sendPrompt]); }, [isStreaming, sendPrompt]);
const handleNewConversation = useCallback(() => { const handleNewConversation = useCallback(() => {
handleStopSpeech(); handleStopSpeech();
stopListening(); stopListening();
shouldAutoScrollRef.current = true;
createSession(); createSession();
composerRef.current?.clear(); composerRef.current?.clear();
window.setTimeout(() => { window.setTimeout(() => {
@@ -144,17 +130,12 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
const handleSelectSession = useCallback( const handleSelectSession = useCallback(
(storageSessionId: string) => { (storageSessionId: string) => {
shouldAutoScrollRef.current = true;
composerRef.current?.clear(); composerRef.current?.clear();
void switchSession(storageSessionId); void switchSession(storageSessionId);
}, },
[switchSession], [switchSession],
); );
const handleWorkspaceScroll = useCallback(() => {
syncAutoScrollState();
}, [syncAutoScrollState]);
const handleDeleteSession = useCallback( const handleDeleteSession = useCallback(
(storageSessionId: string) => { (storageSessionId: string) => {
void removeSession(storageSessionId); void removeSession(storageSessionId);
@@ -334,8 +315,6 @@ export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
branchGroups={branchGroups} branchGroups={branchGroups}
branchTransition={branchTransition} branchTransition={branchTransition}
isStreaming={isStreaming} isStreaming={isStreaming}
scrollContainerRef={workspaceRef}
onScroll={handleWorkspaceScroll}
bottomRef={bottomRef} bottomRef={bottomRef}
speakingMessageId={speakingMessageId} speakingMessageId={speakingMessageId}
speechState={speechState} speechState={speechState}