fix(chat): add shimmer to waiting status
This commit is contained in:
@@ -560,17 +560,21 @@ describe('ChatTimeline', () => {
|
|||||||
messageStartIndex: 0, ...callbacks, retryContent: '', totalMessageCount: 1
|
messageStartIndex: 0, ...callbacks, retryContent: '', totalMessageCount: 1
|
||||||
}
|
}
|
||||||
const message: Message = {
|
const message: Message = {
|
||||||
id: 'status-message', role: 'assistant', content: '已有内容',
|
id: 'status-message', role: 'assistant', content: '',
|
||||||
createdAt: Date.now(), state: 'streaming'
|
createdAt: Date.now(), state: 'streaming'
|
||||||
}
|
}
|
||||||
const { rerender } = render(<ChatTimeline {...props} messages={[message]} />)
|
const { rerender } = render(<ChatTimeline {...props} messages={[message]} />)
|
||||||
expect(screen.getByRole('status', { name: '等待后续进展' })).toBeVisible()
|
expect(screen.getByRole('status', { name: '等待后续进展' })).toBeVisible()
|
||||||
|
expect(screen.getByRole('status', { name: '等待后续进展' })).toHaveClass('message__status--active')
|
||||||
expect(document.querySelector('.message__status-dot--active')).toBeNull()
|
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' }
|
const tool = { callId: 'read-1', name: 'read', summary: 'Read file' }
|
||||||
rerender(<ChatTimeline {...props} messages={[{
|
rerender(<ChatTimeline {...props} messages={[{
|
||||||
...message, tools: [{ ...tool, state: 'pending' }]
|
...message, tools: [{ ...tool, state: 'pending' }]
|
||||||
}]} />)
|
}]} />)
|
||||||
expect(screen.getByRole('status', { name: '工具待执行:read' })).toBeVisible()
|
expect(screen.getByRole('status', { name: '工具待执行:read' })).toBeVisible()
|
||||||
|
expect(screen.getByRole('status', { name: '工具待执行:read' })).toHaveClass('message__status--active')
|
||||||
rerender(<ChatTimeline {...props} messages={[{
|
rerender(<ChatTimeline {...props} messages={[{
|
||||||
...message, tools: [{ ...tool, state: 'running' }]
|
...message, tools: [{ ...tool, state: 'running' }]
|
||||||
}]} />)
|
}]} />)
|
||||||
|
|||||||
@@ -754,6 +754,10 @@ function ChatMessageRowView({
|
|||||||
? t('chat.status.waitingForSubagent')
|
? t('chat.status.waitingForSubagent')
|
||||||
: t('chat.status.waitingForProgress'))
|
: t('chat.status.waitingForProgress'))
|
||||||
: message.status
|
: 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'
|
const roleCaption = message.role !== 'assistant'
|
||||||
? undefined
|
? undefined
|
||||||
@@ -1232,9 +1236,11 @@ function ChatMessageRowView({
|
|||||||
: 'polite'
|
: 'polite'
|
||||||
}
|
}
|
||||||
className={
|
className={
|
||||||
failed
|
[
|
||||||
? 'message__status message__status--error'
|
'message__status',
|
||||||
: 'message__status'
|
failed && 'message__status--error',
|
||||||
|
isWaitingForResponse && 'message__status--active'
|
||||||
|
].filter(Boolean).join(' ')
|
||||||
}
|
}
|
||||||
role={
|
role={
|
||||||
message.state !== 'error' && (message.knowledgeRetrieval || compressionMarkers.length > 0)
|
message.state !== 'error' && (message.knowledgeRetrieval || compressionMarkers.length > 0)
|
||||||
@@ -1248,7 +1254,7 @@ function ChatMessageRowView({
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="message__status-dot"
|
className="message__status-dot"
|
||||||
/>
|
/>
|
||||||
{statusText}
|
<span className="message__status-label">{statusText}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{canRetry && Boolean(retryContent?.trim()) && (
|
{canRetry && Boolean(retryContent?.trim()) && (
|
||||||
|
|||||||
@@ -7343,6 +7343,31 @@ button > svg {
|
|||||||
color: var(--danger);
|
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 {
|
.context-compression-event {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
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 {
|
@keyframes composer-recording-pulse {
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
|
|||||||
Reference in New Issue
Block a user