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

101 lines
5.6 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.
/**
* 🎨 14 种智能体头像风格 × 100 个 = 1400 个 SVG 生成脚本
* - 风格:DiceBear 14 种(资源库 js/data/sources/dicebear-*.js)
* - 每种风格专属:渐变圆底 + 白色圆环 + lucide 风格徽章(视觉身份差异化)
* - bottts / bottts-neutral 复用 bot-common 过滤负面表情
* - 确定性 seed:{style}-1 .. {style}-100,每风格内部唯一
*
* 用法:node scripts/gen-14styles.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: 'avataaars', name: '阿凡达', desc: '可定制人物角色', c1: '#166534', c2: '#4ade80', badge: 'user' },
{ id: 'adventurer', name: '冒险家', desc: '探险冒险人物', c1: '#92400e', c2: '#fbbf24', badge: 'compass' },
{ id: 'avataaars-neutral', name: '中性阿凡达', desc: '中性色人物角色', c1: '#065f46', c2: '#2dd4bf', badge: 'smile' },
{ id: 'adventurer-neutral',name: '中性冒险家', desc: '中性色探险人物', c1: '#78350f', c2: '#d97706', badge: 'map' },
{ id: 'open-peeps', name: '手绘小人', desc: '涂鸦风人物', c1: '#4338ca', c2: '#818cf8', badge: 'users' },
{ id: 'personas', name: '人物插画', desc: '现代扁平人物', c1: '#be185d', c2: '#f472b6', badge: 'palette' },
{ id: 'dylan', name: '迪伦人物', desc: '涂鸦线条人物', c1: '#0e7490', c2: '#67e8f9', badge: 'pen-tool' },
{ id: 'miniavs', name: '迷你头像', desc: '极简小人头像', c1: '#155e75', c2: '#22d3ee', badge: 'heart' },
{ id: 'big-smile', name: '大笑脸', desc: '圆脸微笑', c1: '#b45309', c2: '#fde047', badge: 'laugh' },
{ id: 'big-ears', name: '大耳朵', desc: '圆脸大耳', c1: '#9a3412', c2: '#fb923c', badge: 'ear' },
{ id: 'croodles', name: '卡通线条', desc: '简单线条卡通', c1: '#7c2d12', c2: '#f97316', badge: 'pencil' },
{ id: 'lorelei', name: '洛雷莱', desc: '唯美插画人物', c1: '#9d174d', c2: '#f9a8d4', badge: 'flower' }
];
// —— 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 '';
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*$/, '');
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) {
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`);