interface MoveNodeParams {
nodeId: string;
x: number;
y: number;
}
interface ResizeNodeParams {
nodeId: string;
width: number;
height: number;
}
interface DeleteNodeParams {
nodeId: string;
}
interface DeleteMultipleNodesParams {
nodeIds: string[];
}
interface CloneNodeParams {
nodeId: string;
x?: number;
y?: number;
}
/**
* Move a node to new coordinates
*/
export async function moveNode(params: MoveNodeParams): Promise<{
id: string;
name: string;
x: number;
y: number;
}> {
const { nodeId, x, y } = params;
const node = await figma.getNodeByIdAsync(nodeId);
if (!node) {
throw new Error(`Node not found with ID: ${nodeId}`);
}
if (!("x" in node) || !("y" in node)) {
throw new Error(`Node does not support position: ${nodeId}`);
}
const sceneNode = node as SceneNode;
sceneNode.x = x;
sceneNode.y = y;
return {
id: sceneNode.id,
name: sceneNode.name,
x: sceneNode.x,
y: sceneNode.y,
};
}
/**
* Resize a node
*/
export async function resizeNode(params: ResizeNodeParams): Promise<{
id: string;
name: string;
width: number;
height: number;
}> {
const { nodeId, width, height } = params;
const node = await figma.getNodeByIdAsync(nodeId);
if (!node) {
throw new Error(`Node not found with ID: ${nodeId}`);
}
if (!("resize" in node)) {
throw new Error(`Node does not support resize: ${nodeId}`);
}
const resizableNode = node as SceneNode & {
resize: (width: number, height: number) => void;
};
resizableNode.resize(width, height);
return {
id: resizableNode.id,
name: resizableNode.name,
width: (resizableNode as LayoutMixin).width,
height: (resizableNode as LayoutMixin).height,
};
}
/**
* Delete a node
*/
export async function deleteNode(params: DeleteNodeParams): Promise<{
success: boolean;
deletedId: string;
}> {
const { nodeId } = params;
const node = await figma.getNodeByIdAsync(nodeId);
if (!node) {
throw new Error(`Node not found with ID: ${nodeId}`);
}
if (!("remove" in node)) {
throw new Error(`Node cannot be deleted: ${nodeId}`);
}
(node as SceneNode).remove();
return {
success: true,
deletedId: nodeId,
};
}
/**
* Delete multiple nodes
*/
export async function deleteMultipleNodes(
params: DeleteMultipleNodesParams,
): Promise<{
success: boolean;
deletedCount: number;
failedCount: number;
results: Array<{ nodeId: string; success: boolean; error?: string }>;
}> {
const { nodeIds } = params;
const results: Array<{ nodeId: string; success: boolean; error?: string }> =
[];
for (const nodeId of nodeIds) {
try {
const node = await figma.getNodeByIdAsync(nodeId);
if (!node) {
results.push({ nodeId, success: false, error: "Node not found" });
continue;
}
if (!("remove" in node)) {
results.push({
nodeId,
success: false,
error: "Node cannot be deleted",
});
continue;
}
(node as SceneNode).remove();
results.push({ nodeId, success: true });
} catch (error) {
results.push({
nodeId,
success: false,
error: error instanceof Error ? error.message : String(error),
});
}
}
return {
success: results.some((r) => r.success),
deletedCount: results.filter((r) => r.success).length,
failedCount: results.filter((r) => !r.success).length,
results,
};
}
/**
* Clone a node
*/
export async function cloneNode(params: CloneNodeParams): Promise<{
id: string;
name: string;
x: number;
y: number;
originalId: string;
}> {
const { nodeId, x, y } = params;
const node = await figma.getNodeByIdAsync(nodeId);
if (!node) {
throw new Error(`Node not found with ID: ${nodeId}`);
}
if (!("clone" in node)) {
throw new Error(`Node cannot be cloned: ${nodeId}`);
}
const sceneNode = node as SceneNode;
const clonedNode = sceneNode.clone();
// Set position if provided
if (x !== undefined) {
clonedNode.x = x;
}
if (y !== undefined) {
clonedNode.y = y;
}
return {
id: clonedNode.id,
name: clonedNode.name,
x: clonedNode.x,
y: clonedNode.y,
originalId: nodeId,
};
}