diff --git a/src/pages/ImageCanvas/youth-form-projection.ts b/src/pages/ImageCanvas/youth-form-projection.ts
index f9bcc19e..456a73e4 100644
--- a/src/pages/ImageCanvas/youth-form-projection.ts
+++ b/src/pages/ImageCanvas/youth-form-projection.ts
@@ -1,7 +1,6 @@
import type {
DesignAsset,
DesignCompilationIssue,
- DesignDecisionPrompt,
DesignMedium,
DesignSpecificationValues,
DesignWorkspace,
@@ -141,11 +140,6 @@ export type YouthVideoSummary = {
storyBeats: string[];
};
-export type YouthActiveChoice = Pick<
- DesignDecisionPrompt,
- 'id' | 'kind' | 'title' | 'body' | 'options' | 'targetPaths'
->;
-
export type YouthCreationCardModel = {
phase: YouthCreationPhase;
summary: string | null;
@@ -157,7 +151,6 @@ export type YouthCreationCardModel = {
mustAvoid: string[];
references: YouthReferenceSummary[];
video: YouthVideoSummary | null;
- activeChoice: YouthActiveChoice | null;
blockers: DesignCompilationIssue[];
blockerCount: number;
canRequestQuote: boolean;
@@ -344,8 +337,6 @@ export function projectYouthCreationCard(
): YouthCreationCardModel {
const values = workspace.form.specification.values;
const summary = projectSummary(values);
- const activeChoice = workspace.form.decisionPrompts
- .find((prompt) => prompt.options.length > 0) ?? null;
const blockers = (options.quoteBlockers ?? [])
.filter((issue) => issue.severity === 'blocker')
.map(projectYouthCompilationIssue);
@@ -361,11 +352,9 @@ export function projectYouthCreationCard(
} else if (offeredQuote) {
phase = 'confirming';
readinessMessage = '制作方案准备好了';
- } else if (blockers.length > 0 || activeChoice) {
+ } else if (blockers.length > 0) {
phase = 'exploring';
- readinessMessage = blockers.length > 0
- ? `还差 ${blockers.length} 个关键选择`
- : '还有一个关键选择';
+ readinessMessage = `还差 ${blockers.length} 个关键选择`;
} else if (values.intent.media && hasCoreIdea(values)) {
phase = 'ready';
readinessMessage = '想法已经清楚,可以检查制作方案';
@@ -388,7 +377,6 @@ export function projectYouthCreationCard(
mustAvoid: values.constraints.must_avoid.filter(Boolean),
references: projectReferences(workspace),
video: projectVideo(values),
- activeChoice,
blockers,
blockerCount: blockers.length,
canRequestQuote: !offeredQuote && (phase === 'ready' || phase === 'result'),
diff --git a/tests/e2e/image-workspace-v2.spec.ts b/tests/e2e/image-workspace-v2.spec.ts
index a69b7afd..4bd52770 100644
--- a/tests/e2e/image-workspace-v2.spec.ts
+++ b/tests/e2e/image-workspace-v2.spec.ts
@@ -407,7 +407,8 @@ test.describe('AI Design V2 workspace', () => {
))).toBe(true);
await expect(page.getByTestId('image-workspace-conversation')).toBeVisible();
await expect(page.getByTestId('image-workspace-creation-pane')).toBeVisible();
- await expect(page.getByRole('heading', { name: '我的创作' })).toBeVisible();
+ await expect(page.getByRole('heading', { name: '和 AI 聊聊你的想法' })).toBeVisible();
+ await expect(page.getByRole('heading', { name: 'AI 听懂的想法' })).toBeVisible();
await expect(page.getByText('把便携咖啡机呈现为城市通勤中的精密工具')).toBeVisible();
await expect(page.getByText('Living Form')).toHaveCount(0);
await expect(page.getByText('目标与用途')).toHaveCount(0);
@@ -438,7 +439,7 @@ test.describe('AI Design V2 workspace', () => {
await page.setViewportSize({ width: 375, height: 800 });
await expect(page.getByTestId('image-workspace-conversation')).toBeVisible();
await expect(page.getByTestId('image-workspace-creation-pane')).toBeHidden();
- await page.getByRole('button', { name: /^我的创作/ }).click();
+ await page.getByRole('button', { name: /^当前想法与作品/ }).click();
await expect(page.getByTestId('youth-creation-card')).toBeVisible();
await page.keyboard.press('Escape');
@@ -449,7 +450,7 @@ test.describe('AI Design V2 workspace', () => {
await page.setViewportSize(desktopViewport ?? { width: 1280, height: 800 });
await expect(page.getByTestId('image-workspace-creation-pane')).toBeVisible();
- await page.getByRole('button', { name: '精细调整' }).click();
+ await page.getByRole('button', { name: '手动调整(可选)' }).click();
await page.getByRole('button', { name: '电脑横屏' }).click();
await page.getByRole('button', { name: '完成调整' }).click();
await expect(page.getByTestId('youth-creation-card').getByText('电脑横屏')).toBeVisible();
diff --git a/tests/unit/image-canvas-page.test.tsx b/tests/unit/image-canvas-page.test.tsx
index bc4701fe..30de2204 100644
--- a/tests/unit/image-canvas-page.test.tsx
+++ b/tests/unit/image-canvas-page.test.tsx
@@ -61,7 +61,7 @@ function prepareWorkspace(overrides = {}) {
}
function openMobileCreationCard() {
- fireEvent.click(screen.getByRole('button', { name: /^我的创作/ }));
+ fireEvent.click(screen.getByRole('button', { name: /^当前想法与作品/ }));
}
describe('youth AI Design Canvas page', () => {
@@ -76,28 +76,62 @@ describe('youth AI Design Canvas page', () => {
useImageWorkspaceStore.getState().reset();
});
- it('starts with conversation and a concise Creation Card instead of a professional field matrix', () => {
+ it('starts with a guided conversation and keeps the structured summary secondary', () => {
prepareWorkspace();
render();
expect(screen.getByTestId('image-workspace-conversation')).toBeInTheDocument();
- expect(screen.getByRole('heading', { name: '一起做作品' })).toBeInTheDocument();
+ expect(screen.getByRole('heading', { name: '和 AI 聊聊你的想法' })).toBeInTheDocument();
+ expect(screen.getByText('像和同学讲故事一样说就好。AI 会先听懂,再一次问一个最重要的问题。')).toBeInTheDocument();
openMobileCreationCard();
const card = screen.getByTestId('youth-creation-card');
expect(card).toBeInTheDocument();
- expect(within(card).getByRole('heading', { name: '我的创作' })).toBeInTheDocument();
+ expect(within(card).getByRole('heading', { name: 'AI 听懂的想法' })).toBeInTheDocument();
expect(screen.getByText('做一张便携咖啡机的小红书主视觉')).toBeInTheDocument();
expect(screen.queryByText('Living Form')).not.toBeInTheDocument();
expect(screen.queryByText('目标与用途')).not.toBeInTheDocument();
expect(screen.queryByText(/规格版本|编译器|生成策略|字段决策/)).not.toBeInTheDocument();
});
+ it('invites a free description and does not turn a legacy decision prompt into a form', () => {
+ const workspace = designWorkspaceFixture({
+ form: designFormFixture({
+ decisionPrompts: [{
+ id: 'choice-1',
+ kind: 'question',
+ basedOnSpecificationRevisionId: 'specification-revision-3',
+ targetPaths: ['visual.art_direction'],
+ title: '画面想要什么感觉?',
+ body: '请从下面选一个。',
+ options: [
+ { id: 'cartoon', label: '有趣的卡通', description: null },
+ { id: 'cinema', label: '像电影一样', description: null },
+ ],
+ }],
+ }),
+ turns: [],
+ });
+ prepareWorkspace(workspace);
+ render();
+
+ const conversation = screen.getByTestId('image-workspace-conversation');
+ expect(within(conversation).getByRole('heading', { name: '先把脑中的画面说出来' })).toBeInTheDocument();
+ expect(within(conversation).getByText(/想画什么、发生在哪里/)).toBeInTheDocument();
+ expect(within(conversation).queryByText('画面想要什么感觉?')).not.toBeInTheDocument();
+ expect(within(conversation).queryByRole('button', { name: '有趣的卡通' })).not.toBeInTheDocument();
+
+ fireEvent.click(within(conversation).getByRole('button', { name: '我想做一张热闹的社团招新海报' }));
+ expect(within(conversation).getByRole('textbox', { name: '告诉 AI 你想创作什么' })).toHaveValue(
+ '我想做一张热闹的社团招新海报',
+ );
+ });
+
it('sends high-impact fine adjustments through the existing typed field operations', () => {
const { actions } = prepareWorkspace();
render();
openMobileCreationCard();
- fireEvent.click(screen.getByRole('button', { name: '精细调整' }));
+ fireEvent.click(screen.getByRole('button', { name: '手动调整(可选)' }));
fireEvent.click(screen.getByRole('button', { name: '电脑横屏' }));
expect(actions.applyFieldOperations).toHaveBeenCalledWith(
@@ -160,7 +194,9 @@ describe('youth AI Design Canvas page', () => {
fireEvent.click(screen.getByRole('button', { name: '发送消息' }));
await waitFor(() => {
- expect(toastErrorMock).toHaveBeenCalledWith('AI 没有整理好这次想法,请再试一次');
+ expect(toastErrorMock).toHaveBeenCalledWith(
+ 'AI 这次没听懂。你的内容还在输入框里,可以补充一个画面细节后再发送',
+ );
});
expect(toastErrorMock).not.toHaveBeenCalledWith('消息没有发出去,请再试一次');
});
diff --git a/tests/unit/youth-creation-card.test.tsx b/tests/unit/youth-creation-card.test.tsx
index 07296019..839f201f 100644
--- a/tests/unit/youth-creation-card.test.tsx
+++ b/tests/unit/youth-creation-card.test.tsx
@@ -100,7 +100,8 @@ describe('YouthCreationCard', () => {
it('shows a plain creation summary and offers a youth-facing ready action', () => {
const { requestQuote } = renderCard();
- expect(screen.getByRole('heading', { name: '我的创作' })).toBeInTheDocument();
+ expect(screen.getByRole('heading', { name: 'AI 听懂的想法' })).toBeInTheDocument();
+ expect(screen.getByText('会跟着聊天自动更新,不需要逐项填写。')).toBeInTheDocument();
expect(screen.getByText('把便携咖啡机呈现为城市通勤中的精密工具')).toBeInTheDocument();
expect(screen.getByText('图片')).toBeInTheDocument();
expect(screen.getByText('竖版海报')).toBeInTheDocument();
@@ -113,7 +114,7 @@ describe('YouthCreationCard', () => {
expect(requestQuote).toHaveBeenCalledOnce();
});
- it('shows only the first choice, keeps the server label, and resolves it through the store', () => {
+ it('does not surface legacy decision prompts as a form', () => {
const workspace = designWorkspaceFixture({
form: designFormFixture({
decisionPrompts: [{
@@ -141,18 +142,12 @@ describe('YouthCreationCard', () => {
});
const { resolveDecisionPrompt } = renderCard(workspace);
- expect(screen.getByTestId('youth-creation-choice')).toBeInTheDocument();
- expect(screen.getByText('画面想要什么感觉?')).toBeInTheDocument();
- expect(screen.getByRole('button', { name: /有趣的卡通/ })).toHaveClass('min-h-11');
- expect(screen.getByText('推荐')).toBeInTheDocument();
- expect(screen.getByRole('button', { name: /你帮我选/ })).toBeInTheDocument();
+ expect(screen.queryByTestId('youth-creation-choice')).not.toBeInTheDocument();
+ expect(screen.queryByText('画面想要什么感觉?')).not.toBeInTheDocument();
+ expect(screen.queryByRole('button', { name: /有趣的卡通/ })).not.toBeInTheDocument();
+ expect(screen.queryByRole('button', { name: /你帮我选/ })).not.toBeInTheDocument();
expect(screen.queryByText('这个问题不该显示')).not.toBeInTheDocument();
-
- fireEvent.click(screen.getByRole('button', { name: /你帮我选/ }));
- expect(resolveDecisionPrompt).toHaveBeenCalledWith('choice-1', 'select', 'delegate');
-
- fireEvent.click(screen.getByRole('button', { name: '先跳过这题' }));
- expect(resolveDecisionPrompt).toHaveBeenCalledWith('choice-1', 'reject');
+ expect(resolveDecisionPrompt).not.toHaveBeenCalled();
});
it('shows known must-have, must-avoid, reference, video, and plain blocker details only', () => {
@@ -213,6 +208,7 @@ describe('YouthCreationCard', () => {
renderCard(workspace, { quoteBlockers: [blocker] });
+ fireEvent.click(screen.getByText('查看画面细节'));
expect(screen.getByText('一定要有')).toBeInTheDocument();
expect(screen.getByText('远处的地球')).toBeInTheDocument();
expect(screen.getByText('想保留')).toBeInTheDocument();
@@ -258,10 +254,10 @@ describe('YouthCreationCard', () => {
);
expect(within(screen.getByTestId('youth-creation-readiness')).getByText('等你的第一个想法')).toBeInTheDocument();
- fireEvent.click(screen.getByRole('button', { name: '先说一个想法' }));
+ fireEvent.click(screen.getByRole('button', { name: '开始描述' }));
expect(onFocusComposer).toHaveBeenCalledOnce();
- fireEvent.click(screen.getByRole('button', { name: '精细调整' }));
+ fireEvent.click(screen.getByRole('button', { name: '手动调整(可选)' }));
expect(onOpenFineTune).toHaveBeenCalledOnce();
});
diff --git a/tests/unit/youth-form-projection.test.ts b/tests/unit/youth-form-projection.test.ts
index 1b3d6338..c1b0e182 100644
--- a/tests/unit/youth-form-projection.test.ts
+++ b/tests/unit/youth-form-projection.test.ts
@@ -139,7 +139,7 @@ describe('youth Design projection', () => {
expect(model.readinessMessage).not.toContain('可以开始制作');
});
- it('shows only the first consequential choice and concrete quote blockers', () => {
+ it('projects concrete quote blockers without exposing server decision prompts', () => {
const workspace = designWorkspaceFixture({
form: designFormFixture({
decisionPrompts: [{
@@ -174,8 +174,6 @@ describe('youth Design projection', () => {
const model = projectYouthCreationCard(workspace, { quoteBlockers: blockers });
expect(model.phase).toBe('exploring');
- expect(model.activeChoice?.id).toBe('question-1');
- expect(model.activeChoice?.options).toHaveLength(3);
expect(model.blockers).toEqual([{
...blockers[0],
message: '先选择作品形状。',