- Drop a PDF — textbook chapter, lecture notes, anything with a text layer.
- Kortex chunks it — paragraph-aware splitter, ~6000 chars per chunk.
- An LLM generates cards — Gemini 2.5 Flash Lite with schema-constrained JSON returns Q/A, cloze, concept, and example cards.
- SM-2 schedules reviews — the same spaced-repetition algorithm Anki uses.
- Learn → Practice → Browse → Feed — four surfaces for intro, review, discovery, and social context.
| 📚 PDF → Cards pipeline | Paragraph chunker + Gemini 2.5 Flash Lite with Zod-validated JSON schema |
| 🧠 SM-2 spaced repetition | Full Anki-style algorithm with easeFactor, intervalDays, lapses, and custom mastery scoring |
| 🎓 Intro pass | /learn surface separates "meet the card" from "test the card" via a learnedAt gate |
| 🎬 Browse reel | TikTok-style vertical swipe across all decks; ratings write through to SM-2 |
| 📡 Activity feed | Append-only event log: signup, deck created, card learned, card mastered |
| ↩️ Undo reviews | 5-second window after every rating — one mis-click shouldn't cost a week |
| 🔐 Stateless auth | HMAC-signed session cookies, bcrypt password hashing, no session store |
| 🌓 Light + dark mode | Hand-tuned brutalist palette. Violet highlighter in dark mode, acid in light |
| 📱 Mobile-first | Bottom tab bar, safe-area insets, 56px touch targets, responsive across phone/tablet/desktop |
| ⌨️ Keyboard shortcuts | Space flip, 1–4 rate, ↑↓/jk navigate, U undo, ? for the cheat sheet |
| 🎭 Kort the mascot | Brain-blob SVG with six moods (wave, think, cheer, sleep, study, flex) |
Framework Next.js 14 (App Router) + TypeScript 5.6
UI React 18 + Tailwind CSS + Framer Motion
Data better-sqlite3 (WAL mode) + migrations baked in
Auth HMAC session cookies + bcryptjs
LLM Google Gemini 2.5 Flash Lite + Zod schemas
PDF pdf-parse
Full reasoning behind every choice is in DOCS.md (also available as DOCS.docx).
git clone https://github.com/gagan-53/kortex.git
cd kortex
npm install
cp .env.example .env.local # add GEMINI_API_KEY and AUTH_SECRET
npm run devOpen http://localhost:3000, sign up, drop a PDF, and you're cooking.
Grab a free Gemini key at aistudio.google.com.
| Var | Required | Default | Notes |
|---|---|---|---|
GEMINI_API_KEY |
✅ | — | Get one from Google AI Studio |
AUTH_SECRET |
✅ in prod | dev-only-insecure-secret-change-me |
32+ random chars for HMAC signing |
GEMINI_MODEL |
❌ | gemini-2.5-flash-lite |
Override to use Flash / Pro |
npm run dev # Next dev server (HMR)
npm run build # production build
npm start # serve the production build
npm test # node --test on lib/**/*.test.ts
npm run lint # next lintkortex/
├── app/ # Next App Router
│ ├── api/ # Route handlers (auth, upload, review, feed, …)
│ ├── browse/ # TikTok-style reel
│ ├── decks/ # Library
│ ├── deck/[id]/ # Per-deck stats
│ ├── feed/ # Activity timeline
│ ├── learn/[id]/ # Intro pass
│ ├── practice/[id]/ # SM-2 review loop
│ ├── upload/ # PDF picker
│ ├── login/, signup/ # Auth forms
│ ├── layout.tsx # Root layout (logo, nav, mobile tab bar)
│ ├── page.tsx # Home (streak + deck grid)
│ └── globals.css # Design system (brutalist utilities)
├── components/
│ ├── Mascot.tsx # Kort, six moods, SVG
│ ├── MobileNav.tsx # Bottom tab bar
│ ├── DeckBrowser.tsx # Search + sort + deck cards
│ ├── Flashcard.tsx # CSS-grid flip card
│ ├── RatingBar.tsx # again/hard/good/easy
│ ├── Confetti.tsx # done-state burst
│ └── …
├── lib/
│ ├── db.ts # better-sqlite3 + migrations
│ ├── auth.ts # HMAC sessions + bcrypt
│ ├── sm2.ts # SM-2 + mastery + struggleScore
│ ├── chunker.ts # Paragraph-first text splitter
│ ├── llm.ts # Gemini client + Zod schemas
│ └── prompts/ # Generation prompts
├── middleware.ts # Auth presence-check for protected routes
├── DOCS.md / DOCS.docx # Full technical documentation
└── tailwind.config.ts # Brutalist color + shadow tokens
Editorial brutalism. Thick borders, hard offset shadows, rotated stickers, serif headlines paired with mono labels.
| Token | Hex | Role |
|---|---|---|
ink |
#0B0B0F |
near-black, primary text |
bone |
#F4EFE6 |
warm cream, light-mode base |
charcoal |
#14131A |
dark-mode base |
slab |
#1E1D26 |
dark-mode surface |
acid |
#D4FF3B |
electric lime, primary accent |
lava |
#FF4D26 |
hot orange, warnings, "slap" |
violet |
#5B2BFF |
deep violet, dark-mode highlight |
Type stack: Fraunces (display serif) · Inter (body) · JetBrains Mono (tags / kbd).
⚠️ Netlify / Vercel caveat: this app usesbetter-sqlite3with a local DB file. Serverless hosts don't have persistent filesystems. To deploy to Netlify/Vercel you'll need to migrate the data layer to Turso, Supabase, or Neon. See DOCS.md for trade-offs.
Hosts that work out of the box (persistent disk):
- Export deck as Anki
.apkg - PWA with offline review queue
- Multi-modal cards (diagrams extracted from PDFs)
- Per-deck difficulty calibration via IRT
- Shared decks with fork semantics
MIT © gagan-53
Developed by G🥷