fix(chat): add shimmer to waiting status
This commit is contained in:
@@ -560,17 +560,21 @@ describe('ChatTimeline', () => {
|
||||
messageStartIndex: 0, ...callbacks, retryContent: '', totalMessageCount: 1
|
||||
}
|
||||
const message: Message = {
|
||||
id: 'status-message', role: 'assistant', content: '已有内容',
|
||||
id: 'status-message', role: 'assistant', content: '',
|
||||
createdAt: Date.now(), state: 'streaming'
|
||||
}
|
||||
const { rerender } = render(<ChatTimeline {...props} messages={[message]} />)
|
||||
expect(screen.getByRole('status', { name: '等待后续进展' })).toBeVisible()
|
||||
expect(screen.getByRole('status', { name: '等待后续进展' })).toHaveClass('message__status--active')
|
||||
expect(document.querySelector('.message__status-dot--active')).toBeNull()
|
||||
rerender(<ChatTimeline {...props} messages={[{ ...message, content: '已有内容' }]} />)
|
||||
expect(screen.getByRole('status', { name: '等待后续进展' })).not.toHaveClass('message__status--active')
|
||||
const tool = { callId: 'read-1', name: 'read', summary: 'Read file' }
|
||||
rerender(<ChatTimeline {...props} messages={[{
|
||||
...message, tools: [{ ...tool, state: 'pending' }]
|
||||
}]} />)
|
||||
expect(screen.getByRole('status', { name: '工具待执行:read' })).toBeVisible()
|
||||
expect(screen.getByRole('status', { name: '工具待执行:read' })).toHaveClass('message__status--active')
|
||||
rerender(<ChatTimeline {...props} messages={[{
|
||||
...message, tools: [{ ...tool, state: 'running' }]
|
||||
}]} />)
|
||||
|
||||
@@ -754,6 +754,10 @@ function ChatMessageRowView({
|
||||
? t('chat.status.waitingForSubagent')
|
||||
: t('chat.status.waitingForProgress'))
|
||||
: message.status
|
||||
const isWaitingForResponse = message.role === 'assistant' &&
|
||||
message.state === 'streaming' &&
|
||||
!message.content.trim() &&
|
||||
!message.blocks?.some(block => block.type === 'text' && block.content.trim())
|
||||
|
||||
const roleCaption = message.role !== 'assistant'
|
||||
? undefined
|
||||
@@ -1232,9 +1236,11 @@ function ChatMessageRowView({
|
||||
: 'polite'
|
||||
}
|
||||
className={
|
||||
failed
|
||||
? 'message__status message__status--error'
|
||||
: 'message__status'
|
||||
[
|
||||
'message__status',
|
||||
failed && 'message__status--error',
|
||||
isWaitingForResponse && 'message__status--active'
|
||||
].filter(Boolean).join(' ')
|
||||
}
|
||||
role={
|
||||
message.state !== 'error' && (message.knowledgeRetrieval || compressionMarkers.length > 0)
|
||||
@@ -1248,7 +1254,7 @@ function ChatMessageRowView({
|
||||
aria-hidden="true"
|
||||
className="message__status-dot"
|
||||
/>
|
||||
{statusText}
|
||||
<span className="message__status-label">{statusText}</span>
|
||||
</div>
|
||||
)}
|
||||
{canRetry && Boolean(retryContent?.trim()) && (
|
||||
|
||||
@@ -7343,6 +7343,31 @@ button > svg {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.message__status--active {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.message__status-label {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.message__status--active .message__status-label {
|
||||
background: linear-gradient(
|
||||
105deg,
|
||||
var(--text-muted) 0%,
|
||||
var(--text-muted) 38%,
|
||||
var(--text-primary) 50%,
|
||||
var(--text-muted) 62%,
|
||||
var(--text-muted) 100%
|
||||
);
|
||||
background-size: 320% 100%;
|
||||
background-position: 140% 0;
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
animation: message-status-shimmer 2.2s linear infinite;
|
||||
}
|
||||
|
||||
.context-compression-event {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
@@ -18209,6 +18234,18 @@ textarea.field-control {
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes message-status-shimmer {
|
||||
0%,
|
||||
12% {
|
||||
background-position: 140% 0;
|
||||
}
|
||||
|
||||
88%,
|
||||
100% {
|
||||
background-position: -40% 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes composer-recording-pulse {
|
||||
0%,
|
||||
100% {
|
||||
|
||||
Reference in New Issue
Block a user