/** * 🎨 10 种智能体头像风格 × 100 个 = 1000 个 SVG 生成脚本 * - 风格:DiceBear 10 种(资源库 js/data/sources/dicebear-*.js) * - 每种风格专属:渐变圆底 + 白色圆环 + lucide 风格徽章(视觉身份差异化) * - bottts / bottts-neutral 复用 bot-common 过滤负面表情 * - 确定性 seed:{style}-1 .. {style}-100,每风格内部唯一 * * 用法:node scripts/gen-10styles.js */ 'use strict'; const fs = require('fs'); const path = require('path'); const vm = require('vm'); const { generateSeeds } = require('./bot-common'); const ROOT = path.resolve(__dirname, '..'); const OUT = path.join(ROOT, 'output', 'agent-avatars-png', 'avatars'); const DATE = '2026-08-19'; // —— 1. 风格配置(视觉身份 + 渐变 + 徽章图标)—— const STYLES = [ { id: 'bottts', name: '科技机器人', desc: '机械与电路', c1: '#0f2a6b', c2: '#2563eb', badge: 'bot' }, { id: 'bottts-neutral', name: '极简机器人', desc: '黑白灰几何', c1: '#374151', c2: '#9ca3af', badge: 'cpu' }, { id: 'micah', name: '米迦人物', desc: '几何人物插画', c1: '#6d28d9', c2: '#ec4899', badge: 'fingerprint' }, { id: 'pixel-art', name: '像素风', desc: '复古 8-bit 游戏', c1: '#581c87', c2: '#db2777', badge: 'gamepad-2' }, { id: 'fun-emoji', name: '趣味表情', desc: '活泼可爱 emoji', c1: '#ea580c', c2: '#facc15', badge: 'smile' }, { id: 'rings', name: '圆环几何', desc: '极简同心圆', c1: '#0d9488', c2: '#5eead4', badge: 'circle' }, { id: 'shapes', name: '几何形状', desc: '现代简约图形', c1: '#e11d48', c2: '#fb7185', badge: 'shapes' }, { id: 'avataaars', name: '阿凡达', desc: '可定制人物角色', c1: '#166534', c2: '#4ade80', badge: 'user' }, { id: 'notionists', name: 'Notion 风', desc: '现代扁平商务', c1: '#1e3a8a', c2: '#38bdf8', badge: 'briefcase' }, { id: 'adventurer', name: '冒险家', desc: '探险冒险人物', c1: '#92400e', c2: '#fbbf24', badge: 'compass' } ]; // —— 2. 加载引擎 + 风格定义 + lucide 徽章 —— global.window = global; function load(rel) { vm.runInThisContext(fs.readFileSync(path.join(ROOT, rel), 'utf8'), { filename: rel }); } load('js/core/dicebear.js'); load('js/data/sources/lucide.js'); for (const s of STYLES) load('js/data/sources/dicebear-' + s.id + '.js'); const lucideIcons = window['HGB_Source_lucide'].icons; // —— 3. 徽章 SVG(lucide 24×24 白色线条)—— function badgeSvg(name) { const ic = lucideIcons.find(x => x.name === name); if (!ic || !ic.svg) return ''; // 提取 svg 内部 const m = ic.svg.match(/]*>([\s\S]*)<\/svg>/); return m ? m[1] : ''; } // —— 4. 包装:渐变圆底 + 头像 + 白环 + 徽章 —— function wrap(svg, w, h, st, idx) { const inner = svg.replace(/^<\?xml[^>]*\?>\s*/, '').replace(/^]*>/, '').replace(/<\/svg>\s*$/, ''); // 头像缩放:占 200×200 区域居中 const s = 200 / Math.max(w, h); const tx = (240 - w * s) / 2, ty = (240 - h * s) / 2; const badge = badgeSvg(st.badge); return ` ${inner} ${badge} `; } // —— 5. 逐风格生成 —— const meta = { generatedAt: new Date().toISOString(), date: DATE, styles: [] }; fs.mkdirSync(OUT, { recursive: true }); let total = 0; for (const st of STYLES) { // 每风格 100 个不同 seed:{style}-1..100,复用 generateSeeds 保证唯一 const cards = generateSeeds(seed => HGB_Dicebear.renderAvatar(st.id, seed), 100); const styleDir = path.join(OUT, st.id); fs.mkdirSync(styleDir, { recursive: true }); const list = []; cards.forEach((c, i) => { const n = String(i + 1).padStart(3, '0'); const def = window['HGB_Dicebear_' + st.id.replace(/-/g, '_')]; const w = def.canvas.width, h = def.canvas.height; const out = wrap(c.svg, w, h, st, i + 1); const file = `${st.id}-${n}.svg`; fs.writeFileSync(path.join(styleDir, file), out, 'utf8'); list.push({ file, seed: c.seed, date: DATE }); }); meta.styles.push({ id: st.id, name: st.name, desc: st.desc, c1: st.c1, c2: st.c2, badge: st.badge, count: list.length, avatars: list }); total += list.length; console.log(`✅ ${st.id}(${st.name}): ${list.length} 个`); } fs.writeFileSync(path.join(OUT, 'meta.json'), JSON.stringify(meta), 'utf8'); console.log(`🎉 共 ${total} 个 SVG → output/agent-avatars-png/avatars/ + meta.json`);