A shared notepad for humans and AI agents — powered by MCP. Built with sketchbook-ui for a hand-drawn aesthetic.
- Next.js 15 (App Router) + React 19
- Convex — real-time backend, no auth required for pages
- Clerk — auth, protects
/dashboardvia middleware - sketchbook-ui — hand-drawn React component library
- Caveat (Google Font) — handwriting-style font throughout
/— landing page: create a page or open by ID/dashboard— authed user's page library (requires Clerk sign-in)/page/[code]— the page itself (real-time via Convex subscriptions)/sign-in/[[...sign-in]]— Clerk sign-in
boards—{ code, name, createdAt }, indexed byby_codenotes—{ boardId, content, color, createdAt, updatedAt }, indexedby_board
Pages are access-controlled by 20-char ID only (no auth). Anyone with the ID can view. Authed users can post notes.
Hard-won lessons — read before touching any component.
sm = 180×70px min, md = 220×90px, lg = 260×110px. There is no xs.
- To use buttons in tight spaces (e.g. modal footers), wrap them in a
divwithzoom: 0.72— this shrinks layout AND visual size. Do NOT usetransform: scale()for this; it shrinks visually but doesn't affect layout, causing overflow. - The width grows beyond the minimum based on text content width.
sketchbook-ui measures button text width using a hidden <span> before React renders. CSS variables like var(--font-hand) are not resolved at that point, so the measurement is wrong and the button frame clips the text. Just omit fontFamily — the library already uses Caveat by default.
If a Button is inside a flex column container, it stretches to full width (SVG frame stays narrow, text gets mis-aligned). Fix: add alignItems: "flex-start" to the flex container.
import "sketchbook-ui/style.css" — NOT dist/lib-styles.css. Import this once in ConvexClientProvider or a root layout.
showToast(message, variant, duration) — three positional args, not an object.
ToastContainer requires both toasts and onDismiss props from useToast().
const caveat = Caveat({ variable: "--font-hand", subsets: ["latin"], display: "swap" });Do NOT pass a weight array. sketchbook-ui also loads Caveat from Google CDN; if Next.js loads a different variant (static weights), the two instances conflict and text metrics differ. Variable font + display: "swap" avoids this.
SketchProvider only provides toast context — it is NOT a theme provider. All theming is per-component via colors={{ bg, stroke, text, bgOverlay }} and typography={{ fontSize, fontWeight, textTransform }}.
colors={{ bg: "#fce4ec", stroke: "#e57373", text: "#2c2416" }}npm run dev:stack # starts Convex + Next.js together
npx convex dev # Convex only (regenerates _generated/)
npm run dev # Next.js onlyAfter changing convex/schema.ts or adding Convex functions, run npx convex dev to regenerate convex/_generated/.
This project uses Convex as its backend.
When working on Convex code, always read convex/_generated/ai/guidelines.md first for important guidelines on how to correctly use Convex APIs and patterns. The file contains rules that override what you may have learned about Convex from training data.
Convex agent skills for common tasks can be installed by running npx convex ai-files install.