232 lines
12 KiB
TypeScript
232 lines
12 KiB
TypeScript
import type { DetailGenerationMode } from "@/lib/detail-ui-protocol";
|
||
|
||
export const DETAIL_UI_DESIGN_SKILL_VERSION = "detail-ui-design-skill/v1";
|
||
|
||
export interface DetailUiDesignSkillSpec {
|
||
version: typeof DETAIL_UI_DESIGN_SKILL_VERSION;
|
||
name: string;
|
||
source: string;
|
||
language: "zh" | "en";
|
||
generationMode: DetailGenerationMode;
|
||
canvas: {
|
||
width: number;
|
||
height: number;
|
||
grid: string;
|
||
maxOverlayCoverage: string;
|
||
};
|
||
visualDirection: string[];
|
||
typography: string[];
|
||
layout: string[];
|
||
imageFusion: string[];
|
||
dslContract: string[];
|
||
antiPatterns: string[];
|
||
reviewChecklist: string[];
|
||
metadata: {
|
||
screenCount: number;
|
||
hasReferenceImages: boolean;
|
||
hasModelImage: boolean;
|
||
hasBundleImages: boolean;
|
||
productName?: string;
|
||
brandTone?: string;
|
||
};
|
||
}
|
||
export interface DetailUiDesignSkillSummary {
|
||
version: typeof DETAIL_UI_DESIGN_SKILL_VERSION;
|
||
name: string;
|
||
source: string;
|
||
visualDirection: string[];
|
||
reviewChecklist: string[];
|
||
}
|
||
|
||
function normalizeText(value: unknown) {
|
||
return String(value || "").replace(/\s+/g, " ").trim();
|
||
}
|
||
|
||
export function buildDetailUiDesignSkillSpec(params: {
|
||
language: "zh" | "en";
|
||
generationMode: DetailGenerationMode;
|
||
screenCount: number;
|
||
hasReferenceImages: boolean;
|
||
hasModelImage: boolean;
|
||
hasBundleImages: boolean;
|
||
productName?: string;
|
||
brandTone?: string;
|
||
}): DetailUiDesignSkillSpec {
|
||
const isZh = params.language === "zh";
|
||
|
||
return {
|
||
version: DETAIL_UI_DESIGN_SKILL_VERSION,
|
||
name: isZh ? "详情 UI 设计技能" : "Detail UI Design Skill",
|
||
source: "Google Stitch DESIGN.md pattern + Apple product-showcase restraint + Linear precision + Figma editable-layer semantics",
|
||
language: params.language,
|
||
generationMode: params.generationMode,
|
||
canvas: {
|
||
width: 790,
|
||
height: 1300,
|
||
grid: isZh ? "8px 基础栅格,左右安全边距 56px,主内容按 12 栅格对齐" : "8px base grid, 56px side safe margins, main content aligned to a 12-column rhythm",
|
||
maxOverlayCoverage: isZh ? "可编辑 UI 覆盖面积建议 18%-38%,最高不超过 45%" : "Editable UI overlay should cover 18%-38% of the canvas, never above 45%",
|
||
},
|
||
visualDirection: isZh
|
||
? [
|
||
"产品/素材主体优先,主体轮廓不能被大面积 UI 卡片遮挡。",
|
||
"一屏只建立一个主视觉焦点,文字层服务焦点,不把画面做成模板拼贴说明页。",
|
||
"整体气质克制、精密、可上线:少装饰,靠比例、留白、对比和真实素材质感建立高级感。",
|
||
"强调色只用于 CTA、重点徽章或关键对比,不能把整屏染成单一蓝紫渐变。",
|
||
]
|
||
: [
|
||
"Prioritize the product or reference asset; never bury the silhouette under large UI cards.",
|
||
"Create one dominant focal point per screen. Text supports the focal point instead of turning the frame into a collage.",
|
||
"Use restrained production-grade polish: proportion, whitespace, contrast and material realism over decoration.",
|
||
"Reserve accent color for CTA, proof badge, or key comparison only; avoid one-note blue/purple gradients.",
|
||
],
|
||
typography: isZh
|
||
? [
|
||
"主标题 52-64px、字重 650-750、行高 1.06-1.15,禁止负字距,最多 2 行。",
|
||
"副标题 26-34px、字重 550-650,必须是一句结果句,不写成长段说明。",
|
||
"正文 18-24px、字重 450-600、行高 1.34-1.5,单块最多 4 行。",
|
||
"同一套详情页只能使用一个字体家族和三个清晰层级:标题/副标题/正文。",
|
||
]
|
||
: [
|
||
"Hero title 52-64px, weight 650-750, line-height 1.06-1.15, no negative tracking, max 2 lines.",
|
||
"Subtitle 26-34px, weight 550-650, one result sentence only.",
|
||
"Body 18-24px, weight 450-600, line-height 1.34-1.5, max 4 lines per block.",
|
||
"Use one font family and exactly three readable levels: title, subtitle, body.",
|
||
],
|
||
layout: isZh
|
||
? [
|
||
"首屏必须是封面海报/KV:单主体、强焦点、清晰标题安全区,禁止多卡片堆叠。",
|
||
"卡片只用于承载真实信息模块;卡片圆角 8-12px,阴影轻,边框低透明。",
|
||
"不要把卡片套卡片;一屏最多 1 个主面板 + 1 个辅助网格/证据条。",
|
||
"UI 区域必须避开商品脸部、Logo、关键结构和手持/使用动作。",
|
||
]
|
||
: [
|
||
"First screen must feel like a cover/KV poster: single subject, strong focus, clean headline safe zone.",
|
||
"Use cards only for real information modules; radius 8-12px, light shadow, low-opacity border.",
|
||
"No card nesting. Max one main panel plus one supporting grid/proof strip per screen.",
|
||
"UI regions must avoid product face, logo, critical structure, and usage action.",
|
||
],
|
||
imageFusion: isZh
|
||
? [
|
||
params.hasReferenceImages
|
||
? "已给参考图:生成画面必须复用参考素材的结构、颜色、材质、比例和关键识别特征。"
|
||
: "未给参考图:画面主体仍需清晰、完整、可识别,不用抽象氛围图代替产品。",
|
||
params.hasModelImage
|
||
? "已给模特图:涉及人物时保持同一人物身份,不替换脸型、发型、体型和服装风格。"
|
||
: "未给模特图:不要依赖人物脸部作为主卖点。",
|
||
params.hasBundleImages
|
||
? "搭配图只服务一屏关联销售,其余屏不要把搭配品抢成主角。"
|
||
: "未给搭配图:不要凭空制造搭配商品主叙事。",
|
||
"AGENT排版模式下,生图提示词只生成干净画面层,不生成任何可见文字、假按钮、假 UI。",
|
||
]
|
||
: [
|
||
params.hasReferenceImages
|
||
? "Reference images are provided: preserve structure, color, material, proportion, and key identity cues."
|
||
: "No reference image: keep the product subject clear and inspectable; avoid abstract atmosphere as a substitute.",
|
||
params.hasModelImage
|
||
? "Model image is provided: preserve the same identity when humans appear."
|
||
: "No model image: do not rely on a human face as the main selling point.",
|
||
params.hasBundleImages
|
||
? "Bundle products serve exactly one cross-sell screen and must not steal the main subject elsewhere."
|
||
: "No bundle refs: do not invent a bundle-product narrative.",
|
||
"In agent-layout mode, image prompts generate clean background only: no visible text, fake buttons, or fake UI.",
|
||
],
|
||
dslContract: isZh
|
||
? [
|
||
"dslV2Screens 必须语义化命名 id,例如 hero-title、proof-strip、feature-grid。",
|
||
"region 使用 0-1 归一化坐标;宽高和位置必须像设计工具的 frame,而不是随意漂浮。",
|
||
"nodes 只使用允许的 kind,优先 panel/stack/row/heading/body/badge/proof/cta 的少量组合。",
|
||
"每个文本节点必须可编辑、短句化、层级明确,不把长说明塞进 heading。",
|
||
]
|
||
: [
|
||
"dslV2Screens must use semantic ids such as hero-title, proof-strip, feature-grid.",
|
||
"Use normalized 0-1 regions like design-tool frames, not arbitrary floating boxes.",
|
||
"Use only allowed node kinds, with a sparse combination of panel/stack/row/heading/body/badge/proof/cta.",
|
||
"Every text node must be editable, concise, and hierarchy-aware; no long explanations in headings.",
|
||
],
|
||
antiPatterns: isZh
|
||
? [
|
||
"禁止大面积玻璃拟态卡片盖住主体。",
|
||
"禁止蓝紫渐变、发光球、装饰块堆满画面。",
|
||
"禁止主标题写“核心价值/场景痛点/第X屏/本屏展示”等规划话术。",
|
||
"禁止同屏 4 个以上信息模块或超过 2 种强调色。",
|
||
"禁止可见文字由生图模型生成,文字必须走可编辑 UI 层。",
|
||
]
|
||
: [
|
||
"No large glassmorphism cards covering the subject.",
|
||
"No blue/purple gradient wash, glowing blobs, or decorative block clutter.",
|
||
"No planning phrases in visible copy such as Screen X or This frame shows.",
|
||
"No more than 4 information modules or 2 accent colors in one screen.",
|
||
"No image-model rendered visible text; text must live in editable UI layers.",
|
||
],
|
||
reviewChecklist: isZh
|
||
? [
|
||
"主体是否完整、清晰、没有被 UI 遮挡?",
|
||
"首屏是否像封面海报,而不是拼贴说明页?",
|
||
"字体层级是否只有标题/副标题/正文三层,且统一?",
|
||
"UI 覆盖面积是否控制在 45% 内,并避开关键素材?",
|
||
"是否没有负字距、超大圆角、重阴影、卡片套卡片?",
|
||
"visualPrompt 是否完全禁止生成可见文字?",
|
||
]
|
||
: [
|
||
"Is the subject complete, clear, and unobstructed by UI?",
|
||
"Does the first screen feel like a cover poster rather than a collage?",
|
||
"Are there only title/subtitle/body levels, consistently applied?",
|
||
"Is UI coverage under 45% and away from critical asset details?",
|
||
"Are negative tracking, huge radii, heavy shadows, and nested cards avoided?",
|
||
"Does visualPrompt fully forbid visible text generation?",
|
||
],
|
||
metadata: {
|
||
screenCount: Math.max(1, Math.round(params.screenCount || 1)),
|
||
hasReferenceImages: params.hasReferenceImages,
|
||
hasModelImage: params.hasModelImage,
|
||
hasBundleImages: params.hasBundleImages,
|
||
productName: normalizeText(params.productName) || undefined,
|
||
brandTone: normalizeText(params.brandTone) || undefined,
|
||
},
|
||
};
|
||
}
|
||
|
||
export function summarizeDetailUiDesignSkill(
|
||
spec: DetailUiDesignSkillSpec
|
||
): DetailUiDesignSkillSummary {
|
||
return {
|
||
version: spec.version,
|
||
name: spec.name,
|
||
source: spec.source,
|
||
visualDirection: spec.visualDirection.slice(0, 4),
|
||
reviewChecklist: spec.reviewChecklist.slice(0, 6),
|
||
};
|
||
}
|
||
|
||
export function buildDetailUiDesignSkillFlowStep(
|
||
spec: DetailUiDesignSkillSpec
|
||
): string {
|
||
return spec.language === "zh"
|
||
? "详情 UI Design Skill:按 DESIGN.md 方式锁定视觉语言、素材融合、安全区、可编辑 DSL 与反俗套自检。"
|
||
: "Detail UI Design Skill: lock visual language, asset fusion, safe zones, editable DSL, and anti-slop review using DESIGN.md-style rules.";
|
||
}
|
||
|
||
export function buildDetailUiDesignSkillPrompt(
|
||
spec: DetailUiDesignSkillSpec
|
||
): string {
|
||
const section = (title: string, items: string[]) =>
|
||
`${title}\n${items.map((item) => `- ${item}`).join("\n")}`;
|
||
|
||
const title = spec.language === "zh"
|
||
? `【${spec.name} ${spec.version}】`
|
||
: `[${spec.name} ${spec.version}]`;
|
||
|
||
return [
|
||
title,
|
||
`${spec.language === "zh" ? "来源" : "Source"}: ${spec.source}`,
|
||
`${spec.language === "zh" ? "画布" : "Canvas"}: ${spec.canvas.width}x${spec.canvas.height}; ${spec.canvas.grid}; ${spec.canvas.maxOverlayCoverage}`,
|
||
section(spec.language === "zh" ? "视觉方向" : "Visual Direction", spec.visualDirection),
|
||
section(spec.language === "zh" ? "字体层级" : "Typography", spec.typography),
|
||
section(spec.language === "zh" ? "布局规则" : "Layout Rules", spec.layout),
|
||
section(spec.language === "zh" ? "图片素材融合" : "Image Fusion", spec.imageFusion),
|
||
section(spec.language === "zh" ? "DSL 输出契约" : "DSL Contract", spec.dslContract),
|
||
section(spec.language === "zh" ? "禁止项" : "Anti-patterns", spec.antiPatterns),
|
||
section(spec.language === "zh" ? "生成前自检" : "Preflight Checklist", spec.reviewChecklist),
|
||
].join("\n");
|
||
}
|