Files
drawlogo/scripts/gen-10styles.js
T

100 lines
5.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 🎨 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(/<svg[^>]*>([\s\S]*)<\/svg>/);
return m ? m[1] : '';
}
// —— 4. 包装:渐变圆底 + 头像 + 白环 + 徽章 ——
function wrap(svg, w, h, st, idx) {
const inner = svg.replace(/^<\?xml[^>]*\?>\s*/, '').replace(/^<svg[^>]*>/, '').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 `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g${idx}" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="${st.c1}"/><stop offset="100%" stop-color="${st.c2}"/>
</linearGradient>
<clipPath id="c${idx}"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g${idx})" clip-path="url(#c${idx})"/>
<g transform="translate(${tx.toFixed(2)} ${ty.toFixed(2)}) scale(${s.toFixed(4)})">${inner}</g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)">${badge}</g></g>
</svg>`;
}
// —— 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`);