Skip to content
aliflabPublic

About

DrillMCQ is a fully client-side MCQ Quiz Web App.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

DrillMCQ

Practice smarter. Test yourself.

React 19 TypeScript strict Vite Tailwind CSS v4 License: MIT Live demo on GitHub Pages


DrillMCQ is a fully client-side quiz web app. You paste MCQs as plain text or JSON (either generated from LLMs or copied over from any sources), and it parses them into an interactive quiz simulator with shuffling, a timer, scoring, pass/fail thresholds, and per-question review. An optional bring-your-own-key AI assistant can format messy pastes, repair JSON, verify answers, and explain results.
No backend, no database: everything runs in your browser.

Table of Contents

Features

  • Plain-text MCQ import: paste raw exam dumps or practice questions; a smart parser detects questions, options (A/B/C/D, a/b/c/d, numbered, bulleted), answers (inline or from an answer key at the bottom of the paste), explanations, and categories, with a live preview and per-line error/warning reporting. Common website and PDF clutter (ads, page numbers, "Show Answer" buttons, share widgets) is filtered out and reported as an ignored-lines count.
  • JSON import: paste JSON into a textarea, with friendly validation errors
  • Multiple correct answers: a question whose answer line lists more than one option (Answer: A, C) automatically becomes a "select all that apply" question with tick boxes. Nothing to configure, and single-answer questions are unaffected.
  • Check Answer: every question can be checked on the spot — the correct answer is revealed, your selection is marked right or wrong (all of it, on a multi-answer question), and the question stays put for review. Optional, one-way, and the quiz never advances by itself.
  • Four clear destinations: Home, Create Quiz, Quiz Library, and Results — a sticky header on desktop and a thumb-reachable bottom bar on phones
  • Home dashboard: what to do next at a glance, plus a Continue quiz banner (with a progress bar) whenever an unfinished run is waiting
  • Guided import flow: a Paste → Review → Start step indicator so it is obvious where you are and what comes next
  • Professional quiz engine: one question at a time, progress bar, next/previous navigation
  • Distraction-free quiz screen: while you are answering, the navigation is replaced by a slim bar showing the quiz name, timer, position, and progress — the only way out is a labelled back button behind a confirmation, so a stray tap can't abandon a run
  • Keyboard navigation: ←/→ to move between questions, 1–8 or A–H to select answers (they tick and untick on a multi-answer question), Enter to check the current question
  • Results & review: score percentage in a circular ring, per-question review with correct/incorrect indicators
  • Pass/fail threshold: set a pass mark (default 70%) before the quiz starts; the result screen shows PASS or FAIL against it, with a brief, dismissible confetti celebration on a pass
  • Explanations: optional expandable explanation per question
  • Quiz library: save imported quizzes to your Quiz Library, with question count, categories, best score, attempts, and status (not started / in progress / completed). Rename, start over, view results, or delete from a per-card menu
  • Resume: leave or refresh mid-quiz and pick up from home or the library exactly where you left off, with answers, position, timer, and settings intact
  • Results history: every completed attempt is kept locally, on its own Results screen and per quiz, with latest / best / average scores and a full review of any past attempt
  • Session persistence: progress, answers, and timer survive a page refresh (localStorage)
  • Timer mode: optional countdown that auto-submits when time runs out
  • Shuffle: optionally shuffle questions and/or options
  • Category filtering: pick which categories to include before starting
  • Review tools: filter to incorrect answers only, search questions
  • JSON export: download the loaded quiz as a JSON file from the results screen
  • Dark/light mode: toggle from the header or the ⚙️ Settings dialog, with saved preference (respects OS preference by default)
  • Sound effects: short tones for the moments that carry meaning — a rising chime for a correct answer and a soft falling one for a wrong answer, a fanfare or a quiet closing tone at the end of a run, and light clicks on selecting, paging, saving and confirming a delete. On by default and switchable off in Settings. The tones are generated in the browser with the Web Audio API, so nothing is downloaded and the app stays asset-free
  • Optional AI assistant: bring your own key to tidy a messy paste, repair broken JSON, double-check answers, or explain a result — off by default, and every request is a deliberate button press
  • Mobile-first and responsive: touch-sized targets, safe-area aware bottom navigation, and layouts that scale up to desktop

Getting around

The app has exactly four destinations, in the header on desktop and in a bottom bar on phones:

Destination What lives there
🏠 Home A short intro, the four things you can do, a Continue quiz banner for any unfinished run, and your most recent results
➕ Create Quiz The Plain text / JSON importer, the live preview, and the setup options
📚 Quiz Library Every saved quiz: start, continue, start over, rename, view its results, or delete
📊 Results Every completed attempt across all quizzes, including quizzes never saved to the library

⚙️ Settings (header) holds Appearance — dark/light mode, a background preset (Slate, Warm, Cool, High contrast), the font (Sans, Serif, Monospace, Readable) and the text size — plus Sound effects and the optional AI assistant. Every appearance choice applies instantly, is remembered in this browser, and can be undone with Reset. The background presets compose with dark mode rather than replacing it, and the fonts are system stacks, so nothing is ever downloaded. Sound sits in its own section on purpose, so Reset restores the look without unmuting the app.

Everything else — the setup screen, an active run, a result, a quiz's history, a stored attempt — is a layer over one of those four, so you always know where "back" goes. While a quiz is being answered the navigation is deliberately hidden: you get the quiz bar instead, and leaving asks first.

Screenshots

A full run through the app, in the order you meet it.

Home dashboard with Create Quiz, Quiz Library and Results tiles

1 · Home — pick up an unfinished run, jump into a saved quiz, or start a new one. Recent results sit underneath.

Create Quiz paste step with plain text questions and a live preview

2 · Paste — drop in plain text (or JSON). The parser runs as you type: “5 questions detected” and a live preview, all in the browser.

Review step: save to library, shuffle, timer, pass mark and category filters

3 · Review & configure — save the set to your library, then choose shuffling, a timer, the pass mark, and which categories to include.

Active question with the answer checked and the explanation shown

4 · Answer — pick an option and, when you want it, Check Answer reveals the verdict and the explanation without ending the quiz.

Select all that apply question with two correct options ticked

5 · Select all that apply — a question with more than one correct answer becomes multi-select automatically. Nothing to configure.

Result screen showing 100 percent, a PASS badge and confetti

6 · Result — score ring, pass/fail against your own pass mark, the correct/incorrect/skipped split, and a full answer review below.

Quiz Library card showing categories, last score, best score and attempts

7 · Quiz Library — every saved set with its categories, last and best score, and attempt count. Resume an unfinished run or start again.

Results history listing past attempts newest first

8 · Results history — every attempt, newest first. Open any one to replay its full review, scored against the pass mark it was taken under.

AI assistant settings dialog with provider, model and API key fields

9 · AI assistant (optional) — bring your own key. It goes straight from your browser to the provider, and is only stored if you ask for it.

Provider dropdown open showing OpenAI, Google Gemini and Anthropic Claude

10 · Choose a provider — OpenAI, Google Gemini or Anthropic Claude. Leave the whole thing off and the app behaves exactly as before.

Tech Stack

Getting Started

Prerequisites

  • Node.js 20+ and npm

Installation

git clone https://github.com/thealiflab/DrillMCQ.git
cd DrillMCQ
npm install

Run locally

npm run dev

Open http://localhost:5173 in your browser.

Build for production

npm run build

The optimized static site is emitted to dist/. Preview it locally with:

npm run preview

That serves the production build on http://localhost:4173.

Lint

npm run lint

Test

npm test          # run once
npm run test:watch

Tests cover the storage layer (saving, loading, updating and deleting quizzes and results, corrupted data, migration), the pure library/history logic, the plain-text parser, and the chunk splitters used by the AI rewrite workflows.

Importing Questions

Create Quiz has two tabs, Plain text and JSON. Both are paste-only textareas; there is no file-upload input. A Paste → Review → Start indicator tracks where you are: after a successful import you can name and save the quiz to your library, then choose shuffle, timer, and category options before starting.

Plain text format

The Plain text tab accepts loosely formatted MCQ content. All of these work (and can be mixed in one paste):

1. What does HTTP stand for?

A. HyperText Transfer Protocol
B. High Transfer Text Protocol
C. Hyperlink Text Transport Process
D. Host Transfer Text Protocol

Answer: A
Explanation: HTTP is the HyperText Transfer Protocol used by the web.

What is the capital of Australia?
a) Sydney
b) Melbourne
c) Canberra
d) Perth

Correct Answer: Canberra

Question: Which language runs in the browser?
- Python
- Java
- JavaScript
- C++

Answer: JavaScript

12. Which of the following are characteristics of living organisms?

a) Growth
b) Reproduction
c) Photosynthesis
d) Respiration

Answer: a, b, d

Networking Fundamentals Question 5
Which OSI layer is responsible for routing packets between networks?

❏ A. Data link layer

❏ B. Network layer

❏ C. Transport layer

✓ B. Network layer

Parsing rules:

  • Questions start with a number (1.), a header (Q1., Question:), a titled header (AI Practitioner Exam Question 5), or plain text after a completed question
  • Options may be lettered (A., a), (B)), bulleted (-, *, •), or numbered (a run of 2+ numbered lines under a question). A leading checkbox glyph (❏, ☐, □, ○) is stripped, so exam dumps paste in as-is
  • Answers use Answer:, Ans:, Correct Answer:, Correct:, Sol:, Soln:, or Solution: followed by a letter, number, or the option text itself — including the option restated with its own label (Sol: (c) Both (a) and (b).)
  • Ticked answers are also read: a line marked ✓ (or ✔, ☑) names the correct option, whether the tick sits on an option inside the list or repeats the winning option below it. Several ticked lines make the question a "select all that apply". An explicit Answer: line wins if both are present
  • Multiple answers are written as a list on the same answer line: Answer: A, C, Answer: A and C, Answer: a, b, d, Answer: A; C. Every part has to resolve to a distinct option, otherwise the line is treated as a single answer — so an option whose own text contains a comma (Answer: Atomicity, Consistency, Isolation, Durability) is still matched as one answer
  • Bottom answer keys are read too: put every question first and the answers in one block at the end, under a heading (ANSWER KEY, ANSWERS, SOLUTIONS, Answers and Explanations, …) or on their own. Entries look like 1. C, 2 - A, 3: B, and may list several options (4. A, C) or carry an explanation (5. C — TCP guarantees ordered delivery, or an Explanation: line under the entry). Entries are matched to questions by number, or by position when the questions aren't numbered and the key covers exactly all of them — anything less certain is reported instead of guessed. A question with its own Answer: line keeps it, and a disagreeing key entry is reported as a warning
  • Explanations use Explanation:, Reason:, Rationale:, Because:, or Why:
  • Categories use Category:, Topic:, or Subject:
  • Malformed questions are skipped with a per-line error message; the rest of the bank still loads

⭐ Prompt template for AI-generated questions

Tip

The fastest way to fill the app with questions — no typing, no formatting.

Copy the block below (hover it and click the copy icon), type your topic on the first line, and send it to ChatGPT, Claude, Gemini, or any other assistant. Paste the reply straight into the Plain text tab — no clean-up needed.

Topic: [User input: Describe your topic that you want to generate the MCQ]
Number of questions: 10

Write multiple-choice questions on the topic above.

Output plain text only. No markdown, no bold, no bullet symbols, no code
fences, no introduction and no closing remarks — just the questions in exactly
this format:

1. Question text goes here?
A. First option
B. Second option
C. Third option
D. Fourth option
Answer: B
Explanation: One short sentence on why that option is correct.

Rules:
- Number the questions 1, 2, 3, … and label the options A, B, C, D.
- Put one blank line between questions.
- "Answer:" repeats the letter of the correct option. If more than one option is
  correct, list every correct letter: "Answer: A, C".
- "Explanation:" is one line and optional.
- You may add a "Category: <name>" line under a question to group it by subtopic.

The Plain text tab previews exactly what was parsed before you generate the quiz, so you can see the question count, any skipped blocks, and warnings first. If a reply still comes back messy, the optional Format with AI button can tidy it up in place.


Quiz JSON schema

The app accepts an array of question objects:

[
  {
    "id": 1,
    "question": "What does HTTP stand for?",
    "options": [
      "HyperText Transfer Protocol",
      "High Transfer Text Protocol",
      "Hyperlink Text Transport Process",
      "Host Transfer Text Protocol"
    ],
    "correctAnswers": ["HyperText Transfer Protocol"],
    "explanation": "HTTP is the HyperText Transfer Protocol, the request/response protocol of the web.",
    "category": "Networking",
    "difficulty": "easy"
  },
  {
    "id": 2,
    "question": "Which of the following are programming languages?",
    "options": ["Python", "HTML", "Java", "CSS"],
    "correctAnswers": ["Python", "Java"]
  }
]
Field Type Required Notes
id number Yes Must be unique across the quiz
question string Yes The question text
options string[] Yes At least 2 options
correctAnswers string[] Yes At least one entry, no duplicates, each exactly matching an option. Two or more make it multi-select
explanation string No Shown in an expandable panel when present
category string No Enables category filtering on the setup screen
difficulty string No Displayed as a badge (e.g. easy, medium)

A single "correctAnswer": "…" string is still accepted in place of correctAnswers, so quizzes exported by an older build keep loading. Everything the app writes out uses correctAnswers.

A ready-to-use sample lives at src/data/sampleQuiz.json. You can also click "Try the sample quiz" on the JSON tab in the app.

Check Answer

Every question has a Check Answer button under its options. It stays disabled until you have picked something, and pressing it (or Enter):

  • reveals the correct answer — every correct option turns green, and a pick that wasn't one turns red;
  • says whether your answer was right. On a multi-answer question the verdict judges your complete selection, and every correct option is listed, including the ones you missed;
  • opens the explanation, if the question has one.

Checking is deliberate and one-way. The quiz does not move on by itself — the question stays on screen for as long as you want it, and Previous/Next work as usual. Once checked, the button is replaced by a "checked" note and the question is locked, so it can't be checked twice or quietly re-answered once the answer is on screen. Checking is entirely optional: skip it and the quiz behaves exactly as it did before, with everything revealed on the results screen.

A checked answer is scored no differently from an unchecked one, and reveals survive a refresh or a resume from the library.

Multiple correct answers

A question is single- or multi-select purely from the number of entries in correctAnswers — there is no separate setting, and nothing to switch on.

  • One correct answer: click an option to select it; you can change your mind any time before you finish — or until you check it.
  • Two or more: the card shows a Select all that apply badge and tick boxes. Tick as many options as you like, then check them in one go. A multi-answer question can only be answered once — it locks the moment you check it, so the card warns you before you commit.

Both kinds are marked the same way on the results screen, where each option is labelled as correct, missed, or a wrong pick.

Scoring has no partial credit: a multi-answer question is correct only when the selected set is exactly the correct set. For correct answers A, C:

You selected Result
A, C Correct
A Incorrect
C Incorrect
A, B, C Incorrect
B, C Incorrect
A, B Incorrect

Data & Storage

Everything is stored in your browser's localStorage under versioned keys:

Key Contents
drillmcq_active_session.v1 The in-progress quiz session
drillmcq_saved_quizzes.v1 The Quiz Library
drillmcq_quiz_results.v1 Completed attempts (append-only)
drillmcq.theme.v1 Dark/light preference
drillmcq_appearance.v1 Font, text size and background preset
drillmcq_ai_prefs.v1 AI provider/model preference
drillmcq_ai_key.v1 Your API key — only if you opt in
drillmcq_schema_version Schema version used for migrations

The current schema version is 4. Upgrading from an older version rewrites stored questions and answers into the multi-answer shape, gives an in-progress run its (empty) set of checked questions, and fills in the default 70% pass mark on runs and results recorded before that setting existed — all in place, so saved quizzes, an unfinished run, and your results history survive the upgrade.

Nothing is ever sent to a server. Clearing site data clears your quizzes and history. Corrupted records are repaired or dropped on load rather than crashing the app, and if localStorage is full or blocked the app still runs, it just stops persisting.

AI assistant (optional)

DrillMCQ can use an AI model as a second opinion. It is off by default and the app is fully usable without it — every feature above works untouched.

Because there is no backend, you bring your own API key: open ⚙️ Settings in the header → Configure AI assistant, pick a provider (OpenAI, Google Gemini, Anthropic Claude, DeepSeek, Mistral or xAI Grok), choose a model, paste your key, and hit Test connection. A green dot on the settings button means the assistant is configured; it turns into a spinner while a request is in flight.

The model list for each provider is a short suggestion list — model names change often, so Custom model ID… lets you type any model your provider offers.

DeepSeek, Mistral and xAI are labelled Browser access: unverified: their APIs are called straight from your browser, and none of them documents whether that is supported. If the browser blocks the request, Test connection says so explicitly — pick another provider in that case. Their endpoints are fixed and cannot be edited, so your key only ever goes to the provider's official API.

Once it is on, four optional actions appear:

Where Action What it does
Paste screen · Plain text ✨ Format with AI Tidies a messy paste into the format the parser expects: chapter headings become Category: lines, page numbers and site chrome go, options crammed onto one line are split out, labels of any kind become A.–D., and a correct option marked with a tick, an asterisk or a bottom answer key becomes an Answer: line. Anything that can't be a question (no options, fill-in-the-blank) is dropped and listed in the notes. The result goes back into the text box — the normal parser still does the actual importing, the panel says how many questions it found compared with before, and you can undo it.
Paste screen · JSON ✨ Fix JSON with AI Repairs broken or off-schema quiz JSON (trailing commas, missing fields, answers that don't match an option). The result goes back into the text box and is re-validated immediately, so a repair that didn't work says so straight away — and you can undo it.
After importing Verify answers The AI works out each answer itself and flags where it disagrees with your source. Useful for question banks scraped from the web, which often carry the wrong key.
Results screen 🤖 Ask AI Explains why the correct answer is correct, why yours was wrong, and whether the source answer itself looks mistaken.

Both rewrite buttons work through a long paste in several requests rather than one, showing progress (Fixing 2/5…) as they go, and every AI action can be cancelled mid-flight. If a part fails, the rest of your paste comes back untouched with a note saying how far it got — nothing you pasted is ever lost.

The AI never changes your quiz on its own. When it disagrees with a source answer you get both side by side and choose: keep the source, use the AI's answer, or edit the answers yourself. Nothing runs automatically either — each check is a button press, because each one costs you money against your own key.

About your API key

Your key goes from your browser straight to the provider you picked. DrillMCQ has no server, so it never receives or stores it.

  • By default the key is kept in memory only — reload the page and it is gone.
  • Ticking "Remember this key on this device" stores it in this browser in plain text. Convenient on your own machine; avoid it on a shared one.
  • Clear API key removes it immediately.

This is browser-side key handling, so be honest with yourself about the trade-off: any script running on the page could in principle read it. Prefer a key scoped or rate-limited to this use.

AI output can be wrong. Treat it as a second opinion, not the final word.

🔑 Getting a free Gemini API key

Google AI Studio issues a free-tier key in about a minute, which makes Google Gemini the quickest provider to start with.

  1. Visit Google AI Studio.
  2. Sign in with your Google account.
  3. Click "Create API key" and choose a new or existing Google Cloud project.
  4. Copy the key (it looks like AIza…) — it is only shown in full once.
  5. Back in DrillMCQ, open ⚙️ Settings in the header → Configure AI assistant, pick Google Gemini as the provider, choose a model, paste the key, and hit Test connection. A green dot on the settings button means you are ready.

The free tier is rate-limited rather than billed, so a burst of requests can come back as a 429 — wait a moment and retry, or add billing to the project in Google Cloud for higher limits. Paid keys from OpenAI or Anthropic work exactly the same way; only step 5 changes.

Keep the key to yourself: it is a credential, not a setting. DrillMCQ sends it straight to Google from your browser and never stores it anywhere else — see About your API key above for what "remember on this device" actually does.

Project Structure

src/
 ├── components/
 │    ├── AppNav.tsx           # Header + phone bottom bar (4 destinations)
 │    ├── HomeDashboard.tsx    # Home screen + "Continue quiz" banner
 │    ├── QuizTopBar.tsx       # The only chrome shown during a run
 │    ├── StepIndicator.tsx    # Paste → Review → Start
 │    ├── QuizCard.tsx         # Question + options card
 │    ├── QuizSetup.tsx        # Shuffle / timer / pass mark / category settings
 │    ├── ProgressBar.tsx      # Progress indicator
 │    ├── ResultScreen.tsx     # Score summary + answer review
 │    ├── ScoreRing.tsx        # Circular score ring with the pass-mark tick
 │    ├── CelebrationOverlay.tsx # Dismissible confetti shown on a pass
 │    ├── ExplanationPanel.tsx # Expandable explanation
 │    ├── ThemeToggle.tsx      # Dark/light mode switch
 │    ├── SettingsDialog.tsx   # Appearance + door into the AI assistant
 │    ├── QuizImporter.tsx     # Tabbed import (plain text / JSON)
 │    ├── TextUploader.tsx     # Plain-text MCQ import with live preview
 │    ├── JsonUploader.tsx     # Paste JSON import (+ optional AI repair)
 │    ├── SaveQuizPanel.tsx    # Name + save an import to the library
 │    ├── SavedQuizList.tsx    # Quiz Library + rename / start-over / delete
 │    ├── SavedQuizCard.tsx    # One saved quiz: status, score, actions
 │    ├── QuizHistory.tsx      # Per-quiz results history
 │    ├── RecentResults.tsx    # Home strip + full Results screen
 │    ├── Modal.tsx            # Focus-trapped dialog shell
 │    ├── ConfirmDialog.tsx    # Accessible confirmation modal
 │    ├── OverflowMenu.tsx     # Per-card "…" action menu
 │    ├── Spinner.tsx          # Inline busy indicator
 │    ├── AISettings.tsx       # AI provider / model / key (optional)
 │    ├── AIAnswerExplanation.tsx  # "Ask AI" panel on the results screen
 │    └── AIVerificationPanel.tsx  # AI answer check after importing
 ├── hooks/
 │    ├── useQuiz.ts           # Quiz state machine + persistence
 │    ├── useSavedQuizzes.ts   # Saved quiz library state
 │    ├── useQuizHistory.ts    # Completed attempts state
 │    ├── useTheme.ts          # Theme state
 │    ├── useTimer.ts          # Refresh-safe countdown
 │    ├── useBusyAction.ts     # Busy state for a one-shot async action
 │    └── useAI.ts             # AI config, key, and request lifecycle
 ├── services/
 │    ├── storage.ts           # localStorage wrapper + migration
 │    └── ai/                  # Provider-agnostic AI transport
 │         ├── aiService.ts    # Facade: send, normalize, redact keys
 │         ├── models.ts       # Curated model catalog per provider
 │         ├── providerRegistry.ts # DeepSeek / Mistral / xAI presets
 │         └── providers/      # openai / gemini / anthropic, plus one
 │                             #   shared openaiCompatible client
 ├── types/
 │    ├── quiz.ts              # Shared TypeScript types
 │    ├── navigation.ts        # The four top-level destinations
 │    └── ai.ts                # AI domain types (leaf)
 ├── utils/
 │    ├── quiz.ts              # Validation, shuffling, scoring
 │    ├── library.ts           # Session <-> progress <-> attempt transforms
 │    ├── parseMcqText.ts      # Plain-text MCQ parser
 │    └── ai/                  # Pure prompt builders, chunk splitters,
 │                             # and response validation
 ├── data/
 │    └── sampleQuiz.json      # Sample quiz dataset
 ├── App.tsx
 ├── main.tsx
 └── index.css

Contributing

Issues and pull requests are welcome at github.com/thealiflab/DrillMCQ. For anything larger than a bug fix, open an issue first so the approach can be agreed before you write the code.

Set up

# fork the repo on GitHub, then:
git clone https://github.com/<your-username>/DrillMCQ.git
cd DrillMCQ
npm install
npm run dev          # http://localhost:5173

Before you open a PR

All three must pass — the deploy workflow runs the build on every push to main:

npm run lint         # ESLint (typescript-eslint + react-hooks)
npm test             # Vitest
npm run build        # tsc -b (strict) + vite build

Components and hooks have no tests, so anything you change in the UI has to be driven in a browser. Say in the PR what you actually exercised.

House rules

These are the constraints the project is built on, and what a review will check. CLAUDE.md is the long-form architecture guide — read it first.

  • No backend, no database. Everything is client-side; persistence is localStorage and only ever through src/services/storage.ts. Never touch localStorage anywhere else.
  • No new runtime dependencies without discussing it first. The app ships react + react-dom and nothing more.
  • No binary assets. There is no public/ folder; sounds are synthesized with the Web Audio API in src/services/sound.ts, the only module allowed to create an AudioContext.
  • TypeScript strict, no any escape hatches, no // @ts-expect-error to get a build green.
  • Test the pure layer. New logic in src/utils/** or src/services/storage.ts needs tests; the Vitest environment is node with no DOM, so keep that logic free of browser APIs and React.
  • Storage shape changes need a step in migrate() and a SCHEMA_VERSION bump. Migrate in place under the same key whenever the normalizers can widen the old shape — don't orphan people's saved quizzes and history.
  • Question schema changes go in three places together: parseQuizJson / normalizeQuestion (src/utils/quiz.ts), src/types/quiz.ts, and the schema table in this README.
  • The AI layer is optional. With AI disabled the app must behave exactly as it did before that layer existed — that is the acceptance bar for any change to it. AI never produces questions directly; it only writes text back into an importer textarea for the normal parser to re-validate.
  • Don't change base: './' in vite.config.ts — the same build has to work on GitHub Pages (/DrillMCQ/) and Vercel (/).

Commits and PRs

Follow the existing history: feat:, fix:, docs:, refactor:, chore: prefixes, imperative mood, one logical change per commit. In the PR description say what changes for the user, and add before/after screenshots for anything visual.

Good first contributions

  • A new plain-text pattern from a real exam dump the parser mishandles — add the case to src/utils/parseMcqText.test.ts alongside the fix.
  • A new NOISE_RES entry for website or PDF clutter that leaks into a paste (anchored to the whole line, so real content can't match).
  • Keyboard and screen-reader fixes.
  • Documentation, including sample question banks.

By contributing you agree that your work is licensed under the MIT license.

License

MIT

About

DrillMCQ is a fully client-side MCQ Quiz Web App.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages