fix(design): remove redundant assistant progress banner

This commit is contained in:
2026-09-04 10:09:05 +08:00
parent 403236115b
commit 3bda17aa3c
3 changed files with 94 additions and 36 deletions

View File

@@ -0,0 +1,86 @@
# Task: Remove redundant AI Design progress banner
## Identity
- Task ID: 20260904-remove-design-progress-4e8a1c73
- Mode: Feature
- Branch: codex/20260904-remove-design-progress-4e8a1c73-remove-design-progress
- Worktree: D:\Datas\OthersProjects\.codex-worktrees\makelore\20260904-remove-design-progress-4e8a1c73
- Base commit: 403236115bf81e7617856cd8219b1be23f6abbb8
- Owner: codex-root
- Status: Ready for Integration
## Scope
- Remove the visible AI Design assistant-progress banner from the main conversation
pane. The right-side “AI 听懂的想法” projection remains the sole surface for the
currently organized design understanding.
- Preserve immediate provisional user messages, their `发送中` / `正在确认` feedback,
canonical assistant replies, and the existing internal stream reconciliation.
- Update focused Renderer regression coverage only. Do not change the Works Square
server, Electron Main/API contracts, Current Specification authority, generation,
navigation, packaging, publication, or unrelated project records.
## Intent And Constraints
- The user explicitly identified the progress banner as duplicated information and
requested that it be removed. It must not be replaced by another central loading
surface.
- Raw assistant deltas remain non-canonical and must stay out of the conversation.
Removing their presentation must not remove their Store state or change operation
identity and failure recovery.
- Keep the pending user bubble as the honest acknowledgement that a send was accepted
locally; only the redundant assistant-side banner is removed.
- Concurrent Task Gate and Planning Gate passed in the managed isolated worktree from
exact client `main` base `403236115bf81e7617856cd8219b1be23f6abbb8`.
- The root integration task overlaps the same AI Design area but owns only root/canonical
reconciliation; this feature task owns the component and focused test in isolation.
- ADR-007 remains unchanged: the server Current Specification is semantic authority,
while the right-side youth summary is its secondary client projection.
## Outcome
- `DesignConversationPane` no longer subscribes to `assistantStreams` for visible
presentation and no longer renders the “AI 正在整理你的想法” / result-confirmation
banner in the conversation timeline.
- Raw assistant deltas remain Store-owned transport state and still never appear as
committed chat content. The final canonical assistant turn remains unchanged.
- Provisional user messages and their `发送中` / `正在确认` labels remain visible, so
removing the duplicated assistant banner does not remove send acknowledgement.
- The right-side “AI 听懂的想法” card and all Current Specification behavior are
unchanged.
## Verification
- Focused Renderer test: `pnpm exec vitest run tests/unit/image-canvas-page.test.tsx`
passed `1 file / 10 tests`.
- TypeScript: `pnpm exec tsc --noEmit` passed.
- Scoped ESLint for the changed component and test passed.
- The isolated worktree dependencies were restored from the existing lockfile with
offline, frozen resolution and lifecycle scripts disabled; no dependency version or
lockfile changed.
## Follow-ups
- Integrate this source commit after the existing root AI Design integration task has
safely completed or released its ownership. Do not overwrite that task's dirty
canonical-document reconciliation or its three preserved foreign records.
## Promotion Candidates
- Target: `.project-docs/30-worklog/current-state.md`, AI Design presentation wording
in `.project-docs/20-architecture/system-overview.md`, and the corresponding rule in
`.project-docs/40-domain/business-rules.md`.
- Proposal: record that raw assistant deltas stay internal and invisible in the main
conversation; the right-side Current Specification projection is the single surface
for the AI's organized understanding. The main timeline shows provisional/canonical
user messages and canonical assistant turns, without a separate assistant-progress
banner.
- Evidence: the user explicitly identified the banner as duplicated information and
requested its removal; focused Renderer coverage confirms the banner is absent while
send acknowledgement remains.
- Future impact: later conversation changes should not reintroduce a second summary or
progress card into the timeline merely because assistant delta transport state exists.
- Semantic conflict: this narrows the prior task's optional visible progress projection;
it does not change ADR-007 or server authority. Human confirmation is already present
in this task request.

View File

@@ -44,7 +44,6 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
const chatDraft = useImageWorkspaceStore((state) => state.chatDraft);
const setChatDraft = useImageWorkspaceStore((state) => state.setChatDraft);
const sendChat = useImageWorkspaceStore((state) => state.sendChat);
const assistantStreams = useImageWorkspaceStore((state) => state.assistantStreams);
const pendingOperations = useImageWorkspaceStore((state) => state.pendingOperations);
const scrollAnchorRef = useRef<HTMLDivElement>(null);
const pendingChatMessages = Object.values(pendingOperations).flatMap((operation) => {
@@ -60,13 +59,6 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
status: operation.status,
}];
});
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 submittingDesignInput = Object.values(pendingOperations).some(
(operation) => operation.status === 'submitting' && operation.command.kind === 'apply_input',
);
@@ -81,7 +73,7 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
if (typeof scrollAnchor?.scrollIntoView === 'function') {
scrollAnchor.scrollIntoView({ block: 'end' });
}
}, [hasAssistantProgress, pendingChatKey, workspace.turns.length]);
}, [pendingChatKey, workspace.turns.length]);
const submit = () => {
if (!composerDraft.trim() || submittingDesignInput) return;
@@ -107,8 +99,7 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
<div className="min-h-0 flex-1 overflow-y-auto px-4 py-5">
{workspace.turns.length === 0
&& pendingChatMessages.length === 0
&& !hasAssistantProgress && (
&& pendingChatMessages.length === 0 && (
<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" />
@@ -169,29 +160,6 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
</div>
))}
{hasAssistantProgress && (
<div
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"
>
<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} />
</div>

View File

@@ -93,7 +93,7 @@ describe('youth AI Design Canvas page', () => {
expect(screen.queryByText(/规格版本|编译器|生成策略|字段决策/)).not.toBeInTheDocument();
});
it('presents unfinished AI output as progress instead of a chat reply', () => {
it('keeps unfinished AI output out of the conversation while send status stays visible', () => {
prepareWorkspace();
const unfinishedDraft = '收到,我们要收到,我们要制作一张社团活动海报。';
useImageWorkspaceStore.setState({
@@ -121,7 +121,11 @@ describe('youth AI Design Canvas page', () => {
render(<ImageCanvas />);
const conversation = screen.getByTestId('image-workspace-conversation');
expect(within(conversation).getByRole('status')).toHaveTextContent('AI 正在整理你的想法');
const pendingMessage = within(conversation).getByTestId('pending-design-chat-operation-chat-1');
expect(within(pendingMessage).getByText('做一张社团活动海报')).toBeInTheDocument();
expect(within(pendingMessage).getByText('发送中')).toBeInTheDocument();
expect(within(conversation).queryByTestId('design-assistant-progress')).not.toBeInTheDocument();
expect(within(conversation).queryByText('AI 正在整理你的想法')).not.toBeInTheDocument();
expect(within(conversation).getByText('我已经整理了用途、受众和初步概念,请确认右侧建议。')).toBeInTheDocument();
expect(within(conversation).queryByText(unfinishedDraft)).not.toBeInTheDocument();
});