Dr. Claw
아이디어부터 논문까지 AI 에이전트와 함께하는 풀스택 연구 작업공간
액티브 리콜로 플래시카드 학습, 답을 말하거나 입력하면 KI가 문장별로 채점하고 Anki 가져오기와 간격 반복을 지원한다
Karteikarten lernen mit Active Recall: Antwort sprechen oder tippen, die KI prüft Satz für Satz gegen deine Lösung. Mit Anki-Import und Spaced Repetition.
교육TypeScript · CSSGitHub @niklasafink온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A deliberately simple flashcard web app with one twist: you store your own solution for each card, answer by speaking or typing, and an AI marks which parts of your solution you actually covered, including things you said in your own words.
Every run is saved per card, so you can see whether you are getting better over time.
| Result after answering | History of one card |
|---|---|
The interface is in German. It runs in any modern browser and is laid out for iPad first, so it works on tablets, phones, Mac and Windows alike. On iPad you can add it to the home screen.
[[word]], or Anki's {{c1::word}}); each gap becomes a card that asks for itRequirements: Node.js 22 or newer and pnpm.
pnpm install
cp .env.example .env.local # then add your OpenRouter key
pnpm dev
Open http://localhost:3000.
| Variable | Required | Default | Purpose |
|---|---|---|---|
OPENROUTER_API_KEY | yes | Key from openrouter.ai/keys | |
GRADING_MODEL | no | openai/gpt-6-luna | Model that compares answer and solution |
TRANSCRIBE_MODEL | no | google/gemini-3.8-flash | Model that transcribes recordings; must accept audio input |
TANDEM_MODEL | no | the grading model | Model that talks in the study tandem |
SPEECH_MODEL | no | elevenlabs/eleven-v4-turbo | Text-to-speech model on OpenRouter that voices the tandem |
PAID_EMAILS | no | With accounts on: comma-separated e-mail addresses of paid accounts. Everyone else is on the free plan | |
ADMIN_EMAILS | no | With accounts on: comma-separated e-mail addresses of whoever runs the app: everything of the paid plan and no limits |
With accounts on, AI use is limited per account: gradings, explanations and turns of the study tandem per month (AI_LIMITS in
src/server/quota.ts: 500 paid; 1000 free as a guard against scripts, since free accounts use free
and very cheap models), card generation and pictures read by the AI per day.
Without accounts (local mode) nothing is counted.
| FREE_PLAN_GRADING_MODELS | no | two free models, then a very cheap one | Comma-separated models that grade for the free plan, tried in order |
On the free plan the browser's own speech recognition transcribes (Chrome, Edge, Safari), and grading uses free OpenRouter models, falling back to a very cheap one when they are used up. Without accounts (local mode) the plans do not apply: your own key pays, the full models run.
Browsers only allow microphone access over HTTPS or on localhost. To test on an iPad in your
local network, run pnpm dev --experimental-https and open the https:// address, or deploy
the app.
The app runs in one of two modes, decided by the environment:
Local mode (default). Folders, cards and attempts are stored in your browser (IndexedDB). No account, no server database, no sync. Use Einstellungen → Backup to move data or keep a copy.
Server mode. With a Postgres database and Neon Auth configured, users sign up and sign in, and their library syncs between all their devices. The browser still keeps a local copy and works on it directly, so the app stays fast; changes are sent to the server in the background.
Sent to the AI provider in both modes: the question, your solution and your answer for grading, the audio recording for transcription, the text of material you turn into cards, and in the study tandem the text of the folder's cards together with the conversation. Pictures in cards are never sent to the AI.
DATABASE_URL, NEON_AUTH_BASE_URL and NEON_AUTH_COOKIE_SECRET into .env.local
(see .env.example). With the Neon CLI linked to the project, neon env pull writes the
first two.pnpm db:migratepnpm dev:https and open the https:// address. It now asks you to sign in.
The session cookie is a secure cookie; over plain http:// some browsers (Safari) do not
keep it, and the sign-in then appears to do nothing.Data you created in local mode before your first sign-in on a device is added to your account.
Signing out removes the local copy from that device; the library stays in the account. The
settings offer changing the password and deleting the account (the server checks the password, then
removes library, pictures, shares and tokens and finally the sign-in account in the neon_auth
schema; other devices are signed out within five minutes); "Passwort vergessen" on the sign-in page
sends a reset link through Neon Auth.
The app exposes an MCP endpoint at /api/mcp, built with
the official TypeScript SDK. An assistant connected to it can read the library and learning
history and create, edit, move and delete folders and cards, for example to turn lecture
notes into flashcards. Changes show up in the app on the next sync.
In the app, open Einstellungen → Zugriff für Claude (MCP) and create an access token. It is shown once.
Connect your client. For Claude Code:
claude mcp add --transport http kiku https://your-host/api/mcp \
--header "Authorization: Bearer kk_..."
A token has full access to its account, including deleting; revoke it in the same place. Clients that can only connect through OAuth (rather than a header) are not supported yet.
Tools: get_overview, list_cards, get_card, create_folder, update_folder,
delete_folder, create_cards, update_card, delete_card.
correct,
missing or unsure.This way the solution you see is always your own text, never a rewritten version. The score
is the share of the solution (by characters) that was labelled correct.
Limits: grading quality depends on the model; it can be wrong, which is why uncertain parts are shown in red rather than guessed.
Question and solution are stored as plain text with a small Markdown subset (**bold**, *italic*,
==marked==, `code`, $formulas$, # headings, - and 1. lists, tables, pictures). The
editor shows the formatted card and translates to and from that markup (a button switches to the
source); a card without any markup stays the plain text it always was. Import, export, search
and the MCP tools all work on this one string.
Pictures are scaled down in the browser (longest edge 1600 px, WebP) and kept in IndexedDB. A card
refers to one as . With an account each picture is uploaded once
(/api/images/<id>) and fetched by other devices when a card shows it. Backups contain them.
For grading, the solution is sent without markup and without pictures: the AI compares text. A card whose solution is only a picture can be revealed but not graded.
An Anki package is a ZIP archive with the collection as an SQLite database. It is read in the
browser without a database library: src/lib/sqlite.ts scans the tables, src/lib/anki.ts turns
notes into cards. Both package formats work, the older one and the Zstandard-compressed one that
current Anki versions write.
src/lib/html.ts); colours, fonts and audio are dropped./api/read-image hands a picture to the transcription model and returns the text on it. It is
used for handwritten answers (src/components/Handwriting.tsx, src/lib/ink.ts: strokes are kept
as points, painted with the pen's pressure and sent as one cropped picture) and for pages of
study material without a text layer (src/lib/material.ts). With accounts on, the number of
pictures per day is limited (IMAGE_LIMITS in src/server/quota.ts).
/sw.js (src/app/sw.js/route.ts) is a service worker that answers from the network while there
is one and from its cache otherwise. The data is in IndexedDB anyway, so what it stores are the
pages of the app. Pages that carry an id in their address (/f/<id>, /c/<id>) are identical
apart from that id; for one that was never opened the worker takes the stored copy of another id
and swaps the id. src/lib/offline.ts loads one page of each kind after a new version is
installed. Every deployment has its own cache. Transcription and sync need the network. An answer
given without one is kept in IndexedDB (pending table) and graded by src/lib/pendingGrades.ts
when the app is opened or the network returns; the attempt keeps the time the answer was given.
A share is a random token for a folder (src/server/shares.ts). /s/<token> shows the folder
with its subfolders as it is at that moment, without an account. Pictures are served through the
link only if one of the shared cards shows them. Copying creates new folders, cards and pictures
in the reader's library; the original owner's star and learning history are not part of it.
Nothing extra is stored for scheduling: a card's next date follows from its attempts, replayed through FSRS with the score as the rating (below 40 % again, below 70 % hard, below 90 % good, otherwise easy). Every device therefore arrives at the same dates, and history from before this feature counts.
"Heute lernen" contains the cards that are due and today's share of the cards never answered:
10 per day (adjustable per device in the settings), or, if the folder (or a parent) has an exam date, the remaining new cards spread
evenly over the days until three days before the exam. In those last three days every card
comes up once more. The logic is in src/lib/srs.ts.
Files are read in the browser (src/lib/material.ts): PDFs with pdf.js, PowerPoint and Word files
by unzipping them. Only the text is sent to /api/generate, in pieces of about four pages, so
the size of the file does not matter. Scanned pages without a text layer cannot be read yet.
With accounts on, requests per day are limited (GENERATE_LIMITS in src/server/quota.ts).
Hosts like Vercel cap a request body at 4.5 MB, which is about two minutes of 16 kHz WAV. Instead of uploading one file at the end, the browser cuts the recording into pieces of 45 to 60 seconds while you are still speaking, always at the quietest moment in that window so a cut falls into a pause. Each piece is transcribed right away and the texts are joined in order. This removes the length limit without extra storage, and most of the transcript is already done when you stop. A recording ends on its own after 30 minutes.
src/app/ routes (library, folder, card, study session, settings)
src/app/api/grade compares answer and solution
src/app/api/transcribe speech to text
src/components/ UI components (FormulaNode.tsx typesets formulas inside the editor)
src/lib/repo.ts data access in the browser (IndexedDB)
src/lib/sync.ts browser half of the sync
src/server/ database schema, server half of the sync, MCP server, access tokens
drizzle/ database migrations
src/lib/grading.ts segment alignment, sentence splitting, scoring
src/lib/audio.ts cutting a running recording into pieces, WAV encoding
src/lib/importCards.ts parsing pasted tables and CSV files
src/lib/office.ts reading .xlsx, .pptx and .docx without a library (src/lib/zip.ts)
src/lib/anki.ts reading Anki packages (src/lib/sqlite.ts, src/lib/html.ts)
src/lib/exportCards.ts CSV, Anki and tab-separated export
src/lib/richText.ts the Markdown subset of cards; src/components/RichText.tsx renders it
src/lib/images.ts pictures in cards: scaling, local storage, upload
src/lib/srs.ts spaced repetition and the plan towards an exam
src/lib/stats.ts streak and the numbers of the statistics page
src/lib/ink.ts handwriting: strokes, painting, the picture the AI reads
src/lib/occlusion.ts image occlusion cards, and the covered picture for the Anki export
src/lib/cloze.ts cloze cards from a text with gaps
src/lib/pendingGrades.ts grading answers that were given offline
src/lib/toast.ts short messages with an undo button (src/components/Toasts.tsx)
src/lib/settings.ts preferences kept on the device
src/lib/sample.ts the sample folder for the first visit
src/server/account.ts deleting everything an account stored
src/lib/editorHtml.ts card text to and from the visual editor (src/components/VisualEditor.tsx)
src/lib/ankiExport.ts writing Anki packages (src/lib/sqliteWrite.ts, src/lib/zipWrite.ts)
src/lib/offline.ts registering the service worker and storing the pages
src/server/shares.ts folders shared by link
src/server/generate.ts AI cards from study material
src/server/tandem.ts study tandem: the partner's instructions, its reply and its voice (src/lib/tandem.ts)
docs/design-references/ screens of other flashcard apps used as inspiration
pnpm dev # development server
pnpm build # production build
pnpm test # unit tests (Vitest)
pnpm lint # ESLint
pnpm typecheck # TypeScript
pnpm db:generate # create a migration after changing src/server/schema.ts
pnpm db:migrate # apply migrations to DATABASE_URL
Next.js 16 (App Router), React 19, Tailwind CSS 4, daisyUI 5, Dexie (IndexedDB), AI SDK 7 with the OpenRouter provider, Vitest.