D3 Knowledge Graph
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>`;