diff --git a/src/pages/ImageCanvas/YouthCreationCard.tsx b/src/pages/ImageCanvas/YouthCreationCard.tsx index 6610758..e87e912 100644 --- a/src/pages/ImageCanvas/YouthCreationCard.tsx +++ b/src/pages/ImageCanvas/YouthCreationCard.tsx @@ -70,7 +70,12 @@ function canonicalCreatorPrompt(workspace: DesignWorkspace): string { const values = workspace.form.specification.values; return values.content.concept?.trim() || values.content.narrative?.trim() - || ''; + || [ + ...values.content.subjects.map((subject) => subject.description.trim()), + values.content.action?.trim(), + values.content.environment?.trim(), + values.content.emotional_tone?.trim(), + ].filter((value): value is string => Boolean(value)).join(','); } function Stepper({ @@ -141,7 +146,7 @@ export function YouthCreationCard({ const [replaceIndex, setReplaceIndex] = useState(null); const [uploading, setUploading] = useState(false); const [medium, setMedium] = useState(values.intent.media ?? 'image'); - const [aspectRatio, setAspectRatio] = useState(values.output.aspect_ratio ?? '1:1'); + const [aspectRatio, setAspectRatio] = useState(values.output.aspect_ratio ?? ''); const [duration, setDuration] = useState(values.video.total_duration_seconds ?? 6); const [variantCount, setVariantCount] = useState(values.output.variant_count ?? 1); const promptRef = useRef(null); @@ -155,7 +160,7 @@ export function YouthCreationCard({ useEffect(() => { setMedium(values.intent.media ?? 'image'); - setAspectRatio(values.output.aspect_ratio ?? '1:1'); + setAspectRatio(values.output.aspect_ratio ?? ''); setDuration(values.video.total_duration_seconds ?? 6); setVariantCount(values.output.variant_count ?? 1); }, [ @@ -602,6 +607,7 @@ export function YouthCreationCard({ ], () => setAspectRatio(next)); }} > + {ASPECT_RATIOS.map((item) => )} diff --git a/src/pages/ImageCanvas/plan-lifecycle.ts b/src/pages/ImageCanvas/plan-lifecycle.ts index 634f3b8..33adb63 100644 --- a/src/pages/ImageCanvas/plan-lifecycle.ts +++ b/src/pages/ImageCanvas/plan-lifecycle.ts @@ -1,13 +1,22 @@ import type { DesignWorkspace } from '../../../shared/image-workspace'; -function hasCreatorPrompt(workspace: DesignWorkspace): boolean { +function hasCreatorMeaning(workspace: DesignWorkspace): boolean { const values = workspace.form.specification.values; - return Boolean(values.content.concept?.trim() || values.content.narrative?.trim()); + return Boolean( + values.content.concept?.trim() + || values.content.narrative?.trim() + || values.content.action?.trim() + || values.content.environment?.trim() + || values.content.emotional_tone?.trim() + || values.content.subjects.some((subject) => subject.description.trim()) + || values.content.exact_content.some((item) => item.text.trim()) + || values.references.length > 0, + ); } export function hasActiveCreationPlan(workspace: DesignWorkspace): boolean { if (workspace.form.activeQuotes.some((quote) => quote.status === 'offered')) return true; - if (!hasCreatorPrompt(workspace) && workspace.form.specification.values.references.length === 0) return false; + if (!hasCreatorMeaning(workspace)) return false; const latestTaskRevision = workspace.tasks.reduce( (latest, task) => Math.max(latest, task.specificationRevision), -1, diff --git a/tests/unit/image-canvas-page.test.tsx b/tests/unit/image-canvas-page.test.tsx index a313346..67d6497 100644 --- a/tests/unit/image-canvas-page.test.tsx +++ b/tests/unit/image-canvas-page.test.tsx @@ -192,6 +192,40 @@ describe('AI Design Canvas page', () => { expect(screen.queryByTestId('image-workspace-works-rail')).not.toBeInTheDocument(); }); + it('shows an incomplete meaningful plan and does not invent a selected aspect ratio', async () => { + const form = designFormFixture(); + form.specification.values.content.concept = null; + form.specification.values.content.action = '一只狗和一只猫在草地上互相扑咬玩闹'; + form.specification.values.output.aspect_ratio = null; + form.specification.values.output.orientation = 'portrait'; + const { actions } = prepareWorkspace({ form }); + useImageWorkspaceStore.setState({ + quoteBlockers: [{ + code: 'aspect_ratio_required', + message: 'Choose an aspect ratio before requesting a production quote.', + severity: 'blocker', + path: 'output.aspect_ratio', + }], + }); + + render(); + + expect(screen.getByRole('heading', { name: '制作方案' })).toBeInTheDocument(); + expect(screen.getByText('先选择作品形状。')).toBeInTheDocument(); + expect(screen.getByRole('combobox', { name: '画幅' })).toHaveValue(''); + expect(screen.getByRole('button', { name: '准备制作方案' })).toBeDisabled(); + + fireEvent.change(screen.getByRole('combobox', { name: '画幅' }), { + target: { value: '9:16' }, + }); + await waitFor(() => { + expect(actions.applyFieldOperations).toHaveBeenCalledWith([ + { kind: 'set', path: 'output.aspect_ratio', value: '9:16' }, + { kind: 'set', path: 'output.orientation', value: 'portrait' }, + ]); + }); + }); + it('opens project navigation as a left sheet on compact layouts', () => { prepareWorkspace(); render();