refactor(chat): remove typing indicator
This commit is contained in:
@@ -34,10 +34,6 @@ jest.mock("framer-motion", () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("./GlobalChatbox.parts", () => ({
|
|
||||||
TypingIndicator: () => <div>typing</div>,
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock("./AgentTurn", () => ({
|
jest.mock("./AgentTurn", () => ({
|
||||||
AgentTurn: ({ message, isStreaming }: { message: Message; isStreaming: boolean }) => {
|
AgentTurn: ({ message, isStreaming }: { message: Message; isStreaming: boolean }) => {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import TroubleshootRounded from "@mui/icons-material/TroubleshootRounded";
|
|||||||
import MapRounded from "@mui/icons-material/MapRounded";
|
import MapRounded from "@mui/icons-material/MapRounded";
|
||||||
|
|
||||||
import { AgentTurn } from "./AgentTurn";
|
import { AgentTurn } from "./AgentTurn";
|
||||||
import { TypingIndicator } from "./GlobalChatbox.parts";
|
|
||||||
import type { PermissionReply } from "@/lib/chatStream";
|
import type { PermissionReply } from "@/lib/chatStream";
|
||||||
import type {
|
import type {
|
||||||
Message,
|
Message,
|
||||||
@@ -39,7 +38,7 @@ type AgentWorkspaceProps = {
|
|||||||
|
|
||||||
type TurnListProps = {
|
type TurnListProps = {
|
||||||
messages: Message[];
|
messages: Message[];
|
||||||
isStreaming: boolean;
|
isAssistantStreaming: boolean;
|
||||||
streamingMessageId: string | null;
|
streamingMessageId: string | null;
|
||||||
speakingMessageId: string | null;
|
speakingMessageId: string | null;
|
||||||
speechState: SpeechState;
|
speechState: SpeechState;
|
||||||
@@ -65,7 +64,7 @@ const TurnItem = React.memo(AgentTurn);
|
|||||||
|
|
||||||
const TurnListInner = ({
|
const TurnListInner = ({
|
||||||
messages,
|
messages,
|
||||||
isStreaming,
|
isAssistantStreaming,
|
||||||
streamingMessageId,
|
streamingMessageId,
|
||||||
speakingMessageId,
|
speakingMessageId,
|
||||||
speechState,
|
speechState,
|
||||||
@@ -85,7 +84,7 @@ const TurnListInner = ({
|
|||||||
<TurnItem
|
<TurnItem
|
||||||
key={message.id}
|
key={message.id}
|
||||||
message={message}
|
message={message}
|
||||||
isStreaming={isStreaming && message.id === streamingMessageId}
|
isStreaming={isAssistantStreaming && message.id === streamingMessageId}
|
||||||
messageSpeechState={speakingMessageId === message.id ? speechState : "idle"}
|
messageSpeechState={speakingMessageId === message.id ? speechState : "idle"}
|
||||||
onSpeak={onSpeak}
|
onSpeak={onSpeak}
|
||||||
onPause={onPauseSpeech}
|
onPause={onPauseSpeech}
|
||||||
@@ -106,7 +105,7 @@ const TurnList = React.memo(
|
|||||||
TurnListInner,
|
TurnListInner,
|
||||||
(prevProps, nextProps) =>
|
(prevProps, nextProps) =>
|
||||||
sameMessages(prevProps.messages, nextProps.messages) &&
|
sameMessages(prevProps.messages, nextProps.messages) &&
|
||||||
prevProps.isStreaming === nextProps.isStreaming &&
|
prevProps.isAssistantStreaming === nextProps.isAssistantStreaming &&
|
||||||
prevProps.streamingMessageId === nextProps.streamingMessageId &&
|
prevProps.streamingMessageId === nextProps.streamingMessageId &&
|
||||||
prevProps.speakingMessageId === nextProps.speakingMessageId &&
|
prevProps.speakingMessageId === nextProps.speakingMessageId &&
|
||||||
prevProps.speechState === nextProps.speechState &&
|
prevProps.speechState === nextProps.speechState &&
|
||||||
@@ -318,19 +317,10 @@ export const AgentWorkspace = ({
|
|||||||
onReplyQuestion,
|
onReplyQuestion,
|
||||||
onRejectQuestion,
|
onRejectQuestion,
|
||||||
}: AgentWorkspaceProps) => {
|
}: AgentWorkspaceProps) => {
|
||||||
const theme = useTheme();
|
const streamingMessageId =
|
||||||
const latestAssistant = [...messages]
|
|
||||||
.reverse()
|
|
||||||
.find((message) => message.role === "assistant");
|
|
||||||
const showTypingIndicator =
|
|
||||||
isStreaming &&
|
|
||||||
(!latestAssistant ||
|
|
||||||
(latestAssistant.content.trim().length === 0 &&
|
|
||||||
!(latestAssistant.artifacts?.length)));
|
|
||||||
const streamingMessage =
|
|
||||||
isStreaming && messages.at(-1)?.role === "assistant"
|
isStreaming && messages.at(-1)?.role === "assistant"
|
||||||
? messages.at(-1)
|
? messages.at(-1)?.id ?? null
|
||||||
: undefined;
|
: null;
|
||||||
const handleScroll = React.useCallback(
|
const handleScroll = React.useCallback(
|
||||||
(event: React.UIEvent<HTMLDivElement>) => {
|
(event: React.UIEvent<HTMLDivElement>) => {
|
||||||
if (!onScrollStateChange) return;
|
if (!onScrollStateChange) return;
|
||||||
@@ -372,8 +362,8 @@ export const AgentWorkspace = ({
|
|||||||
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||||
<TurnList
|
<TurnList
|
||||||
messages={messages}
|
messages={messages}
|
||||||
isStreaming={isStreaming}
|
isAssistantStreaming={isStreaming}
|
||||||
streamingMessageId={streamingMessage?.id ?? null}
|
streamingMessageId={streamingMessageId}
|
||||||
speakingMessageId={speakingMessageId}
|
speakingMessageId={speakingMessageId}
|
||||||
speechState={speechState}
|
speechState={speechState}
|
||||||
onSpeak={onSpeak}
|
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
|
<div
|
||||||
ref={bottomRef}
|
ref={bottomRef}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -2,36 +2,6 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { motion } from "framer-motion";
|
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 = ({
|
export const Blob = ({
|
||||||
color,
|
color,
|
||||||
|
|||||||
Reference in New Issue
Block a user