Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/redsheep913/Canvas-Card-Materializer/llms.txt

Use this file to discover all available pages before exploring further.

Obsidian Canvas text nodes exist only as raw JSON entries inside a .canvas file. They have no independent presence in your vault — they can’t be linked to, searched, tagged, or versioned as standalone notes. Materialization is the process of extracting those ephemeral text nodes into real .md files that live in your vault like any other note. Once materialized, a node becomes a first-class citizen: it can appear in graph view, receive backlinks, and be opened directly from the file explorer.

How Materialization Works

The plugin executes the transformation in four sequential passes over the selected nodes.

Pass 1 — File Creation

For each selected text node, the plugin:
  1. Reads node.text and splits it on newlines
  2. Takes the first line as the note title, sanitizes it into a safe filename (stripping Markdown heading markers, backticks, and characters illegal on most filesystems)
  3. Truncates the filename to 50 characters to avoid path-length issues
  4. Resolves the target sub-folder from the node’s color property using colorFolderMap
  5. Creates the .md file at <canvas-parent-dir>/<canvas-name>/<ColorFolder>/<filename>.md with the remaining lines as the file body
  6. Records the new TFile object and its metadata in an internal exportMap keyed by the original node ID
If a file already exists at the target path, the plugin overwrites it with vault.modify() rather than throwing an error. Existing file nodes in the selection are handled gracefully — they are added to exportMap as-is so their connections can still be processed, but no new file is created for them.

Pass 2 — Edge Analysis

After all files exist, the plugin inspects canvas.edges — a Set<CanvasEdge> exposed by Obsidian’s internal Canvas API. For each edge where both edge.from.node.id and edge.to.node.id are present in exportMap, it resolves an Obsidian-style wikilink path using:
app.metadataCache.fileToLinktext(targetData.fileObj, sourceData.fileObj.path)
This produces the shortest unambiguous link text (e.g., Related-Note rather than a full vault path), which is then wrapped in [[...]] and queued under the source node’s ID in a linkUpdates map. For every entry in exportMap, the plugin:
  • Calls app.fileManager.processFrontMatter() to safely inject two YAML keys: canvas_id (the original node UUID) and canvas_color (the color code string)
  • Reads the file content, checks for the presence of **Connected Nodes:**, and — if absent — appends the collected wikilinks under a horizontal rule

Pass 4 — Canvas Data Update

The plugin uses canvas.getData() to read the full canvas JSON, then maps over every node:
  • type: group nodes are left completely untouched
  • Nodes whose IDs are in exportMap and are still type: text are rewritten to type: file, with file set to the new .md path and text removed
  • All original position coordinates (x, y, width, height) are preserved via the spread operator (...n)
The updated node list is pushed back with canvas.setData() in two steps: first a node-clearing flush (nodes set to [] while edges and groups are preserved) followed 100 ms later by the full updated node list, to avoid rendering glitches. canvas.requestSave() then persists the changes to disk without a full reload.
After materialization, each converted node changes from type: text to type: file in the canvas JSON. The card still appears at exactly the same position in the visual layout, but it now references an actual vault file rather than storing raw text inline. You can double-click it to open the note in a new pane.
For multi-line cards, the first line becomes the filename and all subsequent lines become the body of the generated Markdown file. For single-line cards, the full text is used as both the filename and the file body. This means a card whose first line is a clear, descriptive heading (e.g., ## Project Goals) will produce a well-named note, while its bullet points, paragraphs, or sub-headings flow naturally into the note body.

Build docs developers (and LLMs) love