refactor(chat): remove typing indicator
Build Push and Deploy / docker-image (push) Successful in 1m2s
Build Push and Deploy / deploy-fallback-log (push) Has been skipped

This commit is contained in:
2026-06-10 21:33:33 +08:00
parent a6ea97142a
commit 877b79ada8
3 changed files with 9 additions and 74 deletions
@@ -34,10 +34,6 @@ jest.mock("framer-motion", () => ({
},
}));
jest.mock("./GlobalChatbox.parts", () => ({
TypingIndicator: () => <div>typing</div>,
}));
jest.mock("./AgentTurn", () => ({
AgentTurn: ({ message, isStreaming }: { message: Message; isStreaming: boolean }) => {
React.useEffect(() => {
+9 -40
View File
@@ -10,7 +10,6 @@ import TroubleshootRounded from "@mui/icons-material/TroubleshootRounded";
import MapRounded from "@mui/icons-material/MapRounded";
import { AgentTurn } from "./AgentTurn";
import { TypingIndicator } from "./GlobalChatbox.parts";
import type { PermissionReply } from "@/lib/chatStream";
import type {
Message,
@@ -39,7 +38,7 @@ type AgentWorkspaceProps = {
type TurnListProps = {
messages: Message[];
isStreaming: boolean;
isAssistantStreaming: boolean;
streamingMessageId: string | null;
speakingMessageId: string | null;
speechState: SpeechState;
@@ -65,7 +64,7 @@ const TurnItem = React.memo(AgentTurn);
const TurnListInner = ({
messages,
isStreaming,
isAssistantStreaming,
streamingMessageId,
speakingMessageId,
speechState,
@@ -85,7 +84,7 @@ const TurnListInner = ({
<TurnItem
key={message.id}
message={message}
isStreaming={isStreaming && message.id === streamingMessageId}
isStreaming={isAssistantStreaming && message.id === streamingMessageId}
messageSpeechState={speakingMessageId === message.id ? speechState : "idle"}
onSpeak={onSpeak}
onPause={onPauseSpeech}
@@ -106,7 +105,7 @@ const TurnList = React.memo(
TurnListInner,
(prevProps, nextProps) =>
sameMessages(prevProps.messages, nextProps.messages) &&
prevProps.isStreaming === nextProps.isStreaming &&
prevProps.isAssistantStreaming === nextProps.isAssistantStreaming &&
prevProps.streamingMessageId === nextProps.streamingMessageId &&
prevProps.speakingMessageId === nextProps.speakingMessageId &&
prevProps.speechState === nextProps.speechState &&
@@ -318,19 +317,10 @@ export const AgentWorkspace = ({
onReplyQuestion,
onRejectQuestion,
}: AgentWorkspaceProps) => {
const theme = useTheme();
const latestAssistant = [...messages]
.reverse()
.find((message) => message.role === "assistant");
const showTypingIndicator =
isStreaming &&
(!latestAssistant ||
(latestAssistant.content.trim().length === 0 &&
!(latestAssistant.artifacts?.length)));
const streamingMessage =
const streamingMessageId =
isStreaming && messages.at(-1)?.role === "assistant"
? messages.at(-1)
: undefined;
? messages.at(-1)?.id ?? null
: null;
const handleScroll = React.useCallback(
(event: React.UIEvent<HTMLDivElement>) => {
if (!onScrollStateChange) return;
@@ -372,8 +362,8 @@ export const AgentWorkspace = ({
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
<TurnList
messages={messages}
isStreaming={isStreaming}
streamingMessageId={streamingMessage?.id ?? null}
isAssistantStreaming={isStreaming}
streamingMessageId={streamingMessageId}
speakingMessageId={speakingMessageId}
speechState={speechState}
onSpeak={onSpeak}
@@ -391,27 +381,6 @@ export const AgentWorkspace = ({
</>
)}
{!isLoadingSession && showTypingIndicator ? (
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.94 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
transition={{ type: "spring", stiffness: 300 }}
style={{ alignSelf: "flex-start", display: "flex", gap: 12, marginTop: 4, marginLeft: 44 }}
>
<Paper
elevation={0}
sx={{
p: 1.3,
borderRadius: 4,
bgcolor: alpha("#fff", 0.82),
boxShadow: `0 4px 12px ${alpha(theme.palette.common.black, 0.05)}`,
}}
>
<TypingIndicator />
</Paper>
</motion.div>
) : null}
<div
ref={bottomRef}
style={{
@@ -2,36 +2,6 @@
import React from "react";
import { motion } from "framer-motion";
import { Box, Stack } from "@mui/material";
export const TypingIndicator = () => {
return (
<Stack direction="row" spacing={0.5} alignItems="center" sx={{ p: 1 }}>
{[0, 1, 2].map((i) => (
<motion.div
key={i}
initial={{ y: 0 }}
animate={{ y: [-4, 4, -4] }}
transition={{
duration: 0.6,
repeat: Infinity,
delay: i * 0.15,
ease: "easeInOut",
}}
>
<Box
sx={{
width: 8,
height: 8,
borderRadius: "50%",
background: "linear-gradient(135deg, #FF6B6B 0%, #FF8E53 100%)",
}}
/>
</motion.div>
))}
</Stack>
);
};
export const Blob = ({
color,