From 78f4d050d8d7b14abe41dc4eb3009de5b51942b6 Mon Sep 17 00:00:00 2001 From: szecket Date: Wed, 16 Apr 2025 21:36:16 -0400 Subject: [PATCH 1/4] adjusted colors for snippet variables --- tools/figma-inspector/src/main.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/figma-inspector/src/main.tsx b/tools/figma-inspector/src/main.tsx index 9fbbbdefdd9..b6df78743ee 100644 --- a/tools/figma-inspector/src/main.tsx +++ b/tools/figma-inspector/src/main.tsx @@ -387,7 +387,7 @@ export const App = () => { handleExportClick(); } }} // Keyboard accessibility - style={menuItemStyle} + style={{ ...menuItemStyle, padding: "8px 12px" }} onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = menuItemHoverStyle.backgroundColor!) @@ -396,7 +396,7 @@ export const App = () => { (e.currentTarget.style.backgroundColor = "") } > - {exportsAreCurrent ? "Export Again" : "Export Now"} + Export Collections From 0f2dfc12edb9076918e81455f9ac4a52d7b642ca Mon Sep 17 00:00:00 2001 From: szecket Date: Thu, 17 Apr 2025 10:46:23 -0400 Subject: [PATCH 2/4] attempting rudimentary recursive snippet --- .../backend/utils/property-parsing.ts | 158 ++++++++++++++++-- 1 file changed, 148 insertions(+), 10 deletions(-) diff --git a/tools/figma-inspector/backend/utils/property-parsing.ts b/tools/figma-inspector/backend/utils/property-parsing.ts index 6ad77a966f7..462f89d4315 100644 --- a/tools/figma-inspector/backend/utils/property-parsing.ts +++ b/tools/figma-inspector/backend/utils/property-parsing.ts @@ -502,7 +502,7 @@ export function generateUnsupportedNodeSnippet(sceneNode: SceneNode): string { export async function generateRectangleSnippet( sceneNode: SceneNode, ): Promise { - const properties: string[] = []; + const parentProperties: string[] = []; // Renamed for clarity if ("boundVariables" in sceneNode) { console.log( "[generateRectangleSnippet] Inspecting sceneNode.boundVariables:", @@ -517,6 +517,64 @@ export async function generateRectangleSnippet( // --- Add try...catch around each property's logic --- try { switch (property) { + case "x": + const boundXVarId = (sceneNode as any).boundVariables?.x + ?.id; // Assume direct object binding + let xValue: string | null = null; + if (boundXVarId) { + xValue = await getVariablePathString(boundXVarId); + console.log( + `[generateTextSnippet] x: Using variable path: ${xValue}`, + ); + } + if ( + !xValue && + "x" in sceneNode && + typeof sceneNode.x === "number" + ) { + const x = roundNumber(sceneNode.x); + if (x !== null) { + // roundNumber returns null for 0 + xValue = `${x}px`; + console.log( + `[generateTextSnippet] x: Using numeric value: ${xValue}`, + ); + } + } + if (xValue) { + parentProperties.push(`${indentation}x: ${xValue};`); + } + break; + // --- Add case for y --- + case "y": + const boundYVarId = (sceneNode as any).boundVariables?.y + ?.id; // Assume direct object binding + let yValue: string | null = null; + if (boundYVarId) { + yValue = await getVariablePathString(boundYVarId); + console.log( + `[generateTextSnippet] y: Using variable path: ${yValue}`, + ); + } + if ( + !yValue && + "y" in sceneNode && + typeof sceneNode.y === "number" + ) { + const y = roundNumber(sceneNode.y); + if (y !== null) { + // roundNumber returns null for 0 + yValue = `${y}px`; + console.log( + `[generateTextSnippet] y: Using numeric value: ${yValue}`, + ); + } + } + if (yValue) { + parentProperties.push(`${indentation}y: ${yValue};`); + } + break; + case "width": const boundWidthVarId = (sceneNode as any).boundVariables ?.width?.id; @@ -532,7 +590,7 @@ export async function generateRectangleSnippet( } } if (widthValue) { - properties.push(`${indentation}width: ${widthValue};`); + parentProperties.push(`${indentation}width: ${widthValue};`); } break; case "height": @@ -550,7 +608,7 @@ export async function generateRectangleSnippet( } } if (heightValue) { - properties.push( + parentProperties.push( `${indentation}height: ${heightValue};`, ); } @@ -574,14 +632,14 @@ export async function generateRectangleSnippet( fillValue = getBrush(firstFill); } if (fillValue) { - properties.push( + parentProperties.push( `${indentation}background: ${fillValue};`, ); } } else { const brush = getBrush(firstFill); if (brush) { - properties.push( + parentProperties.push( `${indentation}background: ${brush};`, ); } @@ -590,7 +648,7 @@ export async function generateRectangleSnippet( break; case "opacity": if ("opacity" in sceneNode && sceneNode.opacity !== 1) { - properties.push( + parentProperties.push( `${indentation}opacity: ${Math.round(sceneNode.opacity * 100)}%;`, ); } @@ -599,7 +657,7 @@ export async function generateRectangleSnippet( // --- Ensure this uses await and the new async getBorderRadius --- const borderRadiusProp = await getBorderRadius(sceneNode); // Use await if (borderRadiusProp !== null) { - properties.push(borderRadiusProp); + parentProperties.push(borderRadiusProp); // Use new log message console.log( `[generateRectangleSnippet] Added border-radius property: ${borderRadiusProp.includes("\n") ? "\n" + borderRadiusProp : borderRadiusProp}`, @@ -617,7 +675,7 @@ export async function generateRectangleSnippet( const borderWidthAndColor = await getBorderWidthAndColor(sceneNode); if (borderWidthAndColor !== null) { - properties.push(...borderWidthAndColor); + parentProperties.push(...borderWidthAndColor); } break; } @@ -628,15 +686,56 @@ export async function generateRectangleSnippet( err, ); // Optionally add a comment to the snippet indicating the error - properties.push( + parentProperties.push( `${indentation}// Error processing ${property}: ${err instanceof Error ? err.message : err}`, ); } // --- End try...catch --- } - return `Rectangle {\n${properties.join("\n")}\n}`; + + parentProperties.sort(); + + // --- 2. Process Children Recursively --- + let childSnippets: string[] = []; + if ("children" in sceneNode && Array.isArray(sceneNode.children)) { + console.log(`[generateRectangleSnippet] Processing ${sceneNode.children.length} children for ${sceneNode.name}...`); + + // Create promises for generating each child's snippet + const childPromises = sceneNode.children.map(childNode => + generateSlintSnippet(childNode) // <<< --- RECURSIVE CALL --- >>> + .catch(err => { // Add catch here to prevent Promise.all failure + console.error(`[generateRectangleSnippet] Error generating snippet for child ${childNode.name}:`, err); + return `// Error generating snippet for child ${childNode.name}`; // Return error comment + }) + ); + + // Wait for all child snippets to be generated + const resolvedChildSnippets = await Promise.all(childPromises); + + // Filter out null results (unsupported types) and indent valid snippets + childSnippets = resolvedChildSnippets + .filter((snippet): snippet is string => snippet !== null && snippet.trim() !== "") // Keep non-null, non-empty strings + .map(snippet => { + // Indent the entire child snippet block + return snippet.split('\n').map(line => `${indentation}${line}`).join('\n'); + }); + + console.log(`[generateRectangleSnippet] Finished processing children for ${sceneNode.name}. Got ${childSnippets.length} valid snippets.`); + } + + let combinedContent = parentProperties.join("\n"); + if (childSnippets.length > 0) { + // Add a separator if both parent properties and children exist + if (parentProperties.length > 0) { + combinedContent += "\n\n" + indentation + "// --- Children ---"; + } + combinedContent += "\n" + childSnippets.join("\n\n"); // Add newline between children + } + + return `Rectangle {\n${combinedContent}\n}`; // Use Rectangle as base, could be Frame etc. } + export async function generateTextSnippet( sceneNode: SceneNode, ): Promise { @@ -713,6 +812,45 @@ export async function generateTextSnippet( properties.push(`${indentation}y: ${yValue};`); } break; + case "width": + const boundWidthVarId = (sceneNode as any).boundVariables + ?.width?.id; + let widthValue: string | null = null; + if (boundWidthVarId) { + widthValue = + await getVariablePathString(boundWidthVarId); + } + if (!widthValue && "width" in sceneNode) { + const normalizedWidth = roundNumber(sceneNode.width); + if (normalizedWidth) { + widthValue = `${normalizedWidth}px`; + } + } + if (widthValue) { + properties.push(`${indentation}width: ${widthValue};`); + } + break; + case "height": + const boundHeightVarId = (sceneNode as any).boundVariables + ?.height?.id; + let heightValue: string | null = null; + if (boundHeightVarId) { + heightValue = + await getVariablePathString(boundHeightVarId); + } + if (!heightValue && "height" in sceneNode) { + const normalizedHeight = roundNumber(sceneNode.height); + if (normalizedHeight) { + heightValue = `${normalizedHeight}px`; + } + } + if (heightValue) { + properties.push( + `${indentation}height: ${heightValue};`, + ); + } + break; + case "text": // Assuming 'characters' binding is also an array if it exists const boundCharsVarId = (sceneNode as any).boundVariables From 89929165268c518411ef411d895e8a73f8d8cfec Mon Sep 17 00:00:00 2001 From: szecket Date: Mon, 21 Apr 2025 11:08:51 -0400 Subject: [PATCH 3/4] excluding x,y from base level items as their x and y are global --- .../backend/utils/property-parsing.ts | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/tools/figma-inspector/backend/utils/property-parsing.ts b/tools/figma-inspector/backend/utils/property-parsing.ts index 462f89d4315..dde1da3ea2d 100644 --- a/tools/figma-inspector/backend/utils/property-parsing.ts +++ b/tools/figma-inspector/backend/utils/property-parsing.ts @@ -8,6 +8,8 @@ import { export const indentation = " "; const rectangleProperties = [ + "x", + "y", "width", "height", "fill", @@ -20,6 +22,8 @@ const rectangleProperties = [ const textProperties = [ "x", "y", + "width", + "height", "text", "fill", "font-family", @@ -502,7 +506,7 @@ export function generateUnsupportedNodeSnippet(sceneNode: SceneNode): string { export async function generateRectangleSnippet( sceneNode: SceneNode, ): Promise { - const parentProperties: string[] = []; // Renamed for clarity + const parentProperties: string[] = []; if ("boundVariables" in sceneNode) { console.log( "[generateRectangleSnippet] Inspecting sceneNode.boundVariables:", @@ -514,7 +518,6 @@ export async function generateRectangleSnippet( ); } for (const property of rectangleProperties) { - // --- Add try...catch around each property's logic --- try { switch (property) { case "x": @@ -541,7 +544,7 @@ export async function generateRectangleSnippet( ); } } - if (xValue) { + if (xValue && sceneNode.parent?.type !== "PAGE") { parentProperties.push(`${indentation}x: ${xValue};`); } break; @@ -570,7 +573,7 @@ export async function generateRectangleSnippet( ); } } - if (yValue) { + if (yValue && sceneNode.parent?.type !== "PAGE") { parentProperties.push(`${indentation}y: ${yValue};`); } break; @@ -590,7 +593,9 @@ export async function generateRectangleSnippet( } } if (widthValue) { - parentProperties.push(`${indentation}width: ${widthValue};`); + parentProperties.push( + `${indentation}width: ${widthValue};`, + ); } break; case "height": @@ -689,6 +694,15 @@ export async function generateRectangleSnippet( parentProperties.push( `${indentation}// Error processing ${property}: ${err instanceof Error ? err.message : err}`, ); + console.error(`[generateRectangleSnippet] Error processing property "${property}" for parent ${sceneNode.name}:`, err); + parentProperties.push(`${indentation}// Error processing ${property}: ${err instanceof Error ? err.message : err}`); + console.error( + `[generateRectangleSnippet] Error processing property "${property}" for parent ${sceneNode.name}:`, + err, + ); + parentProperties.push( + `${indentation}// Error processing ${property}: ${err instanceof Error ? err.message : err}`, + ); } // --- End try...catch --- } @@ -699,15 +713,21 @@ export async function generateRectangleSnippet( // --- 2. Process Children Recursively --- let childSnippets: string[] = []; if ("children" in sceneNode && Array.isArray(sceneNode.children)) { - console.log(`[generateRectangleSnippet] Processing ${sceneNode.children.length} children for ${sceneNode.name}...`); + console.log( + `[generateRectangleSnippet] Processing ${sceneNode.children.length} children for ${sceneNode.name}...`, + ); // Create promises for generating each child's snippet - const childPromises = sceneNode.children.map(childNode => + const childPromises = sceneNode.children.map((childNode) => generateSlintSnippet(childNode) // <<< --- RECURSIVE CALL --- >>> - .catch(err => { // Add catch here to prevent Promise.all failure - console.error(`[generateRectangleSnippet] Error generating snippet for child ${childNode.name}:`, err); + .catch((err) => { + // Add catch here to prevent Promise.all failure + console.error( + `[generateRectangleSnippet] Error generating snippet for child ${childNode.name}:`, + err, + ); return `// Error generating snippet for child ${childNode.name}`; // Return error comment - }) + }), ); // Wait for all child snippets to be generated @@ -715,20 +735,28 @@ export async function generateRectangleSnippet( // Filter out null results (unsupported types) and indent valid snippets childSnippets = resolvedChildSnippets - .filter((snippet): snippet is string => snippet !== null && snippet.trim() !== "") // Keep non-null, non-empty strings - .map(snippet => { + .filter( + (snippet): snippet is string => + snippet !== null && snippet.trim() !== "", + ) // Keep non-null, non-empty strings + .map((snippet) => { // Indent the entire child snippet block - return snippet.split('\n').map(line => `${indentation}${line}`).join('\n'); + return snippet + .split("\n") + .map((line) => `${indentation}${line}`) + .join("\n"); }); - console.log(`[generateRectangleSnippet] Finished processing children for ${sceneNode.name}. Got ${childSnippets.length} valid snippets.`); + console.log( + `[generateRectangleSnippet] Finished processing children for ${sceneNode.name}. Got ${childSnippets.length} valid snippets.`, + ); } let combinedContent = parentProperties.join("\n"); if (childSnippets.length > 0) { // Add a separator if both parent properties and children exist if (parentProperties.length > 0) { - combinedContent += "\n\n" + indentation + "// --- Children ---"; + combinedContent += "\n\n" + indentation + "// --- Children ---"; } combinedContent += "\n" + childSnippets.join("\n\n"); // Add newline between children } @@ -812,7 +840,7 @@ export async function generateTextSnippet( properties.push(`${indentation}y: ${yValue};`); } break; - case "width": + case "width": const boundWidthVarId = (sceneNode as any).boundVariables ?.width?.id; let widthValue: string | null = null; From 58df176b758b41af0ca714d7f02a808269794de1 Mon Sep 17 00:00:00 2001 From: szecket Date: Tue, 22 Apr 2025 10:58:24 -0400 Subject: [PATCH 4/4] format fixes --- .../figma-inspector/backend/utils/property-parsing.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/figma-inspector/backend/utils/property-parsing.ts b/tools/figma-inspector/backend/utils/property-parsing.ts index dde1da3ea2d..dd7e1e7e4bf 100644 --- a/tools/figma-inspector/backend/utils/property-parsing.ts +++ b/tools/figma-inspector/backend/utils/property-parsing.ts @@ -694,8 +694,13 @@ export async function generateRectangleSnippet( parentProperties.push( `${indentation}// Error processing ${property}: ${err instanceof Error ? err.message : err}`, ); - console.error(`[generateRectangleSnippet] Error processing property "${property}" for parent ${sceneNode.name}:`, err); - parentProperties.push(`${indentation}// Error processing ${property}: ${err instanceof Error ? err.message : err}`); + console.error( + `[generateRectangleSnippet] Error processing property "${property}" for parent ${sceneNode.name}:`, + err, + ); + parentProperties.push( + `${indentation}// Error processing ${property}: ${err instanceof Error ? err.message : err}`, + ); console.error( `[generateRectangleSnippet] Error processing property "${property}" for parent ${sceneNode.name}:`, err, @@ -707,7 +712,6 @@ export async function generateRectangleSnippet( // --- End try...catch --- } - parentProperties.sort(); // --- 2. Process Children Recursively ---