UNPKG

figma-restoration-tools

Version:

Professional Figma Component Restoration Kit - MCP tools with snapDOM-powered high-quality screenshots, intelligent shadow detection, and smart debugging for Vue component restoration. Includes figma_compare and snapdom_screenshot tools.

366 lines (311 loc) 9.29 kB
import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); /** * 智能Figma数据分析器 - 基于真实JSON数据 */ class SmartFigmaAnalyzer { constructor(figmaData) { this.figmaData = figmaData; this.globalStyles = figmaData.globalVars?.styles || {}; this.materials = []; this.icons = []; this.layout = null; } /** * 解析样式引用 */ resolveStyle(styleRef) { if (!styleRef || typeof styleRef !== 'string') return null; return this.globalStyles[styleRef] || null; } /** * 解析填充颜色 */ resolveFill(fillRef) { const fill = this.resolveStyle(fillRef); if (!fill) return null; if (Array.isArray(fill)) { return fill[0]; // 取第一个颜色 } return fill; } /** * 解析布局样式 */ resolveLayout(layoutRef) { return this.resolveStyle(layoutRef); } /** * 解析文本样式 */ resolveTextStyle(styleRef) { return this.resolveStyle(styleRef); } /** * 检查节点是否需要下载素材 */ needsImageDownload(node) { // 检查是否有图片填充 if (node.fills) { const fill = this.resolveFill(node.fills); if (fill && typeof fill === 'object' && fill.type === 'IMAGE') { return true; } } // 检查是否是图标组件 if (node.type === 'INSTANCE' && node.componentId) { // 根据组件名称判断是否是图标 const name = node.name?.toLowerCase() || ''; if (name.includes('icon') || name.includes('ic_')) { return true; } } return false; } /** * 分析节点并转换为Flex布局 */ analyzeNode(node, parentLayout = null) { const layout = this.resolveLayout(node.layout); const textStyle = this.resolveTextStyle(node.textStyle); const fill = this.resolveFill(node.fills); const analyzedNode = { id: node.id, name: node.name, type: node.type, text: node.text, layout: layout, textStyle: textStyle, fill: fill, borderRadius: node.borderRadius, boundingBox: node.boundingBox, styles: this.generateCSSStyles(node, layout, textStyle, fill), children: [] }; // 检查是否需要下载素材 if (this.needsImageDownload(node)) { const material = { nodeId: node.id, name: node.name, type: node.type, componentId: node.componentId, isIcon: this.isIconNode(node) }; if (material.isIcon) { this.icons.push(material); } else { this.materials.push(material); } } // 递归分析子节点 if (node.children && node.children.length > 0) { for (const child of node.children) { const childNode = this.analyzeNode(child, layout); if (childNode) { analyzedNode.children.push(childNode); } } } return analyzedNode; } /** * 判断是否是图标节点 */ isIconNode(node) { const name = node.name?.toLowerCase() || ''; // 根据名称判断 if (name.includes('icon') || name.includes('ic_') || name.includes('close')) { return true; } // 根据尺寸判断 if (node.boundingBox) { const { width, height } = node.boundingBox; return width <= 50 && height <= 50; } // 根据类型判断 if (node.type === 'VECTOR' || node.type === 'GROUP') { return true; } return false; } /** * 生成CSS样式 */ generateCSSStyles(node, layout, textStyle, fill) { const styles = {}; // 布局样式 if (layout) { if (layout.mode === 'row' || layout.mode === 'column') { styles.display = 'flex'; styles.flexDirection = layout.mode; if (layout.justifyContent) { styles.justifyContent = this.mapJustifyContent(layout.justifyContent); } if (layout.alignItems) { styles.alignItems = this.mapAlignItems(layout.alignItems); } if (layout.gap) { styles.gap = typeof layout.gap === 'string' ? layout.gap : `${layout.gap}px`; } if (layout.padding) { styles.padding = layout.padding; } } // 尺寸 if (layout.dimensions) { if (layout.dimensions.width) { styles.width = `${layout.dimensions.width}px`; } if (layout.dimensions.height) { styles.height = `${layout.dimensions.height}px`; } } // 定位 if (layout.position === 'absolute') { styles.position = 'absolute'; if (layout.locationRelativeToParent) { const loc = layout.locationRelativeToParent; if (loc.x !== undefined) styles.left = `${loc.x}px`; if (loc.y !== undefined) styles.top = `${loc.y}px`; } } } // 文本样式 if (textStyle) { if (textStyle.fontFamily) styles.fontFamily = `'${textStyle.fontFamily}', sans-serif`; if (textStyle.fontWeight) styles.fontWeight = textStyle.fontWeight; if (textStyle.fontSize) styles.fontSize = `${textStyle.fontSize}px`; if (textStyle.lineHeight) styles.lineHeight = textStyle.lineHeight; if (textStyle.textAlignHorizontal) { styles.textAlign = textStyle.textAlignHorizontal.toLowerCase(); } } // 填充颜色 if (fill) { if (typeof fill === 'string') { styles.backgroundColor = fill; } else if (Array.isArray(fill)) { styles.backgroundColor = fill[0]; } } // 边框圆角 if (node.borderRadius) { styles.borderRadius = node.borderRadius; } // 边框 if (node.strokes) { const stroke = this.resolveStyle(node.strokes); if (stroke && stroke.colors && stroke.strokeWeight) { styles.border = `${stroke.strokeWeight} solid ${stroke.colors[0]}`; } } // 阴影 if (node.effects) { const effect = this.resolveStyle(node.effects); if (effect && effect.boxShadow) { styles.boxShadow = effect.boxShadow; } } return styles; } /** * 映射justifyContent值 */ mapJustifyContent(value) { const mapping = { 'center': 'center', 'flex-start': 'flex-start', 'flex-end': 'flex-end', 'space-between': 'space-between', 'space-around': 'space-around' }; return mapping[value] || value; } /** * 映射alignItems值 */ mapAlignItems(value) { const mapping = { 'center': 'center', 'flex-start': 'flex-start', 'flex-end': 'flex-end', 'stretch': 'stretch' }; return mapping[value] || value; } /** * 执行完整分析 */ analyze() { console.log('🔍 开始智能分析Figma数据...'); // 分析根节点 const rootNode = this.figmaData.nodes?.[0]; if (!rootNode) { throw new Error('未找到根节点'); } this.layout = this.analyzeNode(rootNode); console.log(`📊 分析完成:`); console.log(` 素材: ${this.materials.length} 个`); console.log(` 图标: ${this.icons.length} 个`); console.log(` 布局节点: ${this.countNodes(this.layout)} 个`); // 打印素材和图标信息 if (this.materials.length > 0) { console.log('\n📷 需要下载的素材:'); this.materials.forEach(m => console.log(` - ${m.name} (${m.nodeId})`)); } if (this.icons.length > 0) { console.log('\n🎨 需要下载的图标:'); this.icons.forEach(i => console.log(` - ${i.name} (${i.nodeId})`)); } return { materials: this.materials, icons: this.icons, layout: this.layout, globalStyles: this.globalStyles }; } /** * 计算节点数量 */ countNodes(node) { if (!node) return 0; let count = 1; if (node.children) { for (const child of node.children) { count += this.countNodes(child); } } return count; } } /** * 分析指定组件 */ async function analyzeComponent(componentName) { console.log(`\n🎯 分析组件: ${componentName}`); // 读取Figma数据 const dataPath = path.join(__dirname, '../results', componentName, 'figma-data.json'); if (!fs.existsSync(dataPath)) { console.error(`❌ 未找到Figma数据文件: ${dataPath}`); return null; } const figmaData = JSON.parse(fs.readFileSync(dataPath, 'utf8')); // 创建分析器并分析 const analyzer = new SmartFigmaAnalyzer(figmaData); const analysis = analyzer.analyze(); // 保存分析结果 const outputDir = path.join(__dirname, '../results', componentName); const analysisPath = path.join(outputDir, 'smart-analysis.json'); fs.writeFileSync(analysisPath, JSON.stringify(analysis, null, 2)); console.log(`💾 智能分析结果已保存: ${analysisPath}`); return analysis; } // 命令行工具 if (import.meta.url === `file://${process.argv[1]}`) { const componentName = process.argv[2] || 'ModalRemoveMember'; analyzeComponent(componentName).catch(console.error); } export { SmartFigmaAnalyzer, analyzeComponent };