145 lines
7.0 KiB
JavaScript
145 lines
7.0 KiB
JavaScript
/**
|
||
* 画个标 · 技能资源库(核心:注册表 + 通用工具)
|
||
* 每个技能采用标准化标识:id / name / category / emoji / scene / summary / params / source / run / test
|
||
* 分类:layout(构图) color(配色) typography(字体) icon(图标) ai(智能工作流) presentation(展示) export(导出)
|
||
*
|
||
* 技能按分类拆分文件(js/skills/{category}.js),各自调用 HGB_Skills.register([...]) 注册:
|
||
* layout.js color.js typography.js icon.js ai.js presentation.js export.js
|
||
*/
|
||
const HGB_Skills = (function () {
|
||
'use strict';
|
||
|
||
/* ---------- 通用工具 ---------- */
|
||
/** 颜色亮度(0-1) */
|
||
function lum(hex) {
|
||
const c = String(hex || '#FFFFFF').replace('#', '');
|
||
if (c.length !== 6) return 0.9;
|
||
const r = parseInt(c.slice(0, 2), 16) / 255;
|
||
const g = parseInt(c.slice(2, 4), 16) / 255;
|
||
const b = parseInt(c.slice(4, 6), 16) / 255;
|
||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||
}
|
||
/** WCAG 对比度 */
|
||
function contrast(a, b) {
|
||
const la = lum(a), lb = lum(b);
|
||
const hi = Math.max(la, lb), lo = Math.min(la, lb);
|
||
return Math.round(((hi + 0.05) / (lo + 0.05)) * 100) / 100;
|
||
}
|
||
/** 从内置图标库搜索图标(懒加载适配:优先搜已加载源,其次索引;返回带库前缀 id 的图标) */
|
||
function searchIcon(keyword) {
|
||
const kw = String(keyword || '').toLowerCase();
|
||
if (!kw) return [];
|
||
const out = [];
|
||
const seen = new Set();
|
||
const push = (id, source) => {
|
||
if (!seen.has(id)) { seen.add(id); out.push({ id, source }); }
|
||
};
|
||
// 已加载源(含 svg,能力最全)
|
||
for (const hit of HGB_IconLib.searchLoaded(kw, 8)) push(hit.id, hit.source);
|
||
// 索引兜底(轻量全量 id/name,无需 svg)
|
||
if (out.length < 8) {
|
||
for (const hit of HGB_IconLib.searchIndex(kw, 8)) push(hit.id, hit.source);
|
||
}
|
||
return out.slice(0, 8);
|
||
}
|
||
/** 估算文字元素的实际渲染宽度(中文全角,西文 0.55,计入字距) */
|
||
function estTextW(el) {
|
||
const t = String(el.text || '');
|
||
const cn = (t.match(/[\u4e00-\u9fa5]/g) || []).length;
|
||
const fs = el.fontSize || 24;
|
||
const sp = el.letterSpacing || 0;
|
||
return cn * fs + (t.length - cn) * fs * 0.55 + Math.max(0, t.length - 1) * sp;
|
||
}
|
||
/** 元素实际包围盒(文字按估算宽度,水平居中) */
|
||
function elBox(el) {
|
||
if (el.type === 'text') {
|
||
const w = estTextW(el);
|
||
return { x: el.x - w / 2, y: el.y - (el.h || 40) / 2, w, h: el.h || 40 };
|
||
}
|
||
return { x: el.x, y: el.y, w: el.w, h: el.h };
|
||
}
|
||
|
||
/* ---------- 设计流程步骤(2026-08-15:技能按「设计流水线」组织) ---------- */
|
||
const DESIGN_STEPS = [
|
||
{ step: 1, id: 'insight', name: '品牌洞察', emoji: '🔍', desc: '把品牌描述提炼成关键词与方向,指导后续生成' },
|
||
{ step: 2, id: 'create', name: '方案生成', emoji: '🎨', desc: '产出色板/字体/布局/图标等设计建议,生成多套候选方案' },
|
||
{ step: 3, id: 'review', name: '方案评审', emoji: '🔬', desc: '对每套方案统一体检(构图/配色/字体/图标),输出检查报告' },
|
||
{ step: 4, id: 'iterate', name: '迭代优化', emoji: '🔄', desc: '按评审结果自动迭代,生成改进方案' },
|
||
{ step: 5, id: 'deliver', name: '交付导出', emoji: '📦', desc: '按用途导出多格式文件,输出交付清单' }
|
||
];
|
||
/** 技能 → 设计流程步骤(1-5)映射(按 id 显式指定) */
|
||
const SKILL_STEPS = {
|
||
// ① 品牌洞察
|
||
'ai-workflow': 1, 'brand-brief': 1, 'slogan-gen': 1, 'prompt-craft': 1, 'concept-words': 1,
|
||
// ② 方案生成
|
||
'layout-choice': 2, 'palette-build': 2, 'color-harmony': 2, 'gradient-suggest': 2, 'color-psychology': 2,
|
||
'font-pairing': 2, 'font-weight-choose': 2, 'icon-match': 2, 'icon-search-cn': 2, 'showcase-bg': 2,
|
||
'neutral-palette': 2,
|
||
// ③ 方案评审(检查类)
|
||
'composition': 3, 'align-check': 3, 'negative-space': 3, 'spacing-check': 3, 'proportion-check': 3,
|
||
'visual-balance': 3, 'focal-point': 3, 'rule-of-thirds': 3, 'circle-crop-safety': 3,
|
||
'contrast-check': 3, 'wcag-contrast': 3, 'color-blind-check': 3, 'monochrome-check': 3,
|
||
'font-hierarchy': 3, 'text-fit': 3, 'tiny-readable': 3, 'letter-spacing-check': 3, 'line-break': 3,
|
||
'cjk-mix-check': 3,
|
||
'icon-consistency': 3, 'icon-tone': 3, 'icon-count': 3, 'icon-text-ratio': 3, 'icon-padding': 3,
|
||
'stroke-consistency': 3,
|
||
'print-safety': 3,
|
||
// ④ 迭代优化
|
||
'ai-iterate': 4,
|
||
// ⑤ 交付导出
|
||
'format-choose': 5, 'export-sizing': 5, 'export-multi': 5, 'export-avatar': 5, 'favicon-kit': 5,
|
||
'transparent-check': 5, 'mockup-scene': 5, 'vi-checklist': 5, 'launch-kit': 5,
|
||
'file-naming': 5, 'brand-story': 5
|
||
};
|
||
/** 技能性质:gen 生成建议 / check 方案检查 / deliver 交付导出 */
|
||
function kind(id) {
|
||
const st = SKILL_STEPS[id];
|
||
if (st === 3) return 'check';
|
||
if (st === 5) return (id === 'transparent-check' || id === 'print-safety') ? 'check' : 'deliver';
|
||
return 'gen';
|
||
}
|
||
|
||
/* ---------- 注册表 API ---------- */
|
||
const skills = [];
|
||
function stepOf(id) { return SKILL_STEPS[id] || 2; } // 技能所属流程步骤(未映射默认方案生成)
|
||
/** 技能适用工作区:logo(LOGO) / avatar(头像) / poster(海报);未显式标注按分类默认 */
|
||
function workspacesOf(id) {
|
||
const s = get(id);
|
||
if (!s) return ['logo', 'avatar', 'poster'];
|
||
if (s.workspaces) return s.workspaces;
|
||
if (s.category === 'typography' || id === 'slogan-gen') return ['logo', 'poster']; // 头像纯图形无文字(slogan 为文字类)
|
||
return ['logo', 'avatar', 'poster'];
|
||
}
|
||
/** 某工作区可用的全部技能 */
|
||
function forWorkspace(ws) {
|
||
return skills.filter(s => workspacesOf(s.id).includes(ws));
|
||
}
|
||
function byStep(step, ws) { return (ws ? forWorkspace(ws) : skills).filter(s => SKILL_STEPS[s.id] === step); } // 某步骤的全部技能
|
||
function steps() { return DESIGN_STEPS; } // 设计流程步骤元数据
|
||
function register(defs) {
|
||
for (const d of (defs || [])) skills.push(d);
|
||
return skills.length;
|
||
}
|
||
function all() { return skills; }
|
||
function categories() {
|
||
const map = {};
|
||
for (const s of skills) map[s.category] = (map[s.category] || 0) + 1;
|
||
return Object.entries(map).map(([id, n]) => ({ id, count: n }));
|
||
}
|
||
function get(id) { return skills.find(s => s.id === id) || null; }
|
||
function run(id, args) {
|
||
const s = get(id);
|
||
if (!s) return { error: '技能不存在: ' + id };
|
||
try { return s.run(args || {}); }
|
||
catch (e) { return { error: '技能执行失败: ' + e.message }; }
|
||
}
|
||
function test(id) {
|
||
const s = get(id);
|
||
if (!s) return { error: '技能不存在: ' + id };
|
||
try { return s.test ? s.test() : { note: '该技能未提供自测' }; }
|
||
catch (e) { return { error: '技能自测失败: ' + e.message }; }
|
||
}
|
||
|
||
return { register, all, categories, get, run, test, steps, stepOf, byStep, workspacesOf, forWorkspace, kind, utils: { lum, contrast, searchIcon, estTextW, elBox } };
|
||
})();
|