diff --git a/apps/web/src/services/ai/__tests__/orchestrator-prompt-optimizer.test.ts b/apps/web/src/services/ai/__tests__/orchestrator-prompt-optimizer.test.ts index 4e6cdcae8..586ef1dd9 100644 --- a/apps/web/src/services/ai/__tests__/orchestrator-prompt-optimizer.test.ts +++ b/apps/web/src/services/ai/__tests__/orchestrator-prompt-optimizer.test.ts @@ -208,6 +208,27 @@ describe('buildFallbackPlanFromPrompt', () => { expect(plan.rootFrame.width).not.toBe(400); }); + // Codex review #6: keywords disqualifying a component fallback MUST + // also route the prompt to the right non-component preset. Otherwise + // "design a workspace with side panel" skipped component AND skipped + // dashboard (the regex only matched dashboard|admin|管理|后台|控制台) + // and fell through to landing-page (1200×0), which is the wrong shape + // for a workspace UI. + it.each([ + ['design a workspace with side panel and metrics'], + ['design a console with charts and live metrics'], + ['设计一个工作台 with charts'], + ['设计一个工作区 with 卡片'], + ])('classifies "%s" as desktop-screen (1200×800), not landing-page', (prompt) => { + const plan = buildFallbackPlanFromPrompt(prompt); + expect(plan.rootFrame.width).toBe(1200); + expect(plan.rootFrame.height).toBe(800); // desktop-screen preset has rootHeight=800 + // 3 default sections (Header / Main Content / Actions) — not the + // 4 sections of landing-page (Header / Main / Supporting / Footer). + expect(plan.subtasks).toHaveLength(3); + expect(plan.subtasks.map((st) => st.label)).toEqual(['Header', 'Main Content', 'Actions']); + }); + it('uses design.md background and style-guide name when designMd is present', () => { const designMd: DesignMdSpec = { raw: '# Test', diff --git a/apps/web/src/services/ai/design-type-presets.ts b/apps/web/src/services/ai/design-type-presets.ts index da6ee278f..40afba8ba 100644 --- a/apps/web/src/services/ai/design-type-presets.ts +++ b/apps/web/src/services/ai/design-type-presets.ts @@ -45,7 +45,7 @@ const COMPONENT_TRIGGER_CJK_RE = /(卡片|徽章|标签|按钮|开关|对话框| // the prompt also names a tile/panel/chart/metric inside it // ("admin dashboard with metric tiles" must not become a Type 0 tile). const COMPONENT_DISQUALIFIER_RE = - /\b(screen|page|app|home|onboarding|flow|mobile|phone|ios|android|dashboard|admin|workspace|console)\b|网页|页面|屏幕|手机|移动端|管理|后台|控制台/i; + /\b(screen|page|app|home|onboarding|flow|mobile|phone|ios|android|dashboard|admin|workspace|console)\b|网页|页面|屏幕|手机|移动端|管理|后台|控制台|工作台|工作区/i; /** * Minimal fallback design type detection. @@ -87,8 +87,13 @@ export function detectDesignType(prompt: string): DesignTypePreset { }; } - // Fixed-height desktop screens - if (/dashboard|admin|管理|后台|控制台/i.test(prompt)) { + // Fixed-height desktop screens. Keep in sync with the workspace markers + // in COMPONENT_DISQUALIFIER_RE — every keyword that disqualifies a + // component fallback for "workspace context" MUST also be detected here, + // otherwise the prompt skips component AND skips dashboard and falls + // through to landing-page (1200×0) which is wrong for "design a workspace + // with side panel" / "design a console with metrics" (Codex review #6). + if (/dashboard|admin|workspace|console|管理|后台|控制台|工作台|工作区/i.test(prompt)) { return { type: 'desktop-screen', width: 1200,