Skip to content
Content
Skill

/canvas-populate

Add content to existing Obsidian Canvas files. Supports all node types: images (with auto aspect ratio detection), text cards, PDFs, wiki notes, web links, Mermaid diagrams, SVGs, GIFs, AI-generated images via banana. Also adds zones (groups), edges between nodes, and imports

From plugin
claude-canvas
2478 skills3 agents1 command1 hook
Install
$ npx -y skills add AgriciDaniel/claude-canvas --skill canvas-populate --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/canvas-populate

Context preview

The summary Claude sees to decide when to auto-load this skill.

Add content to existing Obsidian Canvas files. Supports all node types: images (with auto aspect ratio detection), text cards, PDFs, wiki notes, web links, Mermaid diagrams, SVGs, GIFs, AI-generated images via banana. Also adds zones (groups), edges between nodes, and imports

SKILL.md

canvas-populate.SKILL.md
name: canvas-populate
description: >
  Add content to existing Obsidian Canvas files. Supports all node types:
  images (with auto aspect ratio detection), text cards, PDFs, wiki notes,
  web links, Mermaid diagrams, SVGs, GIFs, AI-generated images via banana.
  Also adds zones (groups), edges between nodes, and imports recent banana images.
  Triggers on: canvas add, add to canvas, put on canvas, canvas zone, canvas
  connect, canvas from banana, add image to canvas, add text to canvas.
user-invocable: false

canvas-populate: Add Content to Canvases

Read `../canvas/references/canvas-spec.md` for the full JSON format before any edit. Read `../canvas/references/performance-guide.md` for node limits and constraints.

---

General Workflow

For every add operation:

1. **Read** the target canvas JSON. Parse `nodes` and `edges` arrays. 2. **Collect existing IDs** to prevent collisions. 3. **Generate a new ID**: `[type]-[slug]-[unix-timestamp]`. 4. **Calculate position** using the auto-positioning algorithm (see canvas orchestrator). 5. **Spacing check**: Verify the calculated position has at least 80px horizontal and 60px vertical gap from ALL existing content nodes. If not, shift the position until spacing is clear. 6. **Append** the new node to the `nodes` array (after any groups — z-index ordering). 7. **Validate**: Run `python3 scripts/canvas_validate.py <path>` — check for overlaps and node count. 8. **Write** the updated canvas JSON. 9. **Report**: "Added [description] to [zone] zone at position ([x], [y])."

---

Node Operations

add image (`/canvas add image [path or url]`)

**Resolve the image:**

  • If URL (starts with `http`): download with `curl -sL [url] -o [media_dir]/[filename]`.

Derive filename from URL path, or use `img-[timestamp].jpg` if unclear.

  • If local path outside canvas dir: `cp [path] [media_dir]/`
  • If already canvas-relative: use as-is.

**Detect aspect ratio:**

python3 -c "from PIL import Image; img=Image.open('[path]'); print(img.width, img.height)"
# or fallback
identify -format '%w %h' [path]

Map to the sizing table in `../canvas/references/canvas-spec.md` (7 ratios + PDF + fallback).

**Create file node** with calculated dimensions. Position using auto-layout.

add text (`/canvas add text [content]`)

{
  "id": "text-[slug]-[timestamp]",
  "type": "text",
  "text": "[content]",
  "x": 0, "y": 0,
  "width": 300, "height": 120
}

For multi-line content, estimate height: count newlines, multiply by 24, add 40px padding. Minimum 120px.

add pdf (`/canvas add pdf [path]`)

Copy to `[media_dir]/` if outside canvas dir. Fixed size: width=400, height=520.

add note (`/canvas add note [wiki-page]`)

1. Search for a file matching the page name (case-insensitive, partial match). 2. Use `"type": "file"` with vault-relative path. Not `"type": "link"` — that's for URLs only. 3. Default size: width=300, height=100.

add link (`/canvas add link [url]`)

{
  "id": "link-[slug]-[timestamp]",
  "type": "link",
  "url": "[url]",
  "x": 0, "y": 0,
  "width": 400, "height": 120
}

Obsidian fetches Open Graph preview automatically.

add mermaid (`/canvas add mermaid [code]`)

Mermaid renders natively in Obsidian text nodes. Wrap in a fenced code block:

{
  "id": "text-mermaid-[timestamp]",
  "type": "text",
  "text": "```mermaid\n[code]\n```",
  "x": 0, "y": 0,
  "width": 500, "height": 400,
  "color": "5"
}

Wider text nodes work better for Mermaid (min 400px wide, 300px tall).

add svg (`/canvas add svg [description or path]`)

  • If path: copy SVG to media dir, add as file node. Width=400, height based on viewBox aspect ratio.
  • If description: delegate to `/svg` skill to generate, then add the output file.
  • SVGs render as `<img>` — no interactivity. Must have `viewBox` attribute.

add gif (`/canvas add gif [description or path]`)

  • If path: copy GIF to media dir, add as file node.
  • If description: delegate to `/claude-gif-generate` skill, then add the output.
  • Performance limit: max 3 GIFs per canvas, max 480px width. Warn if exceeded.

add banana (`/canvas add banana [prompt]`)

1. Check if banana skill is available. If not: "Install the banana skill for AI image generation." 2. Delegate to banana skill with the prompt. 3. Wait for image generation. 4. Add the generated image as a file node (detect aspect ratio, auto-size). 5. Log the path to `.recent-images.txt` in the canvas directory.

---

Zone Operations

zone (`/canvas zone [name] [color]`)

1. Read canvas JSON. 2. Find max_y of all existing nodes. Use `-80` if canvas is empty (consistent with starter canvas layout where title is at y=-300 and default zone at y=-140):

   if not canvas_nodes:
       max_y = -80
   else:
       max_y = max(n["y"] + n.get("height", 0) for n in canvas_nodes) + 60

3. Create group node:

{
  "id": "zone-[slug]-[timestamp]",
  "type": "group",
  "label": "[name]",
  "x": -400,
  "y": "[max_y]",
  "width": 1000,
  "height": 400,
  "color": "[color or '4']"
}

Valid colors: `"1"`=red `"2"`=orange `"3"`=yellow `"4"`=green `"5"`=cyan `"6"`=purple

4. Insert the group node BEFORE content nodes in the array (z-index: groups render behind). 5. Write and report.

---

Edge Operations

connect (`/canvas connect [from] [to] [label]`)

1. Read canvas JSON. 2. Find `from` and `to` nodes by ID, label text, or partial match. 3. If ambiguous, list matches and ask the user to clarify. 4. Create edge:

{
  "id": "e-[from-slug]-[to-slug]-[timestamp]",
  "fromNode": "[from-id]",
  "toNode": "[to-id]",
  "toEnd": "arrow"
}

5. Omit `fromSide`/`toSide` for auto-routing (better results). 6. Add optional `label` if provided. 7. Write and report.

---

Banana Import

from banana (`/canvas from banana`)

1. Check `[canvas_dir]/.recent-images.txt` for recently logged image paths. 2. If not found or empty: search for images modified in the last 10 minu

Read more
Ships withclaude-canvas

AI-orchestrated visual production for Obsidian Canvas. Create presentations, flowcharts, mood boards, knowledge graphs, and galleries with intelligent layout and AI-generated content.

Get the whole plugin

Other skills on claude-canvas.