fix(design): separate progress from chat replies

This commit is contained in:
2026-09-04 08:22:32 +08:00
parent 945b40de11
commit c6b0490d7f
5 changed files with 293 additions and 15 deletions

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef } from 'react';
import { useEffect, useRef } from 'react';
import { Loader2, MessageSquareText, Send, Sparkles } from 'lucide-react';
import { toast } from 'sonner';
import { Button } from '@/components/ui/button';
@@ -47,9 +47,12 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
const assistantStreams = useImageWorkspaceStore((state) => state.assistantStreams);
const pendingOperations = useImageWorkspaceStore((state) => state.pendingOperations);
const scrollAnchorRef = useRef<HTMLDivElement>(null);
const streams = useMemo(
() => Object.entries(assistantStreams).filter(([, text]) => text.trim()),
[assistantStreams],
const streamingOperationIds = Object.entries(assistantStreams)
.filter(([, text]) => text.trim())
.map(([operationId]) => operationId);
const hasAssistantProgress = streamingOperationIds.length > 0;
const hasUnknownAssistantProgress = streamingOperationIds.some(
(operationId) => pendingOperations[operationId]?.status === 'unknown',
);
const submittingChat = Object.values(pendingOperations).some(
(operation) => operation.status === 'submitting' && operation.command.kind === 'apply_input',
@@ -59,7 +62,7 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
if (typeof scrollAnchor?.scrollIntoView === 'function') {
scrollAnchor.scrollIntoView({ block: 'end' });
}
}, [streams, workspace.turns.length]);
}, [hasAssistantProgress, workspace.turns.length]);
const submit = () => {
if (!chatDraft.trim() || submittingChat) return;
@@ -84,7 +87,7 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
</header>
<div className="min-h-0 flex-1 overflow-y-auto px-4 py-5">
{workspace.turns.length === 0 && streams.length === 0 && (
{workspace.turns.length === 0 && !hasAssistantProgress && (
<div className="mx-auto flex h-full max-w-sm flex-col justify-center py-8">
<div className="mb-5 flex h-11 w-11 items-center justify-center rounded-2xl bg-brand/[0.09] text-brand">
<MessageSquareText className="h-5 w-5" />
@@ -125,15 +128,28 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
</div>
))}
{streams.map(([operationId, text]) => (
{hasAssistantProgress && (
<div
key={operationId}
className="mr-auto max-w-[92%] rounded-2xl rounded-bl-md border border-brand/20 bg-background px-3.5 py-3 text-sm leading-6 text-foreground shadow-sm"
role="status"
aria-live="polite"
data-testid="design-assistant-progress"
className="mr-auto flex max-w-[92%] items-start gap-3 rounded-xl bg-brand/[0.06] px-3.5 py-3 text-muted-foreground shadow-sm"
>
{text}
<span className="ml-1 inline-block h-3.5 w-1 animate-pulse rounded-full bg-brand align-middle" />
<span className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-background text-brand shadow-sm">
<Loader2 className="h-3.5 w-3.5 animate-spin" aria-hidden="true" />
</span>
<div className="min-w-0">
<p className="text-xs font-semibold text-foreground">
{hasUnknownAssistantProgress ? '正在确认刚才的整理结果' : 'AI 正在整理你的想法'}
</p>
<p className="mt-0.5 text-xs leading-5 text-muted-foreground">
{hasUnknownAssistantProgress
? '结果回来后会显示在对话里,不需要重复发送。'
: '这不是新的回复,完成后会显示下一步问题或建议。'}
</p>
</div>
</div>
))}
)}
</div>
<div ref={scrollAnchorRef} />