Skip to content

Latest commit

 

History

History
79 lines (55 loc) · 3.81 KB

File metadata and controls

79 lines (55 loc) · 3.81 KB

paper-mcp

A shared notepad for humans and AI agents — powered by MCP. Built with sketchbook-ui for a hand-drawn aesthetic.

Stack

  • Next.js 15 (App Router) + React 19
  • Convex — real-time backend, no auth required for pages
  • Clerk — auth, protects /dashboard via middleware
  • sketchbook-ui — hand-drawn React component library
  • Caveat (Google Font) — handwriting-style font throughout

Key routes

  • / — 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

Convex schema

  • boards{ code, name, createdAt }, indexed by by_code
  • notes{ boardId, content, color, createdAt, updatedAt }, indexed by_board

Pages are access-controlled by 20-char ID only (no auth). Anyone with the ID can view. Authed users can post notes.

sketchbook-ui Gotchas

Hard-won lessons — read before touching any component.

Button sizes are hardcoded SVG frames

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 div with zoom: 0.72 — this shrinks layout AND visual size. Do NOT use transform: scale() for this; it shrinks visually but doesn't affect layout, causing overflow.
  • The width grows beyond the minimum based on text content width.

Never pass fontFamily via the typography prop

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.

Buttons in flex columns stretch

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.

CSS import path

import "sketchbook-ui/style.css" — NOT dist/lib-styles.css. Import this once in ConvexClientProvider or a root layout.

Toast API

showToast(message, variant, duration) — three positional args, not an object. ToastContainer requires both toasts and onDismiss props from useToast().

Caveat font — load as variable font, no weight array

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.

No global theming via SketchProvider

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 }}.

Cancel button color pattern

colors={{ bg: "#fce4ec", stroke: "#e57373", text: "#2c2416" }}

Dev

npm run dev:stack   # starts Convex + Next.js together
npx convex dev      # Convex only (regenerates _generated/)
npm run dev         # Next.js only

After 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.