Skip to content

added first level of recursive snippet for recognized items in hierarchy #8256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: feature/figma-recurse-snippet
Choose a base branch
from
Open
89 changes: 68 additions & 21 deletions tools/figma-inspector/backend/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
// cSpell: ignore codegen

import { listenTS, updateUI } from "./utils/code-utils.js";
import { listenTS, dispatchTS } from "./utils/code-utils.js";
import { generateSlintSnippet } from "./utils/property-parsing.js";
import { exportFigmaVariablesToSeparateFiles } from "./utils/export-variables.js";
if (figma.editorType === "dev" && figma.mode === "codegen") {
figma.codegen.on("generate", async ({ node }: { node: SceneNode }) => {
try {
// Add try...catch for async errors
// --- Await the async function ---
const slintSnippet = await generateSlintSnippet(node);
const useVariablesForCodegen = true;
const slintSnippet = await generateSlintSnippet(
node,
useVariablesForCodegen,
);
// --- End Await ---

return slintSnippet
Expand Down Expand Up @@ -43,8 +47,59 @@ if (figma.editorType === "figma" && figma.mode === "default") {
width: 500,
height: 320,
});
updateUI();
}
listenTS("generateSnippetRequest", async (payload) => {
console.log("[Backend] Entered 'generateSnippetRequest' handler.");
// --- Extract useVariables from payload (default to false) ---
const useVariables = payload.useVariables ?? false; // <-- You likely already have this
console.log(
`[Backend] Received generateSnippetRequest. Use variables: ${useVariables}`,
);

const selection = figma.currentPage.selection;
let title = "Figma Inspector";
let slintSnippet: string | null = "// Select a single component to inspect";

if (selection.length === 1) {
const node = selection[0];
title = node.name;
try {
// --- Pass the useVariables value received from UI ---
console.log(
`[Backend] Calling generateSlintSnippet for ${node.name}. useVariables = ${useVariables} (Type: ${typeof useVariables})`,
);
slintSnippet = await generateSlintSnippet(node, useVariables);
console.log(
`[Backend] Finished generating snippet for ${node.name}. Result is null? ${slintSnippet === null}`,
);

if (slintSnippet === null) {
slintSnippet = `// Unsupported node type: ${node.type}`;
}
console.log(
`[Backend] Generated snippet for ${node.name}. Length: ${slintSnippet?.length ?? 0}`,
);
} catch (error) {
console.error(
`[Backend] Error generating snippet for ${node.name}:`,
error,
);
slintSnippet = `// Error generating snippet for ${node.name}:\n// ${error instanceof Error ? error.message : String(error)}`;
}
} else if (selection.length > 1) {
slintSnippet = "// Select a single component to inspect";
title = "Multiple Items Selected";
}

// Send result back to UI using the correct message type
dispatchTS("updatePropertiesCallback", {
title: title,
slintSnippet: slintSnippet,
});
console.log(
`[Backend] Sent updatePropertiesCallback to UI. Title: ${title}`,
);
});

listenTS("copyToClipboard", ({ result }) => {
if (result) {
Expand All @@ -55,11 +110,10 @@ listenTS("copyToClipboard", ({ result }) => {
});

figma.on("selectionchange", () => {
console.log("[Backend] Selection changed in Figma, notifying UI."); // <-- Add this line

if (figma.editorType === "figma" && figma.mode === "default") {
// Call async function and handle potential errors
updateUI().catch((err) =>
console.error("Error handling selection change:", err),
);
dispatchTS("selectionChangedInFigma", {});
}
});

Expand Down Expand Up @@ -108,12 +162,12 @@ const variableMonitoring: {
const DEBOUNCE_INTERVAL = 3000; // 3 seconds

listenTS("monitorVariableChanges", async () => {
console.log("[Backend] Received 'monitorVariableChanges' from UI."); // <-- Add Log
// console.log("[Backend] Received 'monitorVariableChanges' from UI."); // <-- Add Log

// Confirm setup to UI
console.log(
"[Backend] Posting 'variableMonitoringActive' confirmation to UI.",
); // <-- Add Log
// // Confirm setup to UI
// console.log(
// "[Backend] Posting 'variableMonitoringActive' confirmation to UI.",
// ); // <-- Add Log
figma.ui.postMessage({
type: "variableMonitoringActive", // Keep this confirmation
timestamp: Date.now(),
Expand All @@ -125,7 +179,7 @@ listenTS("checkVariableChanges", async () => {

// Replace your checkVariableChanges handler
async function checkVariableChanges(isInitialRun = false) {
console.log("[Backend] Running checkVariableChanges..."); // Log run
// console.log("[Backend] Running checkVariableChanges..."); // Log run
try {
const collections =
await figma.variables.getLocalVariableCollectionsAsync();
Expand Down Expand Up @@ -182,9 +236,7 @@ async function checkVariableChanges(isInitialRun = false) {
variableMonitoring.lastSnapshot = currentSnapshot;
variableMonitoring.initialized = true;
variableMonitoring.lastChange = now; // Set initial timestamp
console.log(
"[Backend] Variable monitoring initialized/updated with detailed baseline snapshot.",
);

// Optionally notify UI that it's initialized, maybe reset its state
figma.ui.postMessage({
type: "snapshotInitialized",
Expand All @@ -197,9 +249,6 @@ async function checkVariableChanges(isInitialRun = false) {
const hasChanged = variableMonitoring.lastSnapshot !== currentSnapshot;

if (hasChanged) {
console.log(
"[Backend] Detailed snapshot comparison detected changes.",
); // Log change detection
variableMonitoring.lastSnapshot = currentSnapshot;
variableMonitoring.lastChange = now;

Expand All @@ -212,8 +261,6 @@ async function checkVariableChanges(isInitialRun = false) {
? "Snapshot updated (some variable errors)"
: "Snapshot updated",
});
} else {
console.log("[Backend] No changes detected in detailed snapshot."); // Log no change
}
} catch (error) {
console.error("[Backend] Error during checkVariableChanges:", error);
Expand Down
96 changes: 14 additions & 82 deletions tools/figma-inspector/backend/utils/code-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,33 @@ export const dispatchTS = <Key extends keyof EventTS>(
data: EventTS[Key],
origin = "*",
) => {
dispatch({ event, data }, origin);
dispatch({ type: event, ...data }, origin);
};

export const listenTS = <Key extends keyof EventTS>(
eventName: Key,
callback: (data: EventTS[Key]) => any,
callback: (data: EventTS[Key] & { type: Key }) => any,
listenOnce = false,
) => {
const func = (event: any) => {
if (event.event === eventName) {
callback(event);
// --- Define func only ONCE ---
const func = (pluginMessage: any) => {
// The message from figma.ui.on is the payload directly
console.log(`[Backend Listener Raw Msg]:`, pluginMessage); // <-- Uncomment if you want this log

// --- Check if the received message has the correct type ---
if (pluginMessage && pluginMessage.type === eventName) {
console.log(`[Backend Listener Matched Type]: ${eventName}`); // <-- Uncomment if you want this log
callback(pluginMessage); // Pass the received payload
if (listenOnce) {
figma.ui?.off("message", func); // Remove Listener so we only listen once
figma.ui.off("message", func);
}
}
};
// --- End single definition ---

console.log(`[Backend] Attaching listener for type: ${eventName}`);
figma.ui.on("message", func);
};

export const getStore = async (key: string) => {
const value = await figma.clientStorage.getAsync(key);
return value;
Expand All @@ -44,78 +51,3 @@ export const getStore = async (key: string) => {
export const setStore = async (key: string, value: string) => {
await figma.clientStorage.setAsync(key, value);
};

export async function updateUI() {
console.log("[updateUI] Function execution started.");
const selection = figma.currentPage.selection;
let title = "No selection";
let slintSnippet: string | null = null;
let messagePayload: any = null; // Define outside try block

try {
// --- Wrap more logic ---
if (selection.length === 1) {
const node = selection[0];
title = node.name;
// Keep inner try...catch for specific snippet generation error
try {
console.log(
`[updateUI] Calling generateSlintSnippet for node: ${node.name}`,
);
slintSnippet = await generateSlintSnippet(node);
// --- Log immediately after await ---
console.log(
`[updateUI] generateSlintSnippet returned: ${slintSnippet ? "Snippet received" : "null"}`,
);
} catch (snippetError) {
console.error(
"[updateUI] Caught error DURING generateSlintSnippet:",
snippetError,
);
slintSnippet = "// Error generating snippet. See console.";
}
} else if (selection.length > 1) {
title = "Multiple items selected";
}

// --- Create payload and log within the try block ---
messagePayload = {
type: "updatePropertiesCallback",
title: title,
slintSnippet: slintSnippet ?? "// Could not generate snippet.",
};

console.log(
`[updateUI] Preparing to post message. Snippet is null: ${slintSnippet === null}`,
);
console.log(`[updateUI] Payload:`, JSON.stringify(messagePayload));
// --- End create payload and log ---
} catch (outerError) {
// --- Catch errors during selection handling or payload creation ---
console.error(
"[updateUI] >>> ERROR before posting message:",
outerError,
);
// Attempt to create a fallback error payload
messagePayload = {
type: "updatePropertiesCallback",
title: "Error",
slintSnippet: `// Error preparing UI update: ${outerError instanceof Error ? outerError.message : outerError}`,
};
console.log(`[updateUI] Created fallback error payload.`);
}

// --- Post Message (outside the main try block, but payload is guaranteed to exist) ---
if (messagePayload) {
try {
figma.ui.postMessage(messagePayload);
console.log(`[updateUI] Successfully posted message to UI.`);
} catch (postError) {
console.error(`[updateUI] Error POSTING message to UI:`, postError);
}
} else {
console.error(
"[updateUI] messagePayload was unexpectedly null, cannot post to UI.",
);
}
}
Loading
Loading