// 输出画板分类所需信息:顶层图层、文本样本、图片填充、字体与色板汇总 import { readFileSync, writeFileSync, readdirSync } from 'node:fs' import { join } from 'node:path' const dir = '.scratch/lanhu-cockpit-asset-replacement/research/raw' const files = readdirSync(dir).filter((f) => /^desktop-\d+\.json$/.test(f)).sort() function hex(c) { const h = (v) => Math.round(v * 255).toString(16).padStart(2, '0') return `#${h(c.r)}${h(c.g)}${h(c.b)}` } const detail = [] const globalColors = new Map() const globalFonts = new Map() let totalAssets = 0 let totalImageFills = 0 let flagImage = 0 let flagDds = 0 for (const file of files) { const j = JSON.parse(readFileSync(join(dir, file), 'utf8')) const ab = j.artboard const texts = [] const images = [] const fonts = new Map() function walk(node) { if (node.hasExportImage) flagImage += 1 if (node.hasExportDDSImage) flagDds += 1 if (node.type === 'textLayer' && node.text?.style?.content && texts.length < 40) { texts.push(String(node.text.style.content).slice(0, 24)) } if (node.type === 'textLayer' && node.text?.style?.font?.name) { const fam = node.text.style.font.name fonts.set(fam, (fonts.get(fam) || 0) + 1) globalFonts.set(fam, (globalFonts.get(fam) || 0) + 1) } for (const f of node.style?.fills || []) { if (f.type === 'color' && f.color) { const c = hex(f.color) globalColors.set(c, (globalColors.get(c) || 0) + 1) } const url = f.ddsImage?.imageUrl || f.image?.imageUrl || f.imageUrl if (f.type === 'image' && url) { images.push({ name: node.name, path: '', w: Math.round(node.frame?.width ?? 0), h: Math.round(node.frame?.height ?? 0), url }) } } for (const ch of node.layers || []) walk(ch) } for (const l of ab.layers || []) walk(l) totalAssets += (j.assets || []).length totalImageFills += images.length detail.push({ file, artboard: ab.name, frame: { w: ab.frame.width, h: ab.frame.height }, topLayers: (ab.layers || []).map((l) => `${l.type}:${l.name}`), texts, images, fonts: [...fonts.entries()].sort((a, b) => b[1] - a[1]), assetFiles: j.assets || [], }) } writeFileSync(join(dir, 'inventory-detail.json'), JSON.stringify(detail, null, 1)) for (const d of detail) { console.log(`\n=== ${d.artboard} (${d.frame.w}x${d.frame.h})`) console.log(' top:', d.topLayers.join(', ').slice(0, 300)) console.log(' texts:', d.texts.slice(0, 26).join(' | ').slice(0, 320)) console.log(' images:', d.images.map((i) => `${i.name}(${i.w}x${i.h})`).join(', ').slice(0, 260)) console.log(' fonts:', d.fonts.map(([f, c]) => `${f}×${c}`).join(', ').slice(0, 260)) } console.log('\n=== GLOBAL') console.log('assets files:', totalAssets, '| image fills:', totalImageFills, '| flagImage:', flagImage, '| flagDds:', flagDds) console.log('fonts:', [...globalFonts.entries()].sort((a, b) => b[1] - a[1]).map(([f, c]) => `${f}×${c}`).join(', ')) console.log('palette:', [...globalColors.entries()].sort((a, b) => b[1] - a[1]).slice(0, 40).map(([c, n]) => `${c}×${n}`).join(' '))