/** * 画个标 · 技能资源库(核心:注册表 + 通用工具) * 每个技能采用标准化标识: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 } }; })();