import { parseColor } from '../color' import type { SceneNode } from '../scene-graph' import { defineTool, nodeSummary } from './schema' import type { FigmaNodeProxy } from '../figma-api' export const createShape = defineTool({ name: 'create_shape', mutates: true, description: 'Create a shape on the canvas. Use FRAME for containers/cards, RECTANGLE for solid blocks, ELLIPSE for circles, TEXT for labels, SECTION for page sections.', params: { type: { type: 'string', description: 'Node type', required: true, enum: ['FRAME', 'RECTANGLE', 'ELLIPSE', 'TEXT', 'LINE', 'STAR', 'POLYGON', 'SECTION'] }, x: { type: 'number', description: 'X position', required: true }, y: { type: 'number', description: 'Y position', required: true }, width: { type: 'number', description: 'Width in pixels', required: true, min: 1 }, height: { type: 'number', description: 'Height in pixels', required: true, min: 1 }, name: { type: 'string', description: 'Node name shown in layers panel' }, parent_id: { type: 'string', description: 'Parent node ID to nest inside' } }, execute: (figma, args) => { const parentId = args.parent_id const parent = parentId ? figma.getNodeById(parentId) : null const createMap: Record FigmaNodeProxy> = { FRAME: () => figma.createFrame(), RECTANGLE: () => figma.createRectangle(), ELLIPSE: () => figma.createEllipse(), TEXT: () => figma.createText(), LINE: () => figma.createLine(), STAR: () => figma.createStar(), POLYGON: () => figma.createPolygon(), SECTION: () => figma.createSection() } const node = createMap[args.type]() node.x = args.x node.y = args.y node.resize(args.width, args.height) if (args.name) node.name = args.name if (parent) parent.appendChild(node) return nodeSummary(node) } }) export const render = defineTool({ name: 'render', mutates: true, description: 'Render JSX to design nodes. Primary creation tool — creates entire component trees in one call. Example: Title', params: { jsx: { type: 'string', description: 'JSX string to render', required: true }, x: { type: 'number', description: 'X position of the root node' }, y: { type: 'number', description: 'Y position of the root node' }, parent_id: { type: 'string', description: 'Parent node ID to render into' } }, execute: async (figma, args) => { const { renderJSX } = await import('../render/render-jsx.js') const result = renderJSX(figma.graph, args.jsx, { parentId: args.parent_id ?? figma.currentPageId, x: args.x, y: args.y }) return { id: result.id, name: result.name, type: result.type, children: result.childIds } } }) export const createComponent = defineTool({ name: 'create_component', mutates: true, description: 'Convert a frame/group into a component.', params: { id: { type: 'string', description: 'Node ID to convert', required: true } }, execute: (figma, { id }) => { const node = figma.getNodeById(id) if (!node) return { error: `Node "${id}" not found` } const comp = figma.createComponentFromNode(node) return nodeSummary(comp) } }) export const createInstance = defineTool({ name: 'create_instance', mutates: true, description: 'Create an instance of a component.', params: { component_id: { type: 'string', description: 'Component node ID', required: true }, x: { type: 'number', description: 'X position' }, y: { type: 'number', description: 'Y position' } }, execute: (figma, args) => { const comp = figma.getNodeById(args.component_id) if (!comp) return { error: `Component "${args.component_id}" not found` } const instance = comp.createInstance() if (args.x !== undefined) instance.x = args.x if (args.y !== undefined) instance.y = args.y return nodeSummary(instance) } }) export const createPage = defineTool({ name: 'create_page', mutates: true, description: 'Create a new page.', params: { name: { type: 'string', description: 'Page name', required: true } }, execute: (figma, { name }) => { const page = figma.createPage() page.name = name return { id: page.id, name } } }) export const createVector = defineTool({ name: 'create_vector', mutates: true, description: 'Create a vector node with optional path data.', params: { x: { type: 'number', description: 'X position', required: true }, y: { type: 'number', description: 'Y position', required: true }, name: { type: 'string', description: 'Node name' }, path: { type: 'string', description: 'VectorNetwork JSON' }, fill: { type: 'color', description: 'Fill color (hex)' }, stroke: { type: 'color', description: 'Stroke color (hex)' }, stroke_weight: { type: 'number', description: 'Stroke weight' }, parent_id: { type: 'string', description: 'Parent node ID' } }, execute: (figma, args) => { const node = figma.createVector() node.x = args.x node.y = args.y if (args.name) node.name = args.name if (args.path) { figma.graph.updateNode(node.id, { vectorNetwork: JSON.parse(args.path) as SceneNode['vectorNetwork'] }) } if (args.fill) { node.fills = [{ type: 'SOLID', color: parseColor(args.fill), opacity: 1, visible: true }] } if (args.stroke) { node.strokes = [ { color: parseColor(args.stroke), weight: args.stroke_weight ?? 1, opacity: 1, visible: true, align: 'CENTER' } ] } if (args.parent_id) { const parent = figma.getNodeById(args.parent_id) if (parent) parent.appendChild(node) } return nodeSummary(node) } }) export const createSlice = defineTool({ name: 'create_slice', mutates: true, description: 'Create a slice (export region) on the canvas.', params: { x: { type: 'number', description: 'X position', required: true }, y: { type: 'number', description: 'Y position', required: true }, width: { type: 'number', description: 'Width', required: true, min: 1 }, height: { type: 'number', description: 'Height', required: true, min: 1 }, name: { type: 'string', description: 'Slice name' }, parent_id: { type: 'string', description: 'Parent node ID' } }, execute: (figma, args) => { const node = figma.createFrame() node.x = args.x node.y = args.y node.resize(args.width, args.height) node.name = args.name ?? 'Slice' node.fills = [] if (args.parent_id) { const parent = figma.getNodeById(args.parent_id) if (parent) parent.appendChild(node) } return nodeSummary(node) } })