From 9e900040e0dc5e832d5ff7fabdcde84f298a5ee0 Mon Sep 17 00:00:00 2001 From: sina_sajjadi Date: Tue, 7 Jul 2026 09:14:49 +0330 Subject: [PATCH] feat: implement node property cleaning, relative positioning, and extended metadata extraction during traversal --- code.ts | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 7 deletions(-) diff --git a/code.ts b/code.ts index 159db28..4f1edab 100644 --- a/code.ts +++ b/code.ts @@ -60,7 +60,48 @@ async function runExport() { return false; } - async function traverseNode(node: SceneNode, depth: number, currentFolderPath: string) { + function cleanMixed(val: any): any { + if (val === figma.mixed) return "MIXED"; + return val; + } + + function cleanRawProps(props: any): any { + const cleaned: any = {}; + for (const key of Object.keys(props)) { + const val = props[key]; + if (val === null || val === undefined) { + continue; + } + if (Array.isArray(val) && val.length === 0) { + continue; + } + if (key === "opacity" && val === 1) continue; + if (key === "rotation" && val === 0) continue; + if (key === "cornerRadius" && val === 0) continue; + if (key === "topLeftRadius" && val === 0) continue; + if (key === "topRightRadius" && val === 0) continue; + if (key === "bottomLeftRadius" && val === 0) continue; + if (key === "bottomRightRadius" && val === 0) continue; + if (key === "paddingLeft" && val === 0) continue; + if (key === "paddingRight" && val === 0) continue; + if (key === "paddingTop" && val === 0) continue; + if (key === "paddingBottom" && val === 0) continue; + if (key === "itemSpacing" && val === 0) continue; + if (key === "textCase" && val === "ORIGINAL") continue; + if (key === "textDecoration" && val === "NONE") continue; + + if (key === "lineHeight" && typeof val === "object" && val.unit === "AUTO") { + continue; + } + if (key === "letterSpacing" && typeof val === "object" && val.value === 0) { + continue; + } + cleaned[key] = val; + } + return cleaned; + } + + async function traverseNode(node: SceneNode, depth: number, currentFolderPath: string, rootNode: SceneNode) { allFolders.push(currentFolderPath); const indent = " ".repeat(depth); @@ -70,12 +111,22 @@ async function runExport() { markdownOutput += `${indent} - Text Content: ${node.characters}\n`; } - const rawProps = { + let localX = node.x; + let localY = node.y; + if (node.id === rootNode.id) { + localX = 0; + localY = 0; + } else if (node.absoluteBoundingBox && rootNode.absoluteBoundingBox) { + localX = node.absoluteBoundingBox.x - rootNode.absoluteBoundingBox.x; + localY = node.absoluteBoundingBox.y - rootNode.absoluteBoundingBox.y; + } + + const rawProps: any = { id: node.id, width: node.width, height: node.height, - x: node.x, - y: node.y, + x: localX, + y: localY, fills: "fills" in node ? node.fills : null, strokes: "strokes" in node ? node.strokes : null, effects: "effects" in node ? node.effects : null, @@ -83,7 +134,40 @@ async function runExport() { rotation: "rotation" in node ? node.rotation : null }; - markdownOutput += `${indent} - Raw Properties: ${JSON.stringify(rawProps)}\n`; + if (node.type === "TEXT") { + rawProps.fontSize = cleanMixed(node.fontSize); + rawProps.fontName = cleanMixed(node.fontName); + rawProps.lineHeight = cleanMixed(node.lineHeight); + rawProps.letterSpacing = cleanMixed(node.letterSpacing); + rawProps.textAlignHorizontal = cleanMixed(node.textAlignHorizontal); + rawProps.textAlignVertical = cleanMixed(node.textAlignVertical); + rawProps.textCase = cleanMixed(node.textCase); + rawProps.textDecoration = cleanMixed(node.textDecoration); + } + + if ("layoutMode" in node) { + rawProps.layoutMode = node.layoutMode; + rawProps.primaryAxisAlignItems = node.primaryAxisAlignItems; + rawProps.counterAxisAlignItems = node.counterAxisAlignItems; + rawProps.paddingLeft = node.paddingLeft; + rawProps.paddingRight = node.paddingRight; + rawProps.paddingTop = node.paddingTop; + rawProps.paddingBottom = node.paddingBottom; + rawProps.itemSpacing = node.itemSpacing; + } + + if ("cornerRadius" in node) { + rawProps.cornerRadius = cleanMixed(node.cornerRadius); + } + + if ("topLeftRadius" in node) { + rawProps.topLeftRadius = cleanMixed(node.topLeftRadius); + rawProps.topRightRadius = cleanMixed(node.topRightRadius); + rawProps.bottomLeftRadius = cleanMixed(node.bottomLeftRadius); + rawProps.bottomRightRadius = cleanMixed(node.bottomRightRadius); + } + + markdownOutput += `${indent} - Raw Properties: ${JSON.stringify(cleanRawProps(rawProps))}\n`; const isTextNode = node.type === "TEXT"; @@ -107,14 +191,14 @@ async function runExport() { for (const child of node.children) { const childSafeName = getSafeName(child); const childFolderPath = `${currentFolderPath}/children/${childSafeName}`; - await traverseNode(child, depth + 1, childFolderPath); + await traverseNode(child, depth + 1, childFolderPath, rootNode); } } } for (const node of selection) { const safeFolderName = getSafeName(node); - await traverseNode(node, 0, safeFolderName); + await traverseNode(node, 0, safeFolderName, node); } figma.ui.postMessage({