#!/usr/bin/env node // scripts/_rebuild-avatars.mjs(临时脚本,_ 前缀不入镜像) // 从 DrawLogo 的 10 种风格头像中各取前 10 个,重构追光AI 用户头像库 // - 10 风格 × 10 个 = 100 个 SVG → public/avatars/ // - 重写 public/avatars/manifest.json // - 重写 src/lib/avatar-library.ts(AVATAR_LIBRARY) // 用法: node scripts/_rebuild-avatars.mjs import { readFileSync, writeFileSync, copyFileSync, existsSync } from "fs"; import { resolve, dirname } from "path"; import { fileURLToPath } from "url"; const __dirname = dirname(fileURLToPath(import.meta.url)); const ZG_ROOT = resolve(__dirname, ".."); const DRAW_SRC = resolve("D:/SGP_KF/DrawLogo/output/agent-avatars-png/avatars"); const AVATAR_DIR = resolve(ZG_ROOT, "public/avatars"); const MANIFEST_PATH = resolve(AVATAR_DIR, "manifest.json"); const LIB_TS_PATH = resolve(ZG_ROOT, "src/lib/avatar-library.ts"); // 12 种风格(rings/shapes 渲染杂乱已移除,新增 open-peeps/lorelei/big-smile/croodles) const STYLES = [ { id: "bottts", name: "科技机器人" }, { id: "bottts-neutral", name: "极简机器人" }, { id: "micah", name: "米迦人物" }, { id: "pixel-art", name: "像素风" }, { id: "fun-emoji", name: "趣味表情" }, { id: "avataaars", name: "阿凡达" }, { id: "notionists", name: "Notion 风" }, { id: "adventurer", name: "冒险家" }, { id: "open-peeps", name: "简笔人物" }, { id: "lorelei", name: "人物插画" }, { id: "big-smile", name: "笑脸人物" }, { id: "croodles", name: "涂鸦人物" }, ]; const PER_STYLE = 10; // 每种风格 10 个 // 读取 DrawLogo meta.json 获取每种风格渐变颜色 const metaColors = {}; try { const meta = JSON.parse(readFileSync(resolve(DRAW_SRC, "meta.json"), "utf8")); for (const s of meta.styles || []) metaColors[s.id] = [s.c1, s.c2]; } catch { console.log("⚠️ 未读到 meta.json,颜色使用默认值"); } const avatars = []; for (const st of STYLES) { for (let i = 1; i <= PER_STYLE; i++) { const n = String(i).padStart(3, "0"); const srcFile = resolve(DRAW_SRC, st.id, `${st.id}-${n}.svg`); if (!existsSync(srcFile)) { console.log(`❌ 缺少源文件: ${srcFile}`); process.exit(1); } const id = `${st.id}_${n}`; const destFile = resolve(AVATAR_DIR, `${id}.svg`); copyFileSync(srcFile, destFile); // Next.js image-optimizer 按 magic bytes 识别类型,SVG 必须以 \n${raw}`); } avatars.push({ id, theme: st.id, url: `/avatars/${id}.svg`, colors: metaColors[st.id] || ["#334155", "#64748b"], shape: "circle", }); } } if (avatars.length !== STYLES.length * PER_STYLE) { console.log(`❌ 期望 ${STYLES.length * PER_STYLE} 个,实际 ${avatars.length} 个`); process.exit(1); } // —— manifest.json —— const manifest = { generatedAt: new Date().toISOString(), total: avatars.length, categories: {}, themes: {}, avatars, }; for (const st of STYLES) { const count = avatars.filter((a) => a.theme === st.id).length; manifest.categories[st.id] = count; manifest.themes[st.id] = count; } writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2), "utf8"); // —— src/lib/avatar-library.ts —— const idSet = avatars.map((a) => `"${a.id}"`); const libLines = [ "// 头像库100个头像ID列表", "// 由 scripts/_rebuild-avatars.mjs 生成(DrawLogo 10 风格 × 10)", `// 自动生成时间: ${new Date().toISOString().slice(0, 10)}`, "", "export const AVATAR_LIBRARY = [", ]; for (const st of STYLES) { const group = idSet.filter((id) => id.startsWith(`"${st.id}_`)); libLines.push(` // === ${st.name} (${group.length}) ===`); libLines.push(` ${group.join(",")},`); } libLines.push("];"); writeFileSync(LIB_TS_PATH, libLines.join("\n"), "utf8"); console.log(`✅ 完成: ${avatars.length} 个头像 → public/avatars/`); console.log(` 清单: public/avatars/manifest.json`); console.log(` 库表: src/lib/avatar-library.ts`); for (const st of STYLES) console.log(` ${st.id}(${st.name}): ${manifest.categories[st.id]} 个`);