tiptap graph visualization neuron mvp
Research: TipTap 3.x + Graph Visualization for Neuron Desktop MVP
MOKA-345 | Priority: High | Project: Research Date: 2026-03-20 Author: Deep Research Agent Supports: Neuron — AI-Powered Team Knowledge Graph
Executive Summary
Neuron’s MVP requires two core UI components: a rich-text editor with wiki-style [[backlinks]] and an interactive knowledge graph visualization. This report evaluates TipTap’s extension ecosystem for wiki-link support and compares graph visualization libraries for rendering 1K-10K node graphs in a Tauri desktop WebView.
Recommendations:
- Editor: TipTap with
Mentionextension configured for[[trigger + custom WikiLink node type - Graph: Sigma.js (2D WebGL) for the main graph view, with d3-force for layout computation in a Web Worker
- Backlink storage: SQLite
linkstable with source/target + FTS5 for content search - Architecture: Decouple layout computation (Web Worker) from rendering (WebGL) for smooth 60fps interaction
1. TipTap Editor — Wiki-Link Implementation
1.1 Architecture Overview
TipTap is built on ProseMirror, providing a headless, framework-agnostic rich-text editor. Key concepts for Neuron:
- Extensions — Modular plugins (nodes, marks, functionality)
- Node views — Custom React components rendered inline in the document
- Suggestion utility — Powers autocomplete popups (mentions, slash commands, wiki-links)
1.2 Wiki-Link Strategy: Mention Extension + Custom Configuration
The recommended approach uses TipTap’s built-in Mention extension with [[ as the trigger character:
import { Mention } from '@tiptap/extension-mention'
import { ReactRenderer } from '@tiptap/react'
const WikiLink = Mention.configure({
HTMLAttributes: { class: 'wiki-link' },
renderLabel: ({ node }) => `[[${node.attrs.label}]]`,
suggestion: {
char: '[[',
items: async ({ query }) => {
// Query SQLite for matching note titles
return await searchNotes(query)
},
render: () => {
// Custom React popup component
let component: ReactRenderer
return {
onStart: (props) => {
component = new ReactRenderer(WikiLinkPopup, { props, editor: props.editor })
},
onUpdate: (props) => component.updateProps(props),
onExit: () => component.destroy(),
}
},
},
})
1.3 Existing Community Extensions
| Extension | Status | Notes |
|---|---|---|
tiptap-wikilink-extension | Community, not on npm | Basic [[wiki-link]] support, installable from GitHub |
@tiptap/extension-mention | Official, stable | Configurable trigger char, suggestion popup, customizable rendering |
@tiptap/extension-link | Official, stable | Standard hyperlinks (not wiki-style) |
Recommendation: Use @tiptap/extension-mention (official, maintained) with custom configuration rather than the community wikilink extension (not on npm, limited maintenance). The Mention extension provides the full suggestion pipeline and is production-ready.
1.4 Custom WikiLink Node Type
For full control, create a custom TipTap Node:
import { Node, mergeAttributes } from '@tiptap/core'
export const WikiLink = Node.create({
name: 'wikiLink',
group: 'inline',
inline: true,
atom: true, // Non-editable inline block
addAttributes() {
return {
noteId: { default: null },
label: { default: '' },
}
},
parseHTML() {
return [{ tag: 'span[data-wiki-link]' }]
},
renderHTML({ HTMLAttributes }) {
return ['span', mergeAttributes(
{ 'data-wiki-link': '', class: 'wiki-link' },
HTMLAttributes
), `[[${HTMLAttributes.label}]]`]
},
addNodeView() {
return ReactNodeViewRenderer(WikiLinkComponent)
},
})
1.5 TipTap Pro Extensions (Paid)
TipTap offers Pro extensions that may be relevant:
- Collaboration (Hocuspocus) — Real-time CRDT sync (useful for future team features)
- AI Autocompletion — Content generation inline
- Comments — Inline annotations
For Neuron MVP, the free/open-source extensions are sufficient. Pro extensions become relevant when adding team collaboration.
2. Graph Visualization — Library Comparison
2.1 Library Matrix
| Library | Renderer | React | Max Nodes (smooth) | 3D | Bundle Size | License |
|---|---|---|---|---|---|---|
| Sigma.js | WebGL | @sigma/react | 50K+ nodes | No | ~50KB | MIT |
| Reagraph | WebGL (Three.js) | Native | ~10K nodes | Yes | ~200KB | Apache 2.0 |
| react-force-graph | Canvas/WebGL | Native | ~10K (2D), ~5K (3D) | Yes | ~150KB | MIT |
| Cytoscape.js | Canvas | react-cytoscapejs | ~5K nodes | No | ~400KB | MIT |
| D3-force | SVG/Canvas | Manual | ~2K (SVG), ~5K (Canvas) | No | ~30KB | ISC |
| Cosmograph | WebGL (GPU) | Yes | 1M+ nodes | No | ~100KB | Commercial |
2.2 Performance Benchmarks (WebView Context)
Based on rendering technology research:
| Technology | Limit (smooth 60fps) | Notes |
|---|---|---|
| SVG | ~2K nodes, 2K edges | DOM-based, good for small interactive graphs |
| Canvas | ~5K nodes, 5K edges | Pixel-based, good balance of perf and interaction |
| WebGL | ~10K nodes, 11K edges | GPU-accelerated, best for large graphs |
| WebGL + GPU layout | 100K+ | Requires specialized libraries (Cosmograph, PIXI.js) |
Tauri WebView considerations:
- macOS (WKWebView): Excellent WebGL support, hardware acceleration default
- Windows (WebView2/Chromium): Full WebGL 2.0 support
- Linux (WebKitGTK): WebGL support varies by system; test with fallback
- Known issue: Tauri has had WebGL context loss reports on some Windows configurations
2.3 Recommended: Sigma.js + d3-force
Why Sigma.js for Neuron:
- Performance — WebGL rendering handles 50K+ nodes, far exceeding Neuron’s needs
- React bindings —
@sigma/reactprovides clean declarative API - Lightweight — ~50KB vs 200KB+ for Three.js-based alternatives
- 2D focus — Knowledge graphs are inherently 2D; 3D adds visual complexity without UX benefit
- Edge rendering — Supports curved edges, labels, and custom node shapes
- Layout separation — Uses external layout algorithms (d3-force, graphology-layout-forceatlas2)
Architecture pattern:
┌────────────────────────────────────────────┐
│ Main Thread │
│ ┌──────────────┐ ┌───────────────────┐ │
│ │ Sigma.js │ │ React UI │ │
│ │ (WebGL │ │ (TipTap editor, │ │
│ │ rendering) │ │ sidebar, search) │ │
│ └──────┬───────┘ └───────────────────┘ │
│ │ │
│ │ graph data (graphology) │
│ │ │
│ ┌──────┴───────┐ │
│ │ Graphology │ ←── In-memory graph │
│ │ (graph model) │ model │
│ └──────┬───────┘ │
│ │ │
└─────────┼──────────────────────────────────┘
│ postMessage
┌─────────┼──────────────────────────────────┐
│ Web Worker │
│ ┌──────┴───────┐ │
│ │ ForceAtlas2 │ ←── Layout computation │
│ │ (graphology- │ off main thread │
│ │ layout) │ │
│ └──────────────┘ │
└────────────────────────────────────────────┘
│
│ IPC (Tauri commands)
┌─────────┼──────────────────────────────────┐
│ Rust Backend │
│ ┌──────┴───────┐ ┌───────────────────┐ │
│ │ SQLite │ │ File watcher │ │
│ │ (notes, links │ │ (markdown files) │ │
│ │ FTS5 index) │ │ │ │
│ └──────────────┘ └───────────────────┘ │
└────────────────────────────────────────────┘
2.4 Alternative: Reagraph (If 3D Desired)
Reagraph provides out-of-the-box React graph visualization with:
- 2D and 3D modes (Three.js/WebGL)
- Force-directed, radial, and hierarchical layouts
- Clustering and selection
- ~10K node performance
Choose Reagraph over Sigma.js only if the team wants 3D graph exploration as a differentiator.
3. SQLite Backlink Schema
3.1 Schema Design
-- Notes table
CREATE TABLE notes (
id TEXT PRIMARY KEY, -- UUID or nanoid
title TEXT NOT NULL,
content TEXT, -- TipTap JSON or Markdown
content_plain TEXT, -- Plaintext for FTS
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
-- Links table (bidirectional via query)
CREATE TABLE links (
id INTEGER PRIMARY KEY AUTOINCREMENT,
source_id TEXT NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
target_id TEXT NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
context TEXT, -- Surrounding text snippet
position INTEGER, -- Character offset in source document
created_at TEXT DEFAULT (datetime('now')),
UNIQUE(source_id, target_id, position)
);
-- Full-text search
CREATE VIRTUAL TABLE notes_fts USING fts5(
title,
content_plain,
content='notes',
content_rowid='rowid'
);
-- Indexes
CREATE INDEX idx_links_source ON links(source_id);
CREATE INDEX idx_links_target ON links(target_id);
CREATE INDEX idx_notes_title ON notes(title);
3.2 Backlink Queries
-- Get all backlinks to a note (who links TO this note)
SELECT n.id, n.title, l.context
FROM links l
JOIN notes n ON n.id = l.source_id
WHERE l.target_id = ?;
-- Get all outgoing links from a note
SELECT n.id, n.title, l.context
FROM links l
JOIN notes n ON n.id = l.target_id
WHERE l.source_id = ?;
-- Build graph data (all nodes and edges)
SELECT
source_id || '→' || target_id AS edge_id,
source_id,
target_id
FROM links;
-- Search notes by title (for wiki-link autocomplete)
SELECT id, title FROM notes_fts WHERE title MATCH ? || '*' LIMIT 10;
-- Find orphan notes (no incoming or outgoing links)
SELECT n.id, n.title
FROM notes n
LEFT JOIN links l1 ON l1.source_id = n.id
LEFT JOIN links l2 ON l2.target_id = n.id
WHERE l1.id IS NULL AND l2.id IS NULL;
3.3 Link Extraction Pipeline
When a note is saved:
- Parse TipTap JSON document for
wikiLinknodes - Resolve
noteIdfrom title (create note stub if not found) - Delete existing links for this source note
- Insert new links with context snippets
- Update FTS index
- Emit graph update event to frontend
async function extractLinks(noteId: string, doc: JSONContent): Promise<Link[]> {
const links: Link[] = []
function walk(node: JSONContent, path: number[] = []) {
if (node.type === 'wikiLink' && node.attrs?.noteId) {
links.push({
sourceId: noteId,
targetId: node.attrs.noteId,
context: getContextSnippet(doc, path),
position: getCharOffset(doc, path),
})
}
node.content?.forEach((child, i) => walk(child, [...path, i]))
}
walk(doc)
return links
}
4. UX Patterns from Obsidian / Logseq / Notion
4.1 Graph View UX Patterns
| Feature | Obsidian | Logseq | Notion | Neuron (Recommended) |
|---|---|---|---|---|
| Graph layout | Force-directed (custom) | Force-directed | N/A | ForceAtlas2 (graphology) |
| Renderer | Canvas (custom) | Canvas | N/A | WebGL (Sigma.js) |
| Node sizing | By link count | By link count | N/A | By link count + recency |
| Node coloring | By folder/tag | By namespace | N/A | By tag/cluster |
| Edge display | Lines | Lines | N/A | Curved lines + labels |
| Local graph | Yes (per-note) | Yes (per-page) | N/A | Yes (1-2 hop radius) |
| Global graph | Yes (entire vault) | Yes (entire graph) | N/A | Yes (with clustering) |
| Filters | Tags, folders, orphans | Namespaces, built-in | N/A | Tags, date range, orphans |
| Search highlight | No | No | N/A | Yes (highlight search results) |
| Animation | Physics simulation | Physics simulation | N/A | Smooth transitions |
4.2 Key UX Decisions for Neuron
- Local graph by default — Show 1-2 hop neighborhood of current note (less overwhelming than full graph)
- Progressive disclosure — Global graph as opt-in view with clustering for large vaults
- Node size = link count — Visual weight indicates importance
- Hover preview — Show note title + first paragraph on node hover
- Click to navigate — Clicking a node opens the note in the editor
- Bidirectional panel — Sidebar showing backlinks + outgoing links for current note
- Search-to-graph — Search results highlighted on the graph view
4.3 Obsidian’s Graph Architecture (Reference)
Obsidian uses:
- Custom Canvas renderer (not a third-party library)
- Force-directed layout with custom physics
- JSON Canvas spec for spatial canvases (separate from graph view)
- Plugins can extend graph (Neo4j Graph View, Extended Graph)
- Performance: handles vaults of 10K+ notes
Neuron can match this with Sigma.js + Graphology while benefiting from:
- WebGL (faster than Obsidian’s Canvas for large graphs)
- Graphology’s rich algorithm library (centrality, community detection, PageRank)
- ForceAtlas2 (better clustering than basic D3-force)
5. Implementation Roadmap for Neuron MVP
Phase 1: Editor + Links (Weeks 1-2)
- Set up TipTap with StarterKit + Markdown extensions
- Implement WikiLink node (Mention extension with
[[trigger) - SQLite schema: notes, links, FTS5
- Link extraction pipeline (save → parse → store links)
- Backlink sidebar panel
Phase 2: Graph View (Weeks 3-4)
- Integrate Sigma.js with
@sigma/react - Graphology model synced with SQLite link data
- ForceAtlas2 layout in Web Worker
- Local graph (per-note, 2-hop radius)
- Node hover preview + click navigation
Phase 3: Polish (Weeks 5-6)
- Global graph with clustering
- Tag-based node coloring
- Search-to-graph highlighting
- Graph filters (date range, orphans, tags)
- Keyboard shortcuts (Cmd+K for quick link, Cmd+G for graph)
NPM Dependencies
{
"@tiptap/react": "^2.11",
"@tiptap/starter-kit": "^2.11",
"@tiptap/extension-mention": "^2.11",
"@tiptap/extension-placeholder": "^2.11",
"@sigma/react": "^1.0",
"sigma": "^3.0",
"graphology": "^0.25",
"graphology-layout-forceatlas2": "^0.10",
"graphology-communities-louvain": "^2.0",
"graphology-metrics": "^2.0"
}
Rust Dependencies (Tauri Backend)
[dependencies]
rusqlite = { version = "0.32", features = ["bundled"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
uuid = { version = "1", features = ["v4"] }
notify = "7" # File system watcher
6. Risk Assessment
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Sigma.js WebGL context loss on Windows | Low-Medium | Medium | Implement Canvas fallback; test on WebView2 |
TipTap Mention [[ double-char trigger edge cases | Medium | Low | Thorough testing; custom input rule as fallback |
| ForceAtlas2 slow for 10K+ nodes | Low | Medium | Web Worker + progressive loading; limit visible nodes |
| SQLite lock contention (concurrent reads/writes) | Low | Medium | WAL mode; batch link updates |
| Graph layout jitter on data updates | Medium | Low | Animate transitions; freeze layout on interaction |
Sources
- TipTap Extensions Overview
- TipTap Mention Extension
- TipTap Suggestion Utility
- TipTap Custom Extensions
- TipTap Node Views
- TipTap WikiLink Extension (Community)
- TipTap
[[Trigger Discussion - Sigma.js
- Reagraph (WebGL React Graphs)
- react-force-graph
- Cytoscape.js
- Graph Visualization Performance Comparison (Cylynx)
- Rendering Large Force Graphs (Medium)
- Scale Up D3 with PIXI.js (GraphAware)
- Top 10 JS Libraries for Knowledge Graph Visualization
- Simple-Graph: SQLite Graph DB
- Personal Knowledge Graphs with libSQL (Turso)
- Obsidian Graph View
- Tauri Architecture
- Tauri WebGL Issues
- Graph Visualization Efficiency Study (PMC)
- React Graph Visualization Guide (Cambridge Intelligence)