obsidian_canvas_create
Create an Obsidian canvas by defining nodes and edges with automatic graph layout, enabling visual mapping of connected ideas.
Instructions
Create an Obsidian JSON Canvas from semantic nodes/edges with automatic graph layout.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| vault | No | Optional configured vault name. Defaults to the server default vault. | |
| path | Yes | Vault-relative path. Absolute paths and traversal are rejected. | |
| nodes | Yes | ||
| edges | No | ||
| direction | No | TB | |
| overwrite | No |
Implementation Reference
- src/tools.ts:1060-1086 (handler)The tool registration and handler for 'obsidian_canvas_create'. It defines the schema (vault, path, nodes, edges, direction, overwrite) and invokes createCanvas() from canvas.ts to build the canvas, then writes it to the vault.
tool( "obsidian_canvas_create", "Create an Obsidian JSON Canvas from semantic nodes/edges with automatic graph layout.", { vault: vaultArg, path: pathArg, nodes: z.array(z.object({ id: z.string().optional(), label: z.string(), type: z.enum(["text", "file", "link", "group"]).optional(), text: z.string().optional(), file: z.string().optional(), url: z.string().optional(), color: z.string().optional(), width: z.number().optional(), height: z.number().optional(), })).min(1), edges: z.array(z.object({ id: z.string().optional(), from: z.string(), to: z.string(), label: z.string().optional(), color: z.string().optional() })).optional().default([]), direction: z.enum(["TB", "BT", "LR", "RL"]).optional().default("TB"), overwrite: z.boolean().optional().default(false), }, async (args) => { const canvas = createCanvas(args.nodes, args.edges, { direction: args.direction }); const canvasPath = args.path.endsWith(".canvas") ? args.path : `${args.path}.canvas`; return { ...(await vaults.writeText(canvasPath, `${JSON.stringify(canvas, null, 2)}\n`, args.vault, { overwrite: args.overwrite })), summary: { nodes: canvas.nodes.length, edges: canvas.edges.length } }; }, ); - src/canvas.ts:52-85 (helper)The createCanvas() function that converts CanvasNodeInput/CanvasEdgeInput arrays into a CanvasFile (nodes and edges) with auto-generated IDs and dagre-based layout via relayoutCanvas().
export function createCanvas( nodes: CanvasNodeInput[], edges: CanvasEdgeInput[], options: { direction?: "TB" | "BT" | "LR" | "RL"; nodeWidth?: number; nodeHeight?: number } = {}, ): CanvasFile { const idMap = new Map<string, string>(); const canvasNodes: CanvasNode[] = nodes.map((node, index) => { const id = node.id ?? makeId(node.label, index); idMap.set(node.label, id); idMap.set(id, id); const type = node.type ?? (node.file ? "file" : node.url ? "link" : "text"); return { id, type, x: 0, y: 0, width: node.width ?? options.nodeWidth ?? 260, height: node.height ?? estimateHeight(node.text ?? node.label, options.nodeHeight ?? 90), label: node.label, text: type === "text" ? node.text ?? node.label : node.text, file: node.file, url: node.url, color: node.color, }; }); const canvasEdges: CanvasEdge[] = edges.map((edge, index) => ({ id: edge.id ?? `edge-${index + 1}`, fromNode: resolveId(idMap, edge.from), toNode: resolveId(idMap, edge.to), label: edge.label, color: edge.color, })); return relayoutCanvas({ nodes: canvasNodes, edges: canvasEdges }, options); } - src/canvas.ts:32-42 (schema)Type definitions CanvasNodeInput and CanvasEdgeInput used for input to createCanvas and patchCanvas.
export type CanvasNodeInput = { id?: string; label: string; type?: "text" | "file" | "link" | "group"; text?: string; file?: string; url?: string; color?: string; width?: number; height?: number; }; - src/tools.ts:1061-1086 (registration)The tool is registered in the registerObsidianTools() function under the name 'obsidian_canvas_create' using the local 'tool' helper.
"obsidian_canvas_create", "Create an Obsidian JSON Canvas from semantic nodes/edges with automatic graph layout.", { vault: vaultArg, path: pathArg, nodes: z.array(z.object({ id: z.string().optional(), label: z.string(), type: z.enum(["text", "file", "link", "group"]).optional(), text: z.string().optional(), file: z.string().optional(), url: z.string().optional(), color: z.string().optional(), width: z.number().optional(), height: z.number().optional(), })).min(1), edges: z.array(z.object({ id: z.string().optional(), from: z.string(), to: z.string(), label: z.string().optional(), color: z.string().optional() })).optional().default([]), direction: z.enum(["TB", "BT", "LR", "RL"]).optional().default("TB"), overwrite: z.boolean().optional().default(false), }, async (args) => { const canvas = createCanvas(args.nodes, args.edges, { direction: args.direction }); const canvasPath = args.path.endsWith(".canvas") ? args.path : `${args.path}.canvas`; return { ...(await vaults.writeText(canvasPath, `${JSON.stringify(canvas, null, 2)}\n`, args.vault, { overwrite: args.overwrite })), summary: { nodes: canvas.nodes.length, edges: canvas.edges.length } }; }, ); - src/canvas.ts:184-207 (helper)The relayoutCanvas() function applies dagre layout to position nodes and compute edge sides. Called by createCanvas.
export function relayoutCanvas( canvas: CanvasFile, options: { direction?: "TB" | "BT" | "LR" | "RL" } = {}, ): CanvasFile { const graph = new dagre.graphlib.Graph(); graph.setGraph({ rankdir: options.direction ?? "TB", nodesep: 70, ranksep: 100, marginx: 20, marginy: 20 }); graph.setDefaultEdgeLabel(() => ({})); for (const node of canvas.nodes) graph.setNode(node.id, { width: node.width, height: node.height }); for (const edge of canvas.edges) graph.setEdge(edge.fromNode, edge.toNode); dagre.layout(graph); const nodes = canvas.nodes.map((node) => { const positioned = graph.node(node.id); return positioned ? { ...node, x: Math.round(positioned.x - node.width / 2), y: Math.round(positioned.y - node.height / 2) } : node; }); const nodeById = new Map(nodes.map((node) => [node.id, node])); const edges = canvas.edges.map((edge) => { const from = nodeById.get(edge.fromNode); const to = nodeById.get(edge.toNode); return from && to ? { ...edge, ...edgeSides(from, to) } : edge; }); return { nodes, edges }; }