Graph Layer

D3 Knowledge Graph

Source Files: graphData.ts, graphWebview.ts Complexity: High

The D3 Knowledge Graph Visualization provides an interactive, force-directed graphic representing your entire note vault. By mapping note-to-note links and note-to-tag relationships as nodes and edges, it visualizes the connections in your knowledge base in real-time.

1. Overview

In a personal knowledge vault, the connections between notes are often more valuable than the individual notes themselves. The knowledge graph makes these connections visible:

  • Tag Nodes: Displays tags as colored nodes.
  • Note Nodes: Displays notes as distinct nodes.
  • Real-Time Links: Draws lines connecting notes to their tags and to other notes they reference via markdown links.
  • Interactive Navigation: Click a node to open the corresponding note directly in your editor. Drag nodes to reshape the graph.

2. Under the Hood & Code Walkthrough

2.1 Graph Data Structuring

The extension builds a list of nodes and edges by scanning all .md files. Tag links are resolved to tag:<tag-name> IDs, while note links are resolved to absolute file paths:

export interface GraphNode {
    id: string;
    type: 'note' | 'tag';
    label: string;
    summary: string | null;
    connections: number;
}

export interface GraphEdge {
    source: string;
    target: string;
    type: 'tag' | 'link';
}

export function buildGraphData(notes: NoteGraphInput[]): GraphData {
    const nodes: GraphNode[] = [];
    const edges: GraphEdge[] = [];
    const tagNodeMap: Map<string, GraphNode> = new Map();
    const noteSet = new Set(notes.map(n => n.filePath));

    for (const note of notes) {
        const noteNode: GraphNode = {
            id: note.filePath,
            type: 'note',
            label: path.basename(note.filePath),
            summary: note.summary,
            connections: 0,
        };
        nodes.push(noteNode);

        // Add Tag Nodes & Connections
        for (const tag of note.tags) {
            const tagId = `tag:${tag}`;
            if (!tagNodeMap.has(tagId)) {
                const tagNode: GraphNode = { id: tagId, type: 'tag', label: tag, summary: null, connections: 0 };
                tagNodeMap.set(tagId, tagNode);
                nodes.push(tagNode);
            }
            tagNodeMap.get(tagId)!.connections++;
            noteNode.connections++;
            edges.push({ source: note.filePath, target: tagId, type: 'tag' });
        }

        // Add Note-to-Note Connections
        for (const linkTarget of note.links) {
            if (noteSet.has(linkTarget)) {
                noteNode.connections++;
                edges.push({ source: note.filePath, target: linkTarget, type: 'link' });
            }
        }
    }
    return { nodes, edges };
}

2.2 Injected D3 Force Layout Integration

To render the graph inside a VS Code webview, the extension reads D3.js and a custom configuration script from the extension's local resources and injects them directly into the webview's HTML structure:

const d3Path = path.join(this.extensionPath, 'resources', 'd3.min.js');
const graphJsPath = path.join(this.extensionPath, 'resources', 'site-template', 'graph.js');

const d3Script = fs.readFileSync(d3Path, 'utf8');
const graphScript = fs.readFileSync(graphJsPath, 'utf8');

this.panel.webview.html = `<!DOCTYPE html>
<html>
<head>
<style>
    body { margin: 0; padding: 0; background: #1e1e1e; overflow: hidden; }
    #graph-container { width: 100vw; height: 100vh; }
</style>
</head>
<body>
    <div id="graph-container"></div>
    <script>window.vscodeApi = acquireVsCodeApi();</script>
    <script>${d3Script}</script>
    <script>window.graphData = ${JSON.stringify(graphData)};</script>
    <script>${graphScript}</script>
</body>
</html>`;