feat(academy): add AI flashcard studio with Anki export#4229
Open
federiconardelli7 wants to merge 20 commits into
Open
feat(academy): add AI flashcard studio with Anki export#4229federiconardelli7 wants to merge 20 commits into
federiconardelli7 wants to merge 20 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f0b2ee53ab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds AI-powered flashcards to the Builders Hub Academy across five connected surfaces — on-demand Anki export, an AI generation studio, an in-app study player, per-course standard decks,
and a personal deck library — plus a one-line Turbopack build unblock.
What ships
1. Anki download for existing in-app flashcards. The existing
<Flashcard flashcardSetId="..." />component gets an Anki icon in its header. Clicking it downloads a.apkgbuilt ondemand from
flashcardData.json. Anonymous, no rate limit, idempotent. Route:GET /api/flashcards/download/[setId].2. Flashcard Studio.
/academy/flashcardslets logged-in users multi-select chapters from any Academy course (search + collapsible tree), pick a target card count (10–50), andPOST /api/flashcards/generate. The route reads selected MDX, strips JSX (preserving fenced code blocks), builds a prompt for Claude (claude-sonnet-4-6via@ai-sdk/anthropic), validateswith Zod, dedupes by
text-embedding-3-smallcosine similarity (threshold 0.88), and returns the deck. Source input is hard-capped at 100k tokens. Preview at/academy/flashcards/[sessionId]renders Q&A, cloze, and code cards with per-card keep / reject / regenerate (single-card regen viaPOST /api/flashcards/regenerate-card). Finaldeck downloads as
.apkg(POST /api/flashcards/studio-download). Rate-limited per authenticated user: 10 deck generations / 24h, 30 card regenerations / hr.3. In-app study player.
/academy/flashcards/play/[setId](standard decks) and/academy/flashcards/play/user/[deckId](saved decks) provide a full study mode — flip,spaced-repetition rating buckets, progress tracking, and keyboard shortcuts (Space to flip, 1/2/3 to rate, arrows to navigate), with responsive mobile layout.
4. Standard per-course decks. Each Academy course ships a baked ~50-card standard deck (resolved via
lib/flashcards/catalog.ts+deck-resolver.ts), surfaced through a deck pickerand the flashcards dropdown in the sidebar actions.
5. Deck library + profile "My Decks".
/academy/flashcards/librarybrowses available decks; the profile gains a My Decks tab (MyDecksCard) listing user-saved decks. Saved decksand per-card ratings persist client-side via IndexedDB (v4 schema,
setId:cardIndexkeys) — no Prisma migration.6. Coverage scripts.
yarn generate:flashcards(single source) and a batch generator walk MDX undercontent/academy/..., run the same pipeline, and write intoflashcardData.jsonso the existing
<Flashcard />picks them up. Maintainer-run, not CI-auto — not invoked in this PR.Architecture
lib/flashcards/(13 modules): Zodtypes, MDXsource-loader(JSX stripping, fenced-code-safe),promptbuilders (full-deck + single-card), embeddingdedupe,.apkgbuilder,rate-limit, zustand +sessionStoragestudiostore,legacy↔ richFlashcardadapter,catalog/deck-resolver/course-pathfor standard decks, anderrors(sanitizes API keys, stacks, and internal paths out of all client-facing errors).
utils/quizzes/indexedDB.ts, v4). Studio drafts stay session-scoped insessionStorage.anki-apkg-export@4.0.3(MIT).genanki-jsis AGPL-3.0 and would contaminate this BSD-3-Clause repo, so it was rejected. Minimaldeclare moduleshim attypes/anki-apkg-export.d.ts.tests/unit/flashcards/: types adapter, source-loader, dedupe, legacy adapter,.apkground-trip, catalog, course-path, deck-resolver, study-state, errorsanitization, and both generate + download API routes (mocked NextAuth + LLM — covers auth gate, schema validation, rate-limit 429, token-budget, and success paths).
Build / config changes
next.config.mjs: externalizeanki-apkg-exportfor the production server build.package.jsonpostinstall: removes a stale nested@avalanche-sdk/client/node_modules/@noble/secp256k1@3.0.0directory so Turbopack's strict nested resolution doesn't crash onProjectivePoint. Master-wide issue, not introduced here; idempotent,yarn.lock-neutral.Test plan
npx vitest run tests/unit/flashcards— all greenyarn tsc --noEmitclean.apkgopens in Anki desktop/academy/flashcardslogged out → button reads "Sign in to generate"; logged in → enabled.apkgopens withsurviving cards
POST /api/flashcards/generatewithout a session → 401; 11th generation in 24h → 429/academy/flashcards/[sessionId]mid-edit → zustand rehydrates fromsessionStorageNotes for reviewers
yarn.lockis managed separately per repo convention; the newanki-apkg-exportdep is added topackage.jsonand the lockfile is regenerated + committed outside these commits.@noble/secp256k1postinstall is the minimum-touch Turbopack unblock; the cleaner long-term fix is bumping@avalanche-sdk/clientto0.1.1stable in a separate PR.