@digitalpine/inkwell
v0.1.5
Published
Visual exploration canvas for React codebases. Claude drops .tsx frames into the filesystem and watches them render on an infinite React Flow canvas.
Readme
@digitalpine/inkwell
Visual exploration canvas for React codebases. Claude drops
.tsxframes into the filesystem and watches them render on an infinite React Flow canvas.
Inkwell is a live sketchbook where the filesystem is the API. Drop a .tsx file into frames/<board>/ and it appears on the canvas. Delete it and it disappears. The human watches the sketchbook fill up frame by frame while Claude explores designs.
Install
pnpm add @digitalpine/inkwellThe canvas is designed to be installed by the Inkwell Claude Code plugin, which walks you through onboarding (canvas location, framework, Tailwind wiring, proof frame) and scaffolds everything. You can also wire it up manually — see Manual Wiring below.
What you get
- Infinite canvas — React Flow renderer with drag, zoom, pan, auto-layout
- Filesystem-as-API — frames are
.tsxfiles, canvas state is.canvas.json, layout is.layout.json - Auto-discovery — frames in
frames/**/*.tsxappear automatically - Live protocol — dropping a new frame file shows it within a second, error-isolated
- Annotations — click any element on a frame, leave a comment, Claude picks it up via MCP channel
- Vite plugin — the canvas is a dev server plugin. No build step for your frames.
Package exports
import { Canvas } from "@digitalpine/inkwell/Canvas";
import { useFrameModules } from "@digitalpine/inkwell/hooks";
import { canvasPlugin } from "@digitalpine/inkwell/plugins";
import { canvasMcpPlugin } from "@digitalpine/inkwell/mcp-plugin";
import "@digitalpine/inkwell/inkwell.css";Full export list:
| Entry | Purpose |
|---|---|
| @digitalpine/inkwell/Canvas | Core canvas component |
| @digitalpine/inkwell/FrameWrapper | Frame wrapper with error boundary + entrance animation |
| @digitalpine/inkwell/Annotations | Annotation overlay component |
| @digitalpine/inkwell/hooks | State + layout hooks, pure layout functions |
| @digitalpine/inkwell/plugins | Vite dev server plugin (state, layout, capture, file watching) |
| @digitalpine/inkwell/mcp-plugin | Vite plugin serving MCP over Streamable HTTP for annotation push |
| @digitalpine/inkwell/annotation-utils | Annotation helpers + types |
| @digitalpine/inkwell/components/* | Chrome pieces (sidebar, toolbar, top-bar, primitives, sheets) |
| @digitalpine/inkwell/inkwell.css | Pre-built chrome stylesheet — no Tailwind needed |
Manual wiring
Minimal Vite setup:
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { canvasPlugin } from "@digitalpine/inkwell/plugins";
import { canvasMcpPlugin } from "@digitalpine/inkwell/mcp-plugin";
export default defineConfig({
plugins: [react(), canvasPlugin(), canvasMcpPlugin()],
server: { port: 5173, host: "127.0.0.1" },
});// src/App.tsx
import { Canvas, type BoardMeta } from "@digitalpine/inkwell/Canvas";
import { useFrameModules, type FrameModule, type LazyFrameModules } from "@digitalpine/inkwell/hooks";
const lazyFrameModules = import.meta.glob<FrameModule>("../frames/**/*.tsx") as unknown as LazyFrameModules;
const boardMetaModules = import.meta.glob<{ default: BoardMeta }>("../frames/*/.board.json", { eager: true });
export function App() {
const frameModules = useFrameModules(lazyFrameModules);
return (
<div style={{ height: "100vh", display: "flex", flexDirection: "column" }}>
<Canvas frameModules={frameModules} boardMetaModules={boardMetaModules} />
</div>
);
}/* src/main.css */
@import "@digitalpine/inkwell/inkwell.css";Create frames/01-explore/hello.tsx:
export const meta = { title: "hello", preset: "component" };
export default function Frame() {
return <div style={{ padding: 24 }}>hello from inkwell</div>;
}Run pnpm dev, open the printed URL, and watch frames appear as you add them.
Peer dependencies
| Package | Version |
|---|---|
| react | ^19 |
| react-dom | ^19 |
| @xyflow/react | ^12 |
| @modelcontextprotocol/sdk | ^1.27 |
| vite | ^5 \|\| ^6 \|\| ^7 \|\| ^8 |
| vaul | ^1.1 |
Links
License
MIT © Joel Brubaker
