// 票 15 取证:主画板候选(Desktop-6 总体概览)与全稿对比 —— 画板底色、面板填充、标题/正文文字色 import { readFileSync } from 'node:fs' const dir = '.scratch/lanhu-cockpit-asset-replacement/research/raw-v2' const hex = (c) => `#${[c.r, c.g, c.b].map((v) => Math.round(v * 255).toString(16).padStart(2, '0')).join('')}` const alpha = (c) => (c.a === undefined || c.a > 0.999 ? '' : `@${c.a.toFixed(2)}`) function load(n) { return JSON.parse(readFileSync(`${dir}/desktop-${String(n).padStart(2, '0')}.json`, 'utf8')).artboard } // 1. 画板根节点自身的填充(= 页面底色) for (const n of [2, 3, 4, 5, 6, 7, 8, 9, 10]) { const ab = load(n) const rootFills = (ab.style?.fills || []).filter((f) => f.type === 'color').map((f) => hex(f.color) + alpha(f.color)) const big = [] ;(function walk(node) { const w = node.frame?.width ?? 0 const h = node.frame?.height ?? 0 if (w >= 200 && h >= 100) { for (const f of node.style?.fills || []) { if (f.type === 'color') big.push({ n: node.name, w: Math.round(w), h: Math.round(h), c: hex(f.color) + alpha(f.color) }) } } for (const c of node.layers || []) walk(c) })(ab) console.log(`\n=== ${ab.name} ${ab.frame.width}x${ab.frame.height}`) console.log(` 根填充: ${rootFills.join(', ') || '(无)'}`) console.log(` 大色块(≥200x100) 前 14: ${big.slice(0, 14).map((b) => `${b.n} ${b.w}x${b.h} ${b.c}`).join(' | ')}`) } // 2. 文字层级:按字号聚合文字颜色(看设计稿的文字色阶) const bySize = new Map() for (const n of [2, 3, 4, 5, 6, 7, 8, 9, 10]) { const ab = load(n) ;(function walk(node) { if (node.type === 'textLayer' && node.text?.style?.font?.size) { const size = node.text.style.font.size const c = node.text.style.color if (c) { const key = `${size}px ${hex(c)}${alpha(c)}` const cur = bySize.get(key) || { count: 0, sample: '' } cur.count += 1 if (!cur.sample) cur.sample = String(node.text.style.content || '').slice(0, 12) bySize.set(key, cur) } } for (const ch of node.layers || []) walk(ch) })(ab) } console.log('\n=== 文字:字号×颜色 Top 30') console.log( [...bySize.entries()] .sort((a, b) => b[1].count - a[1].count) .slice(0, 30) .map(([k, v]) => `${k} ×${v.count} 例:${v.sample}`) .join('\n'), ) // 3. 面板标题候选:文字内容含「总览/概览/预警/监测」的节点,取其字号与颜色 console.log('\n=== 标题类文字样本(内容含 总览|概览|预警|监测|档案)') const seen = new Set() for (const n of [6, 2, 5]) { const ab = load(n) ;(function walk(node) { if (node.type === 'textLayer' && node.text?.style?.content) { const t = String(node.text.style.content) if (/总览|概览|预警|监测|档案|信息|分析/.test(t) && !seen.has(`${ab.name}:${t}`)) { seen.add(`${ab.name}:${t}`) const f = node.text.style.font const c = node.text.style.color console.log(` [${ab.name}] "${t}" ${f.name} ${f.size}px w${f.fontWeight} ${c ? hex(c) + alpha(c) : '-'} frame=${Math.round(node.frame.width)}x${Math.round(node.frame.height)}`) } } for (const ch of node.layers || []) walk(ch) })(ab) }