From e299df2e1be69ebae9e44ce174ce6d23afa05020 Mon Sep 17 00:00:00 2001 From: ATblackwhite <1621864188@qq.com> Date: Tue, 25 Mar 2025 17:04:00 +0800 Subject: [PATCH 1/2] add set_interactions tool --- src/cursor_mcp_plugin/code.js | 60 ++++++++++++++++++++++++++++++ src/talk_to_figma_mcp/server.ts | 66 ++++++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/cursor_mcp_plugin/code.js b/src/cursor_mcp_plugin/code.js index c35d1f3..0049977 100644 --- a/src/cursor_mcp_plugin/code.js +++ b/src/cursor_mcp_plugin/code.js @@ -109,6 +109,8 @@ async function handleCommand(command, params) { return await setTextContent(params); case "clone_node": return await cloneNode(params); + case "set_node_interactions": + return await setNodeInteractions(params); default: throw new Error(`Unknown command: ${command}`); } @@ -1167,3 +1169,61 @@ async function cloneNode(params) { height: "height" in clone ? clone.height : undefined, }; } + +// Add the setNodeInteractions function implementation +async function setNodeInteractions(params) { + const { nodeId, interactions } = params || {}; + + if (!nodeId) { + throw new Error("Missing nodeId parameter"); + } + + if (!interactions || !Array.isArray(interactions)) { + throw new Error("Missing or invalid interactions parameter"); + } + + const node = await figma.getNodeByIdAsync(nodeId); + if (!node) { + throw new Error(`Node not found with ID: ${nodeId}`); + } + + try { + // Use Figma API's setReactionsAsync to set interactions + await node.setReactionsAsync(interactions.map(interaction => { + let actionObj = { + type: interaction.actionType + }; + + // Add different parameters based on action type + if (interaction.actionType === "NAVIGATE") { + actionObj.destination = interaction.destination; + actionObj.transition = interaction.transition || { type: "NONE" }; + } else if (interaction.actionType === "URL") { + actionObj.url = interaction.url; + } else if (interaction.actionType === "OPEN_NODE") { + actionObj.nodeId = interaction.targetNodeId; + } else if (interaction.actionType === "OVERLAY") { + actionObj.nodeId = interaction.targetNodeId; + actionObj.preserveScrollPosition = interaction.preserveScrollPosition || false; + } else if (interaction.actionType === "SWAP") { + actionObj.componentId = interaction.componentId; + } + + return { + actions: [actionObj], + trigger: { + type: interaction.triggerType || "ON_CLICK", + } + }; + })); + + return { + id: node.id, + name: node.name, + interactionsCount: interactions.length, + }; + } catch (error) { + console.error("Complete error object:", error); + throw new Error(`Error setting interactions: ${error.message || "Unknown error"}`); + } +} diff --git a/src/talk_to_figma_mcp/server.ts b/src/talk_to_figma_mcp/server.ts index 7840789..19d9264 100644 --- a/src/talk_to_figma_mcp/server.ts +++ b/src/talk_to_figma_mcp/server.ts @@ -790,6 +790,69 @@ server.tool( } ); +// Add Interactions Tool +server.tool( + "set_node_interactions", + "Set interactive behaviors to a node in Figma", + { + nodeId: z.string().describe("The ID of the node to add interactions to"), + interactions: z.array( + z.object({ + actionType: z.enum([ + "NAVIGATE", + "URL", + "OPEN_NODE", + "OVERLAY", + "SWAP", + "BACK", + "CLOSE" + ]).describe("Type of interaction action"), + triggerType: z.enum([ + "ON_CLICK", + "ON_HOVER", + "ON_PRESS", + "ON_DRAG" + ]).default("ON_CLICK").describe("Type of trigger (default: ON_CLICK)"), + // Optional parameters based on action type + destination: z.string().optional().describe("For NAVIGATE: Target page ID"), + transition: z.object({ + type: z.enum(["NONE", "DISSOLVE", "SMART_ANIMATE", "MOVE_IN", "MOVE_OUT", "PUSH", "SLIDE_IN", "SLIDE_OUT"]) + }).optional().describe("For NAVIGATE: Transition effect"), + url: z.string().optional().describe("For URL: Web URL to open"), + targetNodeId: z.string().optional().describe("For OPEN_NODE/OVERLAY: Target node ID"), + preserveScrollPosition: z.boolean().optional().describe("For OVERLAY: Preserve scroll position"), + componentId: z.string().optional().describe("For SWAP: Component ID to swap to") + }) + ).describe("Array of interactions to add to the node") + }, + async ({ nodeId, interactions }) => { + try { + const result = await sendCommandToFigma('set_node_interactions', { + nodeId, + interactions + }); + const typedResult = result as { name: string, interactionsCount: number }; + return { + content: [ + { + type: "text", + text: `Successfully added ${typedResult.interactionsCount} interactions to node "${typedResult.name}"` + } + ] + }; + } catch (error) { + return { + content: [ + { + type: "text", + text: `Error adding interactions: ${error instanceof Error ? error.message : String(error)}` + } + ] + }; + } + } +); + // Define design strategy prompt server.prompt( "design_strategy", @@ -900,7 +963,8 @@ type FigmaCommand = | 'join' | 'set_corner_radius' | 'set_text_content' - | 'clone_node'; + | 'clone_node' + | 'set_node_interactions'; // Helper function to process Figma node responses function processFigmaNodeResponse(result: unknown): any { From 8720590a7db144f323bf6927a3df10bfd8fd5a56 Mon Sep 17 00:00:00 2001 From: ATblackwhite <1621864188@qq.com> Date: Tue, 25 Mar 2025 17:45:07 +0800 Subject: [PATCH 2/2] add set_scroll_behaviour tool --- src/cursor_mcp_plugin/code.js | 49 +++++++++++++++++++++++++++++++++ src/talk_to_figma_mcp/server.ts | 44 ++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/cursor_mcp_plugin/code.js b/src/cursor_mcp_plugin/code.js index 0049977..cd37385 100644 --- a/src/cursor_mcp_plugin/code.js +++ b/src/cursor_mcp_plugin/code.js @@ -111,6 +111,8 @@ async function handleCommand(command, params) { return await cloneNode(params); case "set_node_interactions": return await setNodeInteractions(params); + case "set_overflow_direction": + return await setOverflowDirection(params); default: throw new Error(`Unknown command: ${command}`); } @@ -1227,3 +1229,50 @@ async function setNodeInteractions(params) { throw new Error(`Error setting interactions: ${error.message || "Unknown error"}`); } } + +// Add the setOverflowDirection function implementation +async function setOverflowDirection(params) { + const { nodeId, direction } = params || {}; + + if (!nodeId) { + throw new Error("Missing nodeId parameter"); + } + + if (!direction) { + throw new Error("Missing direction parameter"); + } + + const node = await figma.getNodeByIdAsync(nodeId); + if (!node) { + throw new Error(`Node not found with ID: ${nodeId}`); + } + + // 检查节点是否支持overflowDirection属性 + if (!("overflowDirection" in node)) { + throw new Error(`Node does not support overflow direction: ${nodeId}`); + } + + // 设置overflowDirection + switch (direction.toUpperCase()) { + case "NONE": + node.overflowDirection = "NONE"; + break; + case "HORIZONTAL": + node.overflowDirection = "HORIZONTAL"; + break; + case "VERTICAL": + node.overflowDirection = "VERTICAL"; + break; + case "BOTH": + node.overflowDirection = "BOTH"; + break; + default: + throw new Error(`Invalid overflow direction: ${direction}`); + } + + return { + id: node.id, + name: node.name, + overflowDirection: node.overflowDirection + }; +} diff --git a/src/talk_to_figma_mcp/server.ts b/src/talk_to_figma_mcp/server.ts index 19d9264..37461e9 100644 --- a/src/talk_to_figma_mcp/server.ts +++ b/src/talk_to_figma_mcp/server.ts @@ -853,6 +853,47 @@ server.tool( } ); +// Add Set Overflow Direction Tool +server.tool( + "set_overflow_direction", + "Set the overflow direction (scroll behavior) of a node in Figma", + { + nodeId: z.string().describe("The ID of the node to modify"), + direction: z.enum([ + "NONE", + "HORIZONTAL", + "VERTICAL", + "BOTH" + ]).describe("Overflow direction: NONE (no scrolling), HORIZONTAL, VERTICAL, or BOTH") + }, + async ({ nodeId, direction }) => { + try { + const result = await sendCommandToFigma('set_overflow_direction', { + nodeId, + direction + }); + const typedResult = result as { name: string, overflowDirection: string }; + return { + content: [ + { + type: "text", + text: `设置节点 "${typedResult.name}" 的滚动方向为 ${typedResult.overflowDirection}` + } + ] + }; + } catch (error) { + return { + content: [ + { + type: "text", + text: `设置滚动方向出错: ${error instanceof Error ? error.message : String(error)}` + } + ] + }; + } + } +); + // Define design strategy prompt server.prompt( "design_strategy", @@ -964,7 +1005,8 @@ type FigmaCommand = | 'set_corner_radius' | 'set_text_content' | 'clone_node' - | 'set_node_interactions'; + | 'set_node_interactions' + | 'set_overflow_direction'; // Helper function to process Figma node responses function processFigmaNodeResponse(result: unknown): any {