Spaces:
Running
Running
Commit
Β·
3cb2d2e
1
Parent(s):
fe018ce
welcome message change
Browse files
frontend/src/components/Chat/MessageList.tsx
CHANGED
|
@@ -1,9 +1,7 @@
|
|
| 1 |
import { useEffect, useRef, useMemo, useCallback } from 'react';
|
| 2 |
-
import { Box, Stack, Typography
|
| 3 |
-
import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined';
|
| 4 |
import MessageBubble from './MessageBubble';
|
| 5 |
import ThinkingIndicator from './ThinkingIndicator';
|
| 6 |
-
import MarkdownContent from './MarkdownContent';
|
| 7 |
import { useAgentStore } from '@/store/agentStore';
|
| 8 |
import { useSessionStore } from '@/store/sessionStore';
|
| 9 |
import { apiFetch } from '@/utils/api';
|
|
@@ -15,66 +13,48 @@ interface MessageListProps {
|
|
| 15 |
isProcessing: boolean;
|
| 16 |
}
|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
**Models** β Search and discover models Β· Get details and configs Β· Deploy for inference
|
| 25 |
-
|
| 26 |
-
**Research** β Find papers and documentation Β· Explore code examples Β· Check APIs and best practices
|
| 27 |
-
|
| 28 |
-
**Infrastructure** β Run jobs on CPU/GPU instances Β· Manage repos, branches, PRs Β· Monitor Spaces and endpoints
|
| 29 |
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
-
/** Static welcome message rendered when the conversation is empty. */
|
| 33 |
-
function WelcomeMessage() {
|
| 34 |
return (
|
| 35 |
-
<
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
sx={{
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
mt: 0.5,
|
| 43 |
}}
|
| 44 |
>
|
| 45 |
-
|
| 46 |
-
</
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
fontSize: '0.72rem',
|
| 55 |
-
color: 'var(--muted-text)',
|
| 56 |
-
textTransform: 'uppercase',
|
| 57 |
-
letterSpacing: '0.04em',
|
| 58 |
-
}}
|
| 59 |
-
>
|
| 60 |
-
Assistant
|
| 61 |
-
</Typography>
|
| 62 |
-
</Stack>
|
| 63 |
-
<Box
|
| 64 |
-
sx={{
|
| 65 |
-
maxWidth: { xs: '95%', md: '85%' },
|
| 66 |
-
bgcolor: 'var(--surface)',
|
| 67 |
-
borderRadius: 1.5,
|
| 68 |
-
borderTopLeftRadius: 4,
|
| 69 |
-
px: { xs: 1.5, md: 2.5 },
|
| 70 |
-
py: 1.5,
|
| 71 |
-
border: '1px solid var(--border)',
|
| 72 |
-
}}
|
| 73 |
-
>
|
| 74 |
-
<MarkdownContent content={WELCOME_MD} />
|
| 75 |
-
</Box>
|
| 76 |
-
</Box>
|
| 77 |
-
</Stack>
|
| 78 |
);
|
| 79 |
}
|
| 80 |
|
|
@@ -91,8 +71,6 @@ export default function MessageList({ messages, isProcessing }: MessageListProps
|
|
| 91 |
}, []);
|
| 92 |
|
| 93 |
// ββ Track user scroll intent ββββββββββββββββββββββββββββββββββββ
|
| 94 |
-
// When user scrolls up (>80px from bottom), disable auto-scroll.
|
| 95 |
-
// When they scroll back to bottom, re-enable it.
|
| 96 |
useEffect(() => {
|
| 97 |
const el = scrollContainerRef.current;
|
| 98 |
if (!el) return;
|
|
@@ -112,8 +90,6 @@ export default function MessageList({ messages, isProcessing }: MessageListProps
|
|
| 112 |
}, [messages, isProcessing, scrollToBottom]);
|
| 113 |
|
| 114 |
// ββ Auto-scroll on DOM mutations (streaming content growth) βββββ
|
| 115 |
-
// This catches token-by-token updates that don't change the messages
|
| 116 |
-
// array reference (appendToMessage mutates in place).
|
| 117 |
useEffect(() => {
|
| 118 |
const el = scrollContainerRef.current;
|
| 119 |
if (!el) return;
|
|
@@ -145,7 +121,6 @@ export default function MessageList({ messages, isProcessing }: MessageListProps
|
|
| 145 |
if (!activeSessionId) return;
|
| 146 |
try {
|
| 147 |
await apiFetch(`/api/undo/${activeSessionId}`, { method: 'POST' });
|
| 148 |
-
// Optimistic removal β backend will also confirm via undo_complete WS event
|
| 149 |
removeLastTurn(activeSessionId);
|
| 150 |
} catch (e) {
|
| 151 |
logger.error('Undo failed:', e);
|
|
@@ -160,6 +135,8 @@ export default function MessageList({ messages, isProcessing }: MessageListProps
|
|
| 160 |
overflow: 'auto',
|
| 161 |
px: { xs: 0.5, sm: 1, md: 2 },
|
| 162 |
py: { xs: 2, md: 3 },
|
|
|
|
|
|
|
| 163 |
}}
|
| 164 |
>
|
| 165 |
<Stack
|
|
@@ -168,12 +145,12 @@ export default function MessageList({ messages, isProcessing }: MessageListProps
|
|
| 168 |
maxWidth: 880,
|
| 169 |
mx: 'auto',
|
| 170 |
width: '100%',
|
|
|
|
| 171 |
}}
|
| 172 |
>
|
| 173 |
-
{
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
{messages.length > 0 && (
|
| 177 |
messages.map((msg) => (
|
| 178 |
<MessageBubble
|
| 179 |
key={msg.id}
|
|
|
|
| 1 |
import { useEffect, useRef, useMemo, useCallback } from 'react';
|
| 2 |
+
import { Box, Stack, Typography } from '@mui/material';
|
|
|
|
| 3 |
import MessageBubble from './MessageBubble';
|
| 4 |
import ThinkingIndicator from './ThinkingIndicator';
|
|
|
|
| 5 |
import { useAgentStore } from '@/store/agentStore';
|
| 6 |
import { useSessionStore } from '@/store/sessionStore';
|
| 7 |
import { apiFetch } from '@/utils/api';
|
|
|
|
| 13 |
isProcessing: boolean;
|
| 14 |
}
|
| 15 |
|
| 16 |
+
function getGreeting(): string {
|
| 17 |
+
const h = new Date().getHours();
|
| 18 |
+
if (h < 12) return 'Morning';
|
| 19 |
+
if (h < 17) return 'Afternoon';
|
| 20 |
+
return 'Evening';
|
| 21 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
+
/** Minimal greeting shown when the conversation is empty. */
|
| 24 |
+
function WelcomeGreeting() {
|
| 25 |
+
const { user } = useAgentStore();
|
| 26 |
+
const firstName = user?.name?.split(' ')[0] || user?.username;
|
| 27 |
+
const greeting = firstName ? `${getGreeting()}, ${firstName}` : getGreeting();
|
| 28 |
|
|
|
|
|
|
|
| 29 |
return (
|
| 30 |
+
<Box
|
| 31 |
+
sx={{
|
| 32 |
+
flex: 1,
|
| 33 |
+
display: 'flex',
|
| 34 |
+
flexDirection: 'column',
|
| 35 |
+
alignItems: 'center',
|
| 36 |
+
justifyContent: 'center',
|
| 37 |
+
py: 8,
|
| 38 |
+
gap: 1.5,
|
| 39 |
+
}}
|
| 40 |
+
>
|
| 41 |
+
<Typography
|
| 42 |
sx={{
|
| 43 |
+
fontFamily: 'monospace',
|
| 44 |
+
fontSize: '1.1rem',
|
| 45 |
+
color: 'var(--text)',
|
| 46 |
+
fontWeight: 600,
|
|
|
|
| 47 |
}}
|
| 48 |
>
|
| 49 |
+
{greeting}
|
| 50 |
+
</Typography>
|
| 51 |
+
<Typography
|
| 52 |
+
color="text.secondary"
|
| 53 |
+
sx={{ fontFamily: 'monospace', fontSize: '0.8rem' }}
|
| 54 |
+
>
|
| 55 |
+
Let's build something impressive?
|
| 56 |
+
</Typography>
|
| 57 |
+
</Box>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
);
|
| 59 |
}
|
| 60 |
|
|
|
|
| 71 |
}, []);
|
| 72 |
|
| 73 |
// ββ Track user scroll intent ββββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
| 74 |
useEffect(() => {
|
| 75 |
const el = scrollContainerRef.current;
|
| 76 |
if (!el) return;
|
|
|
|
| 90 |
}, [messages, isProcessing, scrollToBottom]);
|
| 91 |
|
| 92 |
// ββ Auto-scroll on DOM mutations (streaming content growth) βββββ
|
|
|
|
|
|
|
| 93 |
useEffect(() => {
|
| 94 |
const el = scrollContainerRef.current;
|
| 95 |
if (!el) return;
|
|
|
|
| 121 |
if (!activeSessionId) return;
|
| 122 |
try {
|
| 123 |
await apiFetch(`/api/undo/${activeSessionId}`, { method: 'POST' });
|
|
|
|
| 124 |
removeLastTurn(activeSessionId);
|
| 125 |
} catch (e) {
|
| 126 |
logger.error('Undo failed:', e);
|
|
|
|
| 135 |
overflow: 'auto',
|
| 136 |
px: { xs: 0.5, sm: 1, md: 2 },
|
| 137 |
py: { xs: 2, md: 3 },
|
| 138 |
+
display: 'flex',
|
| 139 |
+
flexDirection: 'column',
|
| 140 |
}}
|
| 141 |
>
|
| 142 |
<Stack
|
|
|
|
| 145 |
maxWidth: 880,
|
| 146 |
mx: 'auto',
|
| 147 |
width: '100%',
|
| 148 |
+
flex: messages.length === 0 && !isProcessing ? 1 : undefined,
|
| 149 |
}}
|
| 150 |
>
|
| 151 |
+
{messages.length === 0 && !isProcessing ? (
|
| 152 |
+
<WelcomeGreeting />
|
| 153 |
+
) : (
|
|
|
|
| 154 |
messages.map((msg) => (
|
| 155 |
<MessageBubble
|
| 156 |
key={msg.id}
|