Graph visualization for Craft documents
Graft creates an interactive graph of your Craft document connections, making it easy to see how your notes relate to each other.
Interactive 2D force-directed graph showing document connections
- Interactive Graph: Force-directed layout shows document relationships
- Privacy First: All API calls happen in your browser - neither your API URL nor the content of your notes never touches our servers
- Progressive Fetching: Update graph on demand without re-fetching the entire graph
- Node Preview: Click any node to see connections to other documents
- Graph Statistics: See connection counts, orphan nodes, and most-connected documents
- Direct Links: Open documents directly in Craft app
- 3D Visualization: View your graph in 3D with orbit controls
- Fun Effects: "New Year" mode with bloom and colored nodes (pro tip: you can use it on second monitor during that holiday season π)
- A Craft account
- Your Craft API URL (from Craft Imagine)
- Your Craft API Key (from Craft Imagine)
Here is where you get your Craft API URL and Key in Imagine
- Connect to Craft: Enter your Craft API URL and Key
- Fetching Documents: Retrieval will start automatically after connecting
- Extracting Links: The app will parse your notes and extract links between documents
- Building Graph: The app will create nodes and edges from document relationships
- Visualizing: The app will render an interactive force-directed graph
- Refreshing: You can refresh the graph data and re-render the graph on demand. The app uses stored
last modified attimestamps to perform incremental updates, only fetching documents that have changed since the last sync - Disconnecting: You can disconnect from Craft, which will remove the credentials from the browser local storage and clear the graph data
- Clearing Cache: You can also just clear the cache and refresh the graph on demand, e.g. if there are issues
Paste your Craft API credentials and click "Save connection"
To run Graft locally, you can clone this repository and run the development server with bun:
# Install dependencies
bun install
# Run development server
bun devTo deploy Graft, you can use Vercel:
- Push to GitHub (the repo must not be public)
- Import to Vercel via dashboard on Vercel
- Deploy (no configuration needed -
vercel.jsonhandles Bun setup)
- Runtime: Next.js 16 on Bun (via Vercel)
- Graph: react-force-graph-2d & react-force-graph-3d
- UI: shadcn/ui with Craft-inspired design
- Analytics: Vercel Analytics
Browsers block direct requests from one domain (like graft.to) to another (like Craft's API) due to CORS security policies. To work around this, Graft uses a proxy:
- Your browser stores your API credentials in
localStorage(never sent to our servers for storage) - When making API calls, your browser sends requests to our Next.js server at
/api/craft/[...path]with your credentials in request headers - Our server immediately forwards the request to Craft's API using those credentials
- Craft's API responds to our server, which then forwards the response back to your browser
Your credentials exist only in the request headers during this process - they're never logged, stored in a database, or persisted on the server. The proxy is just a pass-through that enables the browser to communicate with Craft's API.
Feel free to review our code, find bugs and create issues if you find any.
Graft stores document metadata (including lastModifiedAt timestamps) in IndexedDB. When refreshing the graph, it performs incremental updates by:
- Comparing timestamps: Compares cached
lastModifiedAtwith current document timestamps - Detecting changes: Only re-fetches documents that have been modified, added, or deleted
- Efficient updates: Avoids re-fetching unchanged documents, significantly reducing API calls
This enables fast refresh operations that only update what's changed since the last sync, rather than rebuilding the entire graph from scratch.
The core graph processing logic lives in lib/graph/ and is framework-agnostic. You can use the logic and build it into your own app. Or you can extract and use this library independently:
import { createFetcher } from '@/lib/graph'
const fetcher = createFetcher(apiUrl, apiKey)
const graph = await fetcher.buildGraph({
onProgress: (current, total, message) => {
console.log(`${current}/${total}: ${message}`)
}
})lib/graph/ # Standalone graph library
βββ types.ts # TypeScript types
βββ parser.ts # Link extraction and graph building
βββ fetcher.ts # Craft API client
βββ cache.ts # IndexedDB caching layer
βββ index.ts # Public exports
components/
βββ graph/ # Graph visualization components
β βββ force-graph.tsx # 2D graph component
β βββ force-graph-3d.tsx # 3D graph component
β βββ graph-controls.tsx # Connection & settings UI
β βββ node-preview.tsx # Node detail preview
βββ ui/ # shadcn/ui components
app/
βββ page.tsx # Main graph visualization page
βββ api/ # API proxy routes
βββ craft/ # Craft API proxy
- API credentials (URL and key) stored in browser
localStorageonly - Credentials passed via headers, never stored on server
- Proxy route forwards requests without logging or persistence
- No database, no data retention
Graft uses Vercel Analytics to collect anonymous usage metrics. This helps us understand how the application is being used and identify areas for improvement.
What we collect:
- Page views and navigation patterns
- Basic device information (browser type, screen size)
- Performance metrics (page load times, Core Web Vitals)
- Geographic region (country-level, not precise location)
- Custom events:
- "Connection Success": Tracks when a user successfully connects to Craft API (timestamp only, no credentials or user data)
- "Session End": Tracks session duration in seconds when user leaves the page
- "Open in Craft": Tracks when users click to open a document in Craft app (only the click count, no document information)
What we do NOT collect:
- Content from your Craft documents
- Your Craft API credentials or API keys
- Document titles, IDs, or any document metadata
- Any data stored in your browser's
localStorageorIndexedDB - Personal information (names, email addresses, IP addresses)
Privacy notes:
- All analytics data is aggregated and anonymized
- No personal data is sent to 3rd party telemetry services
- Your Craft document content never leaves your browser
- You can block analytics using ad blockers or browser privacy settings - the application will continue to function normally
The analytics script is served from /_vercel/insights/script.js and may be blocked by privacy-focused browser extensions. This is expected behavior and will not affect the functionality of Graft. Feel free to review the code to check our claims. If you find any issues, please let us know.
MIT
I have built the project as an experiment, motivated by Craft's winter hackathon challenge. If the project will get any significant traction, I will continue to develop it into a product, as there are many more features one can imagine to build on top of this foundation. For example:
- RAG-based similarity graph for linking even unlinked docs
- Linking proposals based on semantic similarity
- Summaries of recent changes in your space
- Themes support (see my Craft document for inspiration images, scroll down)
- More visualization options
- Augmented reality support - imagine seeing your graph with documents and notes in your AR glasses!
