Spaces:
Running
Running
File size: 4,274 Bytes
7eefa0d 9dbf093 a56db97 0f03c11 e77f678 7eefa0d a56db97 7eefa0d a56db97 7eefa0d a56db97 9dbf093 e77f678 9dbf093 e77f678 9dbf093 e77f678 9dbf093 e77f678 9dbf093 e77f678 b1d24de 7eefa0d e77f678 a56db97 e77f678 7eefa0d e77f678 7eefa0d e77f678 7eefa0d e77f678 7eefa0d a56db97 e77f678 a56db97 e77f678 9dbf093 a56db97 e77f678 9dbf093 e77f678 9dbf093 e77f678 7eefa0d e77f678 7eefa0d e77f678 b1d24de 0f03c11 e77f678 a56db97 e77f678 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | import { useCallback, useEffect, useRef, useMemo } from 'react';
import { Box, Stack, Typography } from '@mui/material';
import MessageBubble from './MessageBubble';
import ActivityStatusBar from './ActivityStatusBar';
import { useAgentStore } from '@/store/agentStore';
import type { UIMessage } from 'ai';
interface MessageListProps {
messages: UIMessage[];
isProcessing: boolean;
approveTools: (approvals: Array<{ tool_call_id: string; approved: boolean; feedback?: string | null }>) => Promise<boolean>;
onUndoLastTurn: () => void | Promise<void>;
}
function getGreeting(): string {
const h = new Date().getHours();
if (h < 12) return 'Morning';
if (h < 17) return 'Afternoon';
return 'Evening';
}
function WelcomeGreeting() {
const { user } = useAgentStore();
const firstName = user?.name?.split(' ')[0] || user?.username;
const greeting = firstName ? `${getGreeting()}, ${firstName}` : getGreeting();
return (
<Box
sx={{
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
py: 8,
gap: 1.5,
}}
>
<Typography
sx={{
fontFamily: 'monospace',
fontSize: '1.6rem',
color: 'var(--text)',
fontWeight: 600,
}}
>
{greeting}
</Typography>
<Typography
color="text.secondary"
sx={{ fontFamily: 'monospace', fontSize: '0.9rem' }}
>
Let's build something impressive?
</Typography>
</Box>
);
}
export default function MessageList({ messages, isProcessing, approveTools, onUndoLastTurn }: MessageListProps) {
const scrollContainerRef = useRef<HTMLDivElement>(null);
const stickToBottom = useRef(true);
const scrollToBottom = useCallback(() => {
const el = scrollContainerRef.current;
if (el) el.scrollTop = el.scrollHeight;
}, []);
useEffect(() => {
const el = scrollContainerRef.current;
if (!el) return;
const onScroll = () => {
const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
stickToBottom.current = distFromBottom < 80;
};
el.addEventListener('scroll', onScroll, { passive: true });
return () => el.removeEventListener('scroll', onScroll);
}, []);
useEffect(() => {
if (stickToBottom.current) scrollToBottom();
}, [messages, isProcessing, scrollToBottom]);
useEffect(() => {
const el = scrollContainerRef.current;
if (!el) return;
const observer = new MutationObserver(() => {
if (stickToBottom.current) el.scrollTop = el.scrollHeight;
});
observer.observe(el, { childList: true, subtree: true, characterData: true });
return () => observer.disconnect();
}, []);
const lastUserMsgId = useMemo(() => {
for (let i = messages.length - 1; i >= 0; i--) {
if (messages[i].role === 'user') return messages[i].id;
}
return null;
}, [messages]);
// The last assistant message is "streaming" when we're processing
const lastAssistantId = useMemo(() => {
for (let i = messages.length - 1; i >= 0; i--) {
if (messages[i].role === 'assistant') return messages[i].id;
}
return null;
}, [messages]);
return (
<Box
ref={scrollContainerRef}
sx={{
flex: 1,
overflow: 'auto',
px: { xs: 0.5, sm: 1, md: 2 },
py: { xs: 2, md: 3 },
display: 'flex',
flexDirection: 'column',
}}
>
<Stack
spacing={3}
sx={{
maxWidth: 880,
mx: 'auto',
width: '100%',
flex: messages.length === 0 && !isProcessing ? 1 : undefined,
}}
>
{messages.length === 0 && !isProcessing ? (
<WelcomeGreeting />
) : (
messages.map((msg) => (
<MessageBubble
key={msg.id}
message={msg}
isLastTurn={msg.id === lastUserMsgId}
onUndoTurn={onUndoLastTurn}
isProcessing={isProcessing}
isStreaming={isProcessing && msg.id === lastAssistantId}
approveTools={approveTools}
/>
))
)}
<ActivityStatusBar />
<div />
</Stack>
</Box>
);
}
|