feat: 初始化画个标仓库 - AI驱动设计系统(BYOK模式) - 224源/371504图标+25海报源+130技能

This commit is contained in:
sgp
2026-10-03 21:44:18 +08:00
commit 9e222877ea
497 changed files with 43611 additions and 0 deletions
+139
View File
@@ -0,0 +1,139 @@
/**
* 冒烟:头像模式 AI 路径(aiDesigner buildPrompt/validate/mapToPlan)
* 验证:
* 1) buildPrompt 头像版布局白名单 = 头像布局、不含 lockup 白名单
* 2) validate + normalizeLayoutDecisions 把 AI 布局修正到头像白名单内
* 3) mapToPlan 产物 layoutId 在头像白名单、lockup 为空、无文字元素
*/
'use strict';
const fs = require('fs');
const path = require('path');
const vm = require('vm');
const ROOT = path.resolve(__dirname, '..');
// 加载顺序:数据 → 核心(依赖全局常量 HGB_*)
const files = [
'js/data/icons.js',
'js/data/styles.js',
'js/data/industries.js',
'js/data/builtin-icons.js',
'js/data/decorations.js',
'js/core/generator.js',
'js/core/avatarGen.js',
'js/skills/skills.js',
'js/core/ai.js',
'js/core/aiDesigner.js'
];
for (const f of files) {
const src = fs.readFileSync(path.join(ROOT, f), 'utf8');
// vm 在全局作用域执行,使 HGB_* 成为全局词法绑定
vm.runInThisContext(src, { filename: f });
}
const ind = HGB_Industries.find('food');
const opt = {
industry: ind,
brand: '川味小馆',
sub: '',
desc: '川菜馆,麻辣鲜香',
icons: [],
styleIds: [],
skills: ['composition', 'contrast-check'],
count: 5,
avatar: true
};
let pass = 0, fail = 0;
function check(name, ok, detail) {
if (ok) { pass++; console.log(' ✅ ' + name + (detail ? ' — ' + detail : '')); }
else { fail++; console.log(' ❌ ' + name + (detail ? ' — ' + detail : '')); }
}
// 1) Prompt 头像版
const msgs = HGB_AIDesigner.buildPrompt(opt);
const sys = msgs[0].content, user = msgs[1].content;
const avatarLayoutIds = HGB_AvatarGen.AVATAR_LAYOUTS;
const fullLayoutIds = HGB_Generator.LAYOUTS.map(l => l.id);
console.log('== 1) buildPrompt 头像版 ==');
check('系统提示为头像设计师', /头像设计师/.test(sys));
// 布局白名单:LOGO 独有的布局(monogram 等)不应出现在头像 prompt
const logoOnly = fullLayoutIds.filter(id => !avatarLayoutIds.includes(id));
check('头像 prompt 不含 LOGO 专属布局', !logoOnly.some(id => new RegExp('\\b' + id + '\\b').test(user)), '排除: ' + logoOnly.join('/'));
check('头像 prompt 含头像布局', avatarLayoutIds.every(id => new RegExp('\\b' + id + '\\b').test(user)) || user.includes('orb'));
check('头像 prompt 不含 lockup 白名单', !user.includes('lockup') && !user.includes('品牌组合位置'));
check('头像 prompt 强调无文字/纯图形', /纯图形|不含任何文字/.test(sys));
// 2) validate:给一批非法布局(含 LOGO 专属 monogram/ribbon),验证全部修正到头像白名单
console.log('== 2) validate 布局修正 ==');
const fake = {
overallConcept: 'x',
plans: [
{ name: 'A', concept: 'c', icon: 'bad', layout: 'monogram', styleId: 'minimal' },
{ name: 'B', concept: 'c', icon: 'bad', layout: 'ribbon', styleId: 'artdeco' },
{ name: 'C', concept: 'c', icon: 'bad', layout: 'badge', styleId: 'neon' },
{ name: 'D', concept: 'c', icon: 'bad', layout: 'seal', styleId: 'minimal' },
{ name: 'E', concept: 'c', icon: 'bad', layout: 'orb', styleId: 'minimal' }
]
};
const data = HGB_AIDesigner.validate(fake, opt);
const layouts = data.plans.map(p => p.layout);
const uniq = new Set(layouts);
check('全部布局在头像白名单', layouts.every(l => avatarLayoutIds.includes(l)), layouts.join('/'));
check('非法布局被修正并标记', layouts.includes('orb') || data.plans.some(p => p._layoutInvalid));
check('布局多样性 ≥3', uniq.size >= 3, '种类 ' + uniq.size);
const round = layouts.filter(l => ['badge', 'seal', 'orb', 'duo', 'burst'].includes(l)).length;
check('头像圆形构图 ≤4', round <= 4, '圆形 ' + round);
// 3) mapToPlan 产物结构(icon 非法 → 回退行业图标)
console.log('== 3) mapToPlan 产物 ==');
const plans = data.plans.map((p, i) => HGB_AIDesigner.mapToPlan(p, opt, i));
for (let i = 0; i < plans.length; i++) {
const p = plans[i];
check('方案' + (i + 1) + ' layoutId 在头像白名单', avatarLayoutIds.includes(p.layoutId), p.layoutId);
const hasText = (p.markElements || []).some(el => el.type === 'text');
check('方案' + (i + 1) + ' Mark 无文字元素', !hasText);
check('方案' + (i + 1) + ' 有 ai 标记与概念', !!p.ai && !!p.concept);
check('方案' + (i + 1) + ' 配色跟随风格', !!p.paletteName && !!p.bg);
}
// 图标差异:AI 图标非法时按序号轮换行业图标,不应全套同一
const iconSet = new Set(plans.map(p => (p.aiIcon || '') + '|' + (p.markElements || []).filter(e => e.type === 'icon').map(e => e.iconId).join(',')));
check('图标有差异(>1 种)', iconSet.size > 1, '种类 ' + iconSet.size);
// 4) 头像专有布局(avatarOnly)差异化:LOGO 全局池排除、头像白名单包含
console.log('== 4) 头像专有布局差异化 ==');
const avatarOnlyIds = HGB_Generator.LAYOUTS.filter(l => l.avatarOnly).map(l => l.id);
check('存在 3 个头像专有布局', avatarOnlyIds.length === 3 && JSON.stringify(avatarOnlyIds) === JSON.stringify(['fullbleed', 'concentric', 'centroid']), avatarOnlyIds.join('/'));
// LOGO 模式 generatePlans(无白名单)不得出现头像专有布局
const logoPlans = HGB_Generator.generatePlans({ industry: ind, brand: '川味小馆', count: 12 });
check('LOGO 模式 12 套全部布局合法', logoPlans.every(p => !avatarOnlyIds.includes(p.layoutId)));
check('LOGO 模式布局多样', new Set(logoPlans.map(p => p.layoutId)).size >= 5);
// 头像模式 generateAvatars 白名单包含头像专有布局
check('头像白名单含头像专有布局', avatarOnlyIds.every(id => HGB_AvatarGen.AVATAR_LAYOUTS.includes(id)));
const avatarPlans = HGB_AvatarGen.generateAvatars({ industry: ind, brand: '川味小馆', count: 20 });
const avLayouts = new Set(avatarPlans.map(p => p.layoutId));
check('头像 20 套产出头像专有布局', avatarOnlyIds.some(id => avLayouts.has(id)), '头像布局 ' + avLayouts.size + ' 种: ' + [...avLayouts].slice(0, 8).join('/') + '…');
check('头像 20 套全部在白名单内', avatarPlans.every(p => HGB_AvatarGen.isAvatarLayout(p.layoutId)));
// AI 校验:头像 validate 允许头像专有布局;LOGO validate 标记头像专有布局非法
const avFake = { overallConcept: 'x', plans: [
{ name: 'A', concept: 'c', icon: 'bad', layout: 'fullbleed', styleId: 'minimal' },
{ name: 'B', concept: 'c', icon: 'bad', layout: 'concentric', styleId: 'neon' },
{ name: 'C', concept: 'c', icon: 'bad', layout: 'centroid', styleId: 'minimal' },
{ name: 'D', concept: 'c', icon: 'bad', layout: 'orb', styleId: 'minimal' },
{ name: 'E', concept: 'c', icon: 'bad', layout: 'seal', styleId: 'artdeco' }
]};
const avData = HGB_AIDesigner.validate(avFake, opt);
check('AI 头像接受头像专有布局', avData.plans.every(p => avatarOnlyIds.includes(p.layout) || HGB_AvatarGen.isAvatarLayout(p.layout)));
const logoOpt = { ...opt, avatar: false };
const lgFake = { overallConcept: 'x', plans: [
{ name: 'A', concept: 'c', icon: 'bad', layout: 'fullbleed', styleId: 'minimal' },
{ name: 'B', concept: 'c', icon: 'bad', layout: 'badge', styleId: 'minimal' },
{ name: 'C', concept: 'c', icon: 'bad', layout: 'seal', styleId: 'minimal' },
{ name: 'D', concept: 'c', icon: 'bad', layout: 'orb', styleId: 'minimal' },
{ name: 'E', concept: 'c', icon: 'bad', layout: 'monogram', styleId: 'minimal' }
]};
const lgData = HGB_AIDesigner.validate(lgFake, logoOpt);
const lgLayouts = lgData.plans.map(p => p.layout);
check('AI LOGO 排除头像专有布局', lgLayouts.every(l => !avatarOnlyIds.includes(l)), lgLayouts.join('/'));
console.log('\n结果: ' + pass + ' 通过, ' + fail + ' 失败');
process.exit(fail ? 1 : 0);