/**
* 🤖 100 个 bottts 机器人「技能变换」变体预览页
* 组合技能库变换(全部来自 js/skills/):
* 1. palette-build → 10 个行业色板(primary/secondary/accent)
* 2. gradient-suggest→ 每个主色的同色/互补/邻近渐变方向
* 3. lucide 源图标 → 科技徽章叠加
* 每个色板 × 5 渐变模式 × 2 徽章 = 10 变体 × 10 色板 = 100 个变体。
* 主体保持 bottts 机器人(资源库 DiceBear 源),渐变圆底 + 亮环 + 科技徽章。
*
* 用法:node scripts/gen-100-bot-variants.js
*/
'use strict';
const fs = require('fs');
const path = require('path');
const vm = require('vm');
const ROOT = path.resolve(__dirname, '..');
const OUT_DIR = path.join(ROOT, 'output', 'agent-avatars-png');
// —— 1. 加载技能环境 ——
global.window = global;
function load(rel) { vm.runInThisContext(fs.readFileSync(path.join(ROOT, rel), 'utf8'), { filename: rel }); }
load('js/data/icons.js'); // HGB_Icons
load('js/data/industries.js'); // HGB_Industries
load('js/data/styles.js'); // HGB_Styles
load('js/data/palettes.js'); // HGB_Palettes
load('js/data/fonts.js'); // HGB_Fonts
load('js/data/decorations.js'); // HGB_Decorations
load('js/skills/core.js'); // HGB_Skills (const)
load('js/skills/color.js'); // palette-build / gradient-suggest 等
load('js/data/sources/lucide.js'); // HGB_Source_lucide(徽章图标)
load('js/data/sources/dicebear-bottts.js');
load('js/core/dicebear.js'); // HGB_Dicebear
const Skills = HGB_Skills;
console.log('✅ 技能已加载,技能数:', Skills.all().length);
// —— 2. 色板:6 行业 + 4 内置色板 = 10 ——
const inds = HGB_Industries.slice(0, 6);
const extraPals = HGB_Palettes.slice(0, 4);
const palettes = [
...inds.map(ind => {
const r = Skills.run('palette-build', { desc: '科技感 ' + ind.name, industry: ind.id });
return { industry: ind.id, indName: ind.name, palette: r.palette || { primary: '#2563eb', secondary: '#3b82f6', accent: '#60a5fa' } };
}),
...extraPals.map((p, i) => ({
industry: 'palette-' + i, indName: p.name,
palette: { primary: p.colors[0], secondary: p.colors[1] || p.colors[0], accent: p.colors[2] || p.colors[1] || p.colors[0] }
}))
];
console.log('✅ 色板数:', palettes.length, '→', palettes.map(p => p.indName).join(' / '));
// —— 3. HSL 工具(渐变派生)——
function hexToHsl(hex) {
let c = String(hex).replace('#', ''); if (c.length === 3) c = c.split('').map(x => x + x).join('');
const r = parseInt(c.slice(0, 2), 16) / 255, g = parseInt(c.slice(2, 4), 16) / 255, b = parseInt(c.slice(4, 6), 16) / 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b); let h = 0, s = 0, l = (max + min) / 2;
if (max !== min) {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) * 60;
else if (max === g) h = ((b - r) / d + 2) * 60;
else h = ((r - g) / d + 4) * 60;
}
return { h, s: s * 100, l: l * 100 };
}
function hslToHex(h, s, l) {
s /= 100; l /= 100;
const k = n => (n + h / 30) % 12;
const a = s * Math.min(l, 1 - l);
const f = n => { const x = k(n); return Math.round(255 * (l - a * Math.max(-1, Math.min(x - 3, Math.min(9 - x, 1))))); };
return '#' + [f(0), f(8), f(4)].map(x => x.toString(16).padStart(2, '0')).join('');
}
function adjust(hex, dl) { const { h, s, l } = hexToHsl(hex); return hslToHex(h, s, Math.max(2, Math.min(98, l + dl))); }
// —— 4. 徽章图标(lucide 科技图标 10 个)——
const lucideIcons = window['HGB_Source_lucide'].icons;
const badgeNames = ['bot', 'sparkles', 'cpu', 'radar', 'satellite', 'zap', 'shield', 'atom', 'circuit-board', 'brain-circuit'];
const badges = badgeNames.map(n => {
const ic = lucideIcons.find(x => x.name === n);
return { name: n, body: ic ? ic.svg : '