Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 4,274 Bytes
bdbcdab a56db97 bdbcdab a56db97 bdbcdab a56db97 bdbcdab a56db97 bdbcdab b1d24de bdbcdab a56db97 bdbcdab a56db97 bdbcdab a56db97 bdbcdab a56db97 bdbcdab aaf7d3b bdbcdab b1d24de bdbcdab b1d24de bdbcdab a56db97 bdbcdab | 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>
);
}
|