| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- // 批量分析 9 个画板 FigmaJSON,输出汇总 JSON + 紧凑表格
- 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 summary = []
- for (const file of files) {
- const j = JSON.parse(readFileSync(join(dir, file), 'utf8'))
- const ab = j.artboard
- const fonts = new Map()
- const slices = []
- const images = []
- const colors = new Map()
- const textSamples = []
- let nodeCount = 0
- function walk(node, path) {
- nodeCount += 1
- const p = path ? `${path}/${node.name}` : node.name
- if (node.type === 'textLayer' && node.text) {
- const f = node.text.style?.font
- if (f && f.name) {
- const key = `${f.name} | ${f.size}px | w${f.fontWeight ?? ''}`
- fonts.set(key, (fonts.get(key) || 0) + 1)
- }
- if (textSamples.length < 12 && node.text.style?.content) textSamples.push(String(node.text.style.content).slice(0, 20))
- for (const r of node.text.runs || []) {
- const rf = r.font || r.style?.font
- if (rf && rf.name) {
- const key = `${rf.name} | ${rf.size}px | w${rf.fontWeight ?? ''}`
- fonts.set(key, (fonts.get(key) || 0) + 1)
- }
- }
- }
- if (node.hasExportImage || node.hasExportDDSImage) {
- slices.push({ path: p, w: Math.round(node.frame?.width ?? 0), h: Math.round(node.frame?.height ?? 0), dds: !!node.hasExportDDSImage })
- }
- for (const f of node.style?.fills || []) {
- if (f.type === 'color' && f.color) {
- const c = hex(f.color)
- colors.set(c, (colors.get(c) || 0) + 1)
- }
- const url = f.ddsImage?.imageUrl || f.image?.imageUrl || f.imageUrl
- if (f.type === 'image' && url) {
- images.push({ path: p, url, w: Math.round(node.frame?.width ?? 0), h: Math.round(node.frame?.height ?? 0) })
- }
- }
- for (const ch of node.layers || []) walk(ch, p)
- }
- for (const l of ab.layers || []) walk(l, '')
- summary.push({
- file,
- artboard: ab.name,
- frame: { w: ab.frame.width, h: ab.frame.height },
- rootExportFlags: { hasExportImage: ab.hasExportImage, hasExportDDSImage: ab.hasExportDDSImage },
- nodeCount,
- sliceCount: slices.length,
- ddsSliceCount: slices.filter((s) => s.dds).length,
- topSlices: slices.filter((s) => s.w >= 24 && s.h >= 24).slice(0, 12),
- fonts: [...fonts.entries()].sort((a, b) => b[1] - a[1]).map(([k, v]) => ({ font: k, count: v })),
- imageRefCount: images.length,
- images: images.map((i) => ({ path: i.path, w: i.w, h: i.h, url: i.url })),
- topColors: [...colors.entries()].sort((a, b) => b[1] - a[1]).slice(0, 16).map(([k, v]) => ({ color: k, count: v })),
- assetFiles: j.assets || [],
- textSamples,
- })
- }
- writeFileSync(join(dir, 'analysis-summary.json'), JSON.stringify(summary, null, 1))
- console.log('artboard | frame | nodes | slices(dds) | fonts | imgFills | colors | assets')
- for (const s of summary) {
- console.log(
- `${s.artboard} | ${s.frame.w}x${s.frame.h} | ${s.nodeCount} | ${s.sliceCount}(${s.ddsSliceCount}) | ${s.fonts.length} | ${s.imageRefCount} | ${s.topColors.length} | ${s.assetFiles.length}`,
- )
- }
- const allFonts = new Set()
- for (const s of summary) for (const f of s.fonts) allFonts.add(f.font.split(' | ')[0])
- console.log('\nall font families:', [...allFonts].join(' / '))
|