feat(design): integrate conversation-first creation flow
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
brother7 committed 2026-09-04 07:39:58 +08:00
1 parent 14efa99f0c
commit 945b40de11
14 files changed
+374 -234

No files matched your search

+47 -55
View File
@@ -1,6 +1,7 @@
import {
Ban,
Check,
ChevronDown,
Film,
ImageIcon,
Lightbulb,
@@ -25,7 +26,6 @@ import {
projectYouthCreationCard,
type YouthCreationCardModel,
} from './youth-form-projection';
import { CreativeChoiceGrid } from './CreativeChoiceGrid';
export type YouthCreationCardProps = {
workspace: DesignWorkspace;
@@ -245,20 +245,20 @@ function ActionFooter({
case 'empty':
return (
<div className="flex flex-col gap-3 border-t border-border/60 pt-4 sm:flex-row sm:items-center sm:justify-between">
<p className="text-xs leading-5 text-muted-foreground">不用想得很完整,先说一句就好。</p>
<p className="text-xs leading-5 text-muted-foreground">先在对话里说说脑中的画面,不用一次想完整。</p>
<Button type="button" className="min-h-11 rounded-xl" onClick={onFocusComposer}>
<MessageCircle className="mr-2 h-4 w-4" aria-hidden="true" />
先说一个想法
开始描述
</Button>
</div>
);
case 'exploring':
return (
<div className="flex flex-col gap-3 border-t border-border/60 pt-4 sm:flex-row sm:items-center sm:justify-between">
<p className="text-xs leading-5 text-muted-foreground">你可以继续补充,也可以让 AI 帮你整理。</p>
<p className="text-xs leading-5 text-muted-foreground">继续描述你想看到的画面,AI 会接着问一个关键问题。</p>
<Button type="button" className="min-h-11 rounded-xl" onClick={onFocusComposer}>
<MessageCircle className="mr-2 h-4 w-4" aria-hidden="true" />
继续聊聊
回到对话
</Button>
</div>
);
@@ -336,37 +336,24 @@ export function YouthCreationCard({
onOpenFineTune,
onFocusComposer,
}: YouthCreationCardProps) {
const resolveDecisionPrompt = useImageWorkspaceStore((state) => state.resolveDecisionPrompt);
const requestQuote = useImageWorkspaceStore((state) => state.requestQuote);
const pendingOperations = useImageWorkspaceStore((state) => state.pendingOperations);
const model = projectYouthCreationCard(workspace, { quoteBlockers });
const submittingInput = Object.values(pendingOperations).some(
(operation) => operation.status === 'submitting' && operation.command.kind === 'apply_input',
);
const requesting = Object.values(pendingOperations).some(
(operation) => operation.status === 'submitting' && operation.command.kind === 'request_quote',
);
const choose = (optionId: string) => {
if (!model.activeChoice || submittingInput) return;
void resolveDecisionPrompt(model.activeChoice.id, 'select', optionId).catch(() => {
toast.error('这个选择没有保存,请再试一次');
});
};
const rejectChoice = () => {
if (!model.activeChoice || submittingInput) return;
void resolveDecisionPrompt(model.activeChoice.id, 'reject').catch(() => {
toast.error('这个选择没有保存,请再试一次');
});
};
const request = () => {
if (!generationAvailable || requesting || !model.canRequestQuote) return;
void requestQuote().catch(() => {
toast.error('制作方案还没准备好,请稍后再试');
});
};
const hasMoreDetails = model.mustInclude.length > 0
|| model.mustPreserve.length > 0
|| model.mustAvoid.length > 0
|| model.references.length > 0
|| model.video !== null;
return (
<section
@@ -379,18 +366,22 @@ export function YouthCreationCard({
<div className="flex items-center gap-2">
<Sparkles className="h-4 w-4 shrink-0 text-brand" aria-hidden="true" />
<h2 id="youth-creation-card-title" className="text-base font-semibold text-foreground">
我的创作
AI 听懂的想法
</h2>
</div>
<p className="mt-1 text-xs leading-5 text-muted-foreground">
会跟着聊天自动更新,不需要逐项填写。
</p>
</div>
<Button
type="button"
variant="ghost"
aria-label="手动调整(可选)"
className="min-h-11 shrink-0 rounded-xl px-2.5 text-xs"
onClick={onOpenFineTune}
>
<SlidersHorizontal className="mr-1.5 h-3.5 w-3.5" aria-hidden="true" />
精细调整
手动调整
</Button>
</header>
@@ -401,7 +392,7 @@ export function YouthCreationCard({
<section>
<div className="flex items-center gap-2 text-xs font-semibold text-foreground">
<Lightbulb className="h-3.5 w-3.5 text-brand" aria-hidden="true" />
<h3>想做什么</h3>
<h3>目前听懂的是</h3>
</div>
<p className="mt-2 rounded-xl bg-surface-subtle/75 px-3 py-2.5 text-sm leading-6 text-foreground">
{model.summary}
@@ -433,35 +424,36 @@ export function YouthCreationCard({
</div>
)}
<div className="space-y-4">
<TagSection
title="一定要有"
items={model.mustInclude}
icon={<ListChecks className="h-3.5 w-3.5" aria-hidden="true" />}
/>
<TagSection
title="想保留"
items={model.mustPreserve}
icon={<Check className="h-3.5 w-3.5" aria-hidden="true" />}
/>
<TagSection
title="不要出现"
items={model.mustAvoid}
icon={<Ban className="h-3.5 w-3.5" aria-hidden="true" />}
tone="avoid"
/>
</div>
<ReferenceSection model={model} />
<VideoSection model={model} />
{model.activeChoice && (
<CreativeChoiceGrid
choice={model.activeChoice}
disabled={submittingInput}
onSelect={choose}
onReject={rejectChoice}
/>
{hasMoreDetails && (
<details
data-testid="youth-creation-details"
className="group rounded-xl border border-border/65 bg-surface-subtle/35"
>
<summary className="flex min-h-11 cursor-pointer list-none items-center justify-between gap-3 rounded-xl px-3 text-xs font-medium text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/35 [&::-webkit-details-marker]:hidden">
<span>查看画面细节 <span className="font-normal text-muted-foreground">(可选)</span></span>
<ChevronDown className="h-4 w-4 text-muted-foreground transition-transform group-open:rotate-180" aria-hidden="true" />
</summary>
<div className="space-y-4 border-t border-border/60 px-3 py-4">
<TagSection
title="一定要有"
items={model.mustInclude}
icon={<ListChecks className="h-3.5 w-3.5" aria-hidden="true" />}
/>
<TagSection
title="想保留"
items={model.mustPreserve}
icon={<Check className="h-3.5 w-3.5" aria-hidden="true" />}
/>
<TagSection
title="不要出现"
items={model.mustAvoid}
icon={<Ban className="h-3.5 w-3.5" aria-hidden="true" />}
tone="avoid"
/>
<ReferenceSection model={model} />
<VideoSection model={model} />
</div>
</details>
)}
<ActionFooter