Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 4,513 Bytes
79b2fcc 67b16c6 79b2fcc 67b16c6 79b2fcc 67b16c6 2a2e170 79b2fcc 0611031 67b16c6 79b2fcc 3ce798d 79b2fcc 2a2e170 79b2fcc 67b16c6 79b2fcc 67b16c6 79b2fcc 67b16c6 79b2fcc 67b16c6 79b2fcc 72af187 79b2fcc 3ce798d 79b2fcc 0611031 79b2fcc 2a2e170 79b2fcc 3ce798d 79b2fcc 67b16c6 79b2fcc | 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 153 154 155 156 | 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;
sessionId?: string | null;
approveTools: (approvals: Array<{ tool_call_id: string; approved: boolean; feedback?: string | null }>) => Promise<boolean>;
onUndoLastTurn: () => void | Promise<void>;
onEditAndRegenerate?: (messageId: string, newText: string) => 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, sessionId, approveTools, onUndoLastTurn, onEditAndRegenerate }: 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}
onEditAndRegenerate={onEditAndRegenerate}
isProcessing={isProcessing}
isStreaming={isProcessing && msg.id === lastAssistantId}
sessionId={sessionId}
approveTools={approveTools}
/>
))
)}
<ActivityStatusBar />
<div />
</Stack>
</Box>
);
}
|