distribute_elements
Evenly space selected elements horizontally or vertically in Excalidraw diagrams to organize layouts and improve visual alignment.
Instructions
Distribute elements evenly (horizontal or vertical)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| elementIds | Yes | ||
| direction | Yes |
Implementation Reference
- src/mcp/index.ts:368-423 (handler)The actual working handler implementation - the distribute_elements tool is registered with inline logic that distributes elements horizontally or vertically by calculating equal gaps between sorted elementsserver.tool( 'distribute_elements', 'Distribute elements evenly (horizontal or vertical)', { elementIds: z.array(IdZ).min(3).max(LIMITS.MAX_ELEMENT_IDS), direction: z.enum(['horizontal', 'vertical']), }, async ({ elementIds, direction }) => { try { const elements = []; for (const eid of elementIds) { const el = await client.getElement(eid); if (!el) throw new Error(`Element ${eid} not found`); if (el.locked) throw new Error(`Element ${eid} is locked`); elements.push(el); } if (direction === 'horizontal') { const sorted = [...elements].sort((a, b) => a.x - b.x); const first = sorted[0]!; const last = sorted[sorted.length - 1]!; const totalSpan = (last.x + (last.width ?? 0)) - first.x; const totalWidth = sorted.reduce((s, e) => s + (e.width ?? 0), 0); const gap = (totalSpan - totalWidth) / (sorted.length - 1); let currentX = first.x; for (const el of sorted) { await client.updateElement(el.id, { x: currentX }); currentX += (el.width ?? 0) + gap; } } else { const sorted = [...elements].sort((a, b) => a.y - b.y); const first = sorted[0]!; const last = sorted[sorted.length - 1]!; const totalSpan = (last.y + (last.height ?? 0)) - first.y; const totalHeight = sorted.reduce((s, e) => s + (e.height ?? 0), 0); const gap = (totalSpan - totalHeight) / (sorted.length - 1); let currentY = first.y; for (const el of sorted) { await client.updateElement(el.id, { y: currentY }); currentY += (el.height ?? 0) + gap; } } return { content: [{ type: 'text', text: JSON.stringify({ distributed: true, direction, elementIds }, null, 2), }], }; } catch (err) { return { content: [{ type: 'text', text: `Error: ${(err as Error).message}` }], isError: true }; } } );
- src/mcp/schemas/element.ts:147-156 (schema)Zod schema definition for distribute_elements input validation - requires elementIds array (min 3, max limit) and direction (horizontal or vertical)export const DistributeElementsSchema = z .object({ elementIds: z .array(z.string().max(LIMITS.MAX_ID_LENGTH)) .min(3) .max(LIMITS.MAX_ELEMENT_IDS), direction: z.enum(['horizontal', 'vertical']), }) .strict();
- Alternative standalone handler implementation with similar logic but using DistributeElementsSchema.parse for validation - appears to be unused by the main MCP serverexport async function distributeElementsTool( args: unknown, client: CanvasClient ) { const { elementIds, direction } = DistributeElementsSchema.parse(args); const elements = await Promise.all( elementIds.map(async (id) => { const el = await client.getElement(id); if (!el) throw new Error(`Element ${id} not found`); return el; }) ); if (direction === 'horizontal') { const sorted = [...elements].sort((a, b) => a.x - b.x); const first = sorted[0]; const last = sorted[sorted.length - 1]; const totalSpan = last.x + (last.width ?? 0) - first.x; const totalElementWidth = sorted.reduce( (sum, el) => sum + (el.width ?? 0), 0 ); const gap = (totalSpan - totalElementWidth) / (sorted.length - 1); let currentX = first.x; for (const el of sorted) { if (el.x !== currentX) { await client.updateElement(el.id, { x: currentX }); } currentX += (el.width ?? 0) + gap; } } else { const sorted = [...elements].sort((a, b) => a.y - b.y); const first = sorted[0]; const last = sorted[sorted.length - 1]; const totalSpan = last.y + (last.height ?? 0) - first.y; const totalElementHeight = sorted.reduce( (sum, el) => sum + (el.height ?? 0), 0 ); const gap = (totalSpan - totalElementHeight) / (sorted.length - 1); let currentY = first.y; for (const el of sorted) { if (el.y !== currentY) { await client.updateElement(el.id, { y: currentY }); } currentY += (el.height ?? 0) + gap; } } return { success: true, distributed: true, direction, elementIds }; }
- src/mcp/schemas/element.ts:247-247 (schema)TypeScript type inferred from DistributeElementsSchema for type safetyexport type DistributeElements = z.infer<typeof DistributeElementsSchema>;
- src/mcp/index.ts:569-569 (registration)Sandbox registration for capability scanning - registers the tool with no-op handlerserver.tool('distribute_elements', 'Distribute elements evenly (horizontal or vertical)', { elementIds: z.array(IdZ).min(3).max(LIMITS.MAX_ELEMENT_IDS), direction: z.enum(['horizontal', 'vertical']) }, noop);