/** * 🤖 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 : '' }; }); // —— 5. 生成 100 个变体 —— const MODES = [ { name: '主色渐变', f: (p, g) => [p.primary, p.secondary] }, { name: '点缀渐变', f: (p, g) => [p.primary, p.accent] }, { name: '深邃渐变', f: (p, g) => [p.primary, adjust(p.primary, -30)] }, { name: '互补渐变', f: (p, g) => [p.primary, hslToHex((g.h + 180) % 360, 60, 50)] }, { name: '邻近渐变', f: (p, g) => [p.primary, hslToHex((g.h + 30) % 360, 60, 50)] } ]; const cards = []; let idx = 1; for (let pi = 0; pi < palettes.length; pi++) { const P = palettes[pi]; const gradInfo = Skills.run('gradient-suggest', { color: P.palette.primary }); for (let mi = 0; mi < MODES.length; mi++) { for (let bi = 0; bi < 2; bi++) { const badge = badges[(pi * 5 + mi * 2 + bi) % badges.length]; const [c1, c2] = MODES[mi].f(P.palette, gradInfo); const seed = 'agent-' + idx; const { svg } = HGB_Dicebear.renderAvatar('bottts', seed); // 包裹:渐变圆底 + bottts + 亮环 + 科技徽章 const wrapped = ` ${svg.replace(/^]*>|<\/svg>$/g, '')} ${badge.body} `; cards.push({ idx, pi, mi, bi, seed, svg: wrapped, name: `${P.indName} · ${MODES[mi].name} · ${badge.name}` }); idx++; } } } console.log('✅ 变体数:', cards.length); // —— 6. 唯一性校验 —— const { createHash } = require('crypto'); const hashes = new Set(); let dup = 0; for (const c of cards) { const h = createHash('md5').update(c.svg).digest('hex'); if (hashes.has(h)) dup++; hashes.add(h); } console.log('唯一性:' + (cards.length - dup) + '/' + cards.length + (dup ? '(⚠️ ' + dup + ' 重复)' : ' ✅')); // —— 7. 预览页 —— let cardHtml = ''; for (const c of cards) { const n = String(c.idx).padStart(2, '0'); cardHtml += `\n`; } const html = ` 🎨 100 个 bottts 技能变换变体 · 勾选后批量渲染

🎨 100 个 bottts 技能变换变体

技能库组合变换:palette-build 色板 × gradient-suggest 渐变 × lucide 科技徽章 · 点击勾选(默认全选)· 复制编号交渲染
已选 0/100
${cardHtml}
`; fs.writeFileSync(path.join(OUT_DIR, 'preview-100-variants.html'), html, 'utf8'); console.log('🎉 已生成 100 个技能变换变体 → output/agent-avatars-png/preview-100-variants.html');