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
+87
View File
@@ -0,0 +1,87 @@
/**
* 画个标 · 海报 AI 设计器:DeepSeek 输出设计决策 JSON,系统校验后映射为元素方案
* 复用 HGB_AI.chat;白名单:版式模板(poster-generator.LAYOUTS)+ 配色(poster-generator.PALETTES)
* AI 输出 JSON:{ overallConcept, plans:[{ title, subtitle, paletteId, layoutId, concept }] }
* AI 失败由调用方降级为内置引擎(defaultDecision 随机组合),保证页面不空白。
*/
const HGB_PosterAI = (function () {
'use strict';
function buildPrompt(opt) {
const layoutList = HGB_PosterGenerator.LAYOUTS.map(l => `${l.id}:${l.name}`).join('\n');
const paletteList = HGB_PosterGenerator.PALETTES.map(p => `${p.id}:${p.name}(背景 ${p.bg})`).join('\n');
const sys = `你是顶级的海报设计师。请基于用户提供的主题信息,从白名单中选择版式与配色,设计多套专业海报方案。
设计原则:
1. 标题(title)精炼有力、突出主题;副标题(subtitle)补充说明(可空)。
2. 版式 layoutId 与配色 paletteId 必须从白名单中选择,不得编造。
3. 多套方案尽量使用不同的版式与配色,呈现不同设计方向。
4. 每套方案给出贴切的中文方案名(name)与设计理念(concept)。
只输出 JSON,不要输出任何其他文字。JSON 结构:
{ "overallConcept": "整体理念", "plans": [ { "name": "方案名", "title": "主标题", "subtitle": "副标题(可空)", "paletteId": "indigo", "layoutId": "centered", "concept": "设计理念" } ] }
其中 plans 数组包含 ${opt.count || 5} 套方案。`;
const user = `【主题信息】
- 行业/主题:${opt.industry || '通用'}
- 主标题:${opt.title || '(由你拟定)'}
- 副标题:${opt.sub || '(无)'}
- 描述:${opt.desc || '(无)'}
【版式白名单】
${layoutList}
【配色白名单】
${paletteList}
请设计 ${opt.count || 5} 套海报方案。`;
return [
{ role: 'system', content: sys },
{ role: 'user', content: user }
];
}
function extractJson(content) {
let t = String(content || '').trim();
t = t.replace(/^```(?:json)?\s*/i, '').replace(/```\s*$/, '');
const s = t.indexOf('{'), e = t.lastIndexOf('}');
if (s < 0 || e < s) throw new Error('AI 输出不是有效 JSON');
return JSON.parse(t.slice(s, e + 1));
}
/** 校验并规范化 AI 输出 */
function validate(data, opt) {
if (!data || !Array.isArray(data.plans) || !data.plans.length) throw new Error('AI 未返回方案');
for (const p of data.plans) {
if (!HGB_PosterGenerator.PALETTES.some(x => x.id === p.paletteId)) p._paletteInvalid = true;
if (!HGB_PosterGenerator.LAYOUTS.some(x => x.id === p.layoutId)) p._layoutInvalid = true;
}
return data;
}
/** 决策 → 元素方案(非法字段回退默认,不随机) */
function mapToPlan(spec, p, idx) {
const decision = HGB_PosterGenerator.defaultDecision(p.title, p.subtitle);
if (!p._paletteInvalid) decision.paletteId = p.paletteId;
if (!p._layoutInvalid) decision.layoutId = p.layoutId;
const elements = HGB_PosterGenerator.generate(spec, decision);
return {
id: 'poster_' + Date.now().toString(36) + idx.toString(36),
name: (p.name || '').trim() || ('方案 ' + (idx + 1)),
concept: (p.concept || '').trim(),
elements,
specId: spec.id,
ai: true
};
}
async function generatePostersAI(opt) {
const spec = HGB_PosterStore.spec();
const messages = buildPrompt(opt);
const content = await HGB_AI.chat(messages, { json: true, maxTokens: 4096 });
const data = validate(extractJson(content), opt);
const plans = data.plans.map((p, i) => mapToPlan(spec, p, i));
return { plans, overallConcept: (data.overallConcept || '').trim(), raw: content };
}
return { generatePostersAI, buildPrompt, extractJson, validate, mapToPlan };
})();