fix(design): remove redundant assistant progress banner
This commit is contained in:
@@ -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.
|
||||||
@@ -44,7 +44,6 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
|
|||||||
const chatDraft = useImageWorkspaceStore((state) => state.chatDraft);
|
const chatDraft = useImageWorkspaceStore((state) => state.chatDraft);
|
||||||
const setChatDraft = useImageWorkspaceStore((state) => state.setChatDraft);
|
const setChatDraft = useImageWorkspaceStore((state) => state.setChatDraft);
|
||||||
const sendChat = useImageWorkspaceStore((state) => state.sendChat);
|
const sendChat = useImageWorkspaceStore((state) => state.sendChat);
|
||||||
const assistantStreams = useImageWorkspaceStore((state) => state.assistantStreams);
|
|
||||||
const pendingOperations = useImageWorkspaceStore((state) => state.pendingOperations);
|
const pendingOperations = useImageWorkspaceStore((state) => state.pendingOperations);
|
||||||
const scrollAnchorRef = useRef<HTMLDivElement>(null);
|
const scrollAnchorRef = useRef<HTMLDivElement>(null);
|
||||||
const pendingChatMessages = Object.values(pendingOperations).flatMap((operation) => {
|
const pendingChatMessages = Object.values(pendingOperations).flatMap((operation) => {
|
||||||
@@ -60,13 +59,6 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
|
|||||||
status: operation.status,
|
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(
|
const submittingDesignInput = Object.values(pendingOperations).some(
|
||||||
(operation) => operation.status === 'submitting' && operation.command.kind === 'apply_input',
|
(operation) => operation.status === 'submitting' && operation.command.kind === 'apply_input',
|
||||||
);
|
);
|
||||||
@@ -81,7 +73,7 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
|
|||||||
if (typeof scrollAnchor?.scrollIntoView === 'function') {
|
if (typeof scrollAnchor?.scrollIntoView === 'function') {
|
||||||
scrollAnchor.scrollIntoView({ block: 'end' });
|
scrollAnchor.scrollIntoView({ block: 'end' });
|
||||||
}
|
}
|
||||||
}, [hasAssistantProgress, pendingChatKey, workspace.turns.length]);
|
}, [pendingChatKey, workspace.turns.length]);
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
if (!composerDraft.trim() || submittingDesignInput) return;
|
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">
|
<div className="min-h-0 flex-1 overflow-y-auto px-4 py-5">
|
||||||
{workspace.turns.length === 0
|
{workspace.turns.length === 0
|
||||||
&& pendingChatMessages.length === 0
|
&& pendingChatMessages.length === 0 && (
|
||||||
&& !hasAssistantProgress && (
|
|
||||||
<div className="mx-auto flex h-full max-w-sm flex-col justify-center py-8">
|
<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">
|
<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" />
|
<MessageSquareText className="h-5 w-5" />
|
||||||
@@ -169,29 +160,6 @@ export function DesignConversationPane({ workspace }: { workspace: DesignWorkspa
|
|||||||
</div>
|
</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>
|
||||||
<div ref={scrollAnchorRef} />
|
<div ref={scrollAnchorRef} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ describe('youth AI Design Canvas page', () => {
|
|||||||
expect(screen.queryByText(/规格版本|编译器|生成策略|字段决策/)).not.toBeInTheDocument();
|
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();
|
prepareWorkspace();
|
||||||
const unfinishedDraft = '收到,我们要收到,我们要制作一张社团活动海报。';
|
const unfinishedDraft = '收到,我们要收到,我们要制作一张社团活动海报。';
|
||||||
useImageWorkspaceStore.setState({
|
useImageWorkspaceStore.setState({
|
||||||
@@ -121,7 +121,11 @@ describe('youth AI Design Canvas page', () => {
|
|||||||
render(<ImageCanvas />);
|
render(<ImageCanvas />);
|
||||||
|
|
||||||
const conversation = screen.getByTestId('image-workspace-conversation');
|
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).getByText('我已经整理了用途、受众和初步概念,请确认右侧建议。')).toBeInTheDocument();
|
||||||
expect(within(conversation).queryByText(unfinishedDraft)).not.toBeInTheDocument();
|
expect(within(conversation).queryByText(unfinishedDraft)).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user