Wikipedia Flashcard Generator and Quiz

Generate flashcards from Wikipedia articles and test your knowledge with interactive quizzes

Size

37.7 KB

Version

1.1.1

Created

Oct 23, 2025

Updated

1 day ago

1// ==UserScript==
2// @name		Wikipedia Flashcard Generator and Quiz
3// @description		Generate flashcards from Wikipedia articles and test your knowledge with interactive quizzes
4// @version		1.1.1
5// @match		https://*.en.wikipedia.org/*
6// @icon		https://en.wikipedia.org/static/favicon/wikipedia.ico
7// @require		https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
8// ==/UserScript==
9(function() {
10    'use strict';
11
12    // Utility function to debounce
13    function debounce(func, wait) {
14        let timeout;
15        return function executedFunction(...args) {
16            const later = () => {
17                clearTimeout(timeout);
18                func(...args);
19            };
20            clearTimeout(timeout);
21            timeout = setTimeout(later, wait);
22        };
23    }
24
25    // Extract article content from Wikipedia page
26    function extractArticleContent() {
27        const contentDiv = document.querySelector('#mw-content-text .mw-parser-output');
28        if (!contentDiv) {
29            console.error('Could not find article content');
30            return '';
31        }
32
33        // Get all paragraphs, excluding references and other non-content sections
34        const paragraphs = Array.from(contentDiv.querySelectorAll('p'))
35            .filter(p => {
36                const parent = p.closest('.reflist, .navbox, .infobox, .metadata');
37                return !parent;
38            })
39            .map(p => p.textContent.trim())
40            .filter(text => text.length > 50) // Only substantial paragraphs
41            .slice(0, 10); // Limit to first 10 paragraphs
42
43        return paragraphs.join('\n\n');
44    }
45
46    // Get article title
47    function getArticleTitle() {
48        const titleElement = document.querySelector('#firstHeading');
49        return titleElement ? titleElement.textContent : 'Wikipedia Article';
50    }
51
52    // Export flashcards to Anki .apkg format
53    async function exportToAnki(flashcards, deckName) {
54        console.log('Exporting flashcards to Anki format');
55        
56        try {
57            const zip = new JSZip();
58            
59            // Create a timestamp for unique IDs
60            const timestamp = Date.now();
61            
62            // Create the collection.anki21 database structure
63            const deckId = timestamp;
64            
65            // Build the Anki database SQL
66            let sql = `PRAGMA foreign_keys=OFF;
67BEGIN TRANSACTION;
68
69CREATE TABLE col (
70    id integer primary key,
71    crt integer not null,
72    mod integer not null,
73    scm integer not null,
74    ver integer not null,
75    dty integer not null,
76    usn integer not null,
77    ls integer not null,
78    conf text not null,
79    models text not null,
80    decks text not null,
81    dconf text not null,
82    tags text not null
83);
84
85CREATE TABLE notes (
86    id integer primary key,
87    guid text not null,
88    mid integer not null,
89    mod integer not null,
90    usn integer not null,
91    tags text not null,
92    flds text not null,
93    sfld text not null,
94    csum integer not null,
95    flags integer not null,
96    data text not null
97);
98
99CREATE TABLE cards (
100    id integer primary key,
101    nid integer not null,
102    did integer not null,
103    ord integer not null,
104    mod integer not null,
105    usn integer not null,
106    type integer not null,
107    queue integer not null,
108    due integer not null,
109    ivl integer not null,
110    factor integer not null,
111    reps integer not null,
112    lapses integer not null,
113    left integer not null,
114    odue integer not null,
115    odid integer not null,
116    flags integer not null,
117    data text not null
118);
119
120CREATE TABLE revlog (
121    id integer primary key,
122    cid integer not null,
123    usn integer not null,
124    ease integer not null,
125    ivl integer not null,
126    lastIvl integer not null,
127    factor integer not null,
128    time integer not null,
129    type integer not null
130);
131
132CREATE TABLE graves (
133    usn integer not null,
134    oid integer not null,
135    type integer not null
136);
137
138CREATE INDEX ix_notes_usn on notes (usn);
139CREATE INDEX ix_cards_usn on cards (usn);
140CREATE INDEX ix_revlog_usn on revlog (usn);
141CREATE INDEX ix_cards_nid on cards (nid);
142CREATE INDEX ix_cards_sched on cards (did, queue, due);
143CREATE INDEX ix_revlog_cid on revlog (cid);
144CREATE INDEX ix_notes_csum on notes (csum);
145
146`;
147
148            // Model (note type) definition
149            const modelId = timestamp + 1;
150            const models = {
151                [modelId]: {
152                    id: modelId,
153                    name: 'Basic',
154                    type: 0,
155                    mod: Math.floor(timestamp / 1000),
156                    usn: -1,
157                    sortf: 0,
158                    did: deckId,
159                    tmpls: [{
160                        name: 'Card 1',
161                        ord: 0,
162                        qfmt: '{{Front}}',
163                        afmt: '{{FrontSide}}\n\n<hr id=answer>\n\n{{Back}}',
164                        bqfmt: '',
165                        bafmt: '',
166                        did: null,
167                        bfont: '',
168                        bsize: 0
169                    }],
170                    flds: [
171                        { name: 'Front', ord: 0, sticky: false, rtl: false, font: 'Arial', size: 20 },
172                        { name: 'Back', ord: 1, sticky: false, rtl: false, font: 'Arial', size: 20 }
173                    ],
174                    css: '.card {\n font-family: arial;\n font-size: 20px;\n text-align: center;\n color: black;\n background-color: white;\n}\n',
175                    latexPre: '\\documentclass[12pt]{article}\n\\special{papersize=3in,5in}\n\\usepackage[utf8]{inputenc}\n\\usepackage{amssymb,amsmath}\n\\pagestyle{empty}\n\\setlength{\\parindent}{0in}\n\\begin{document}\n',
176                    latexPost: '\\end{document}',
177                    latexsvg: false,
178                    req: [[0, 'all', [0]]]
179                }
180            };
181
182            // Deck definition
183            const decks = {
184                1: {
185                    id: 1,
186                    mod: Math.floor(timestamp / 1000),
187                    name: 'Default',
188                    usn: 0,
189                    lrnToday: [0, 0],
190                    revToday: [0, 0],
191                    newToday: [0, 0],
192                    timeToday: [0, 0],
193                    collapsed: false,
194                    browserCollapsed: false,
195                    desc: '',
196                    dyn: 0,
197                    conf: 1,
198                    extendNew: 0,
199                    extendRev: 0
200                },
201                [deckId]: {
202                    id: deckId,
203                    mod: Math.floor(timestamp / 1000),
204                    name: deckName,
205                    usn: -1,
206                    lrnToday: [0, 0],
207                    revToday: [0, 0],
208                    newToday: [0, 0],
209                    timeToday: [0, 0],
210                    collapsed: false,
211                    browserCollapsed: false,
212                    desc: '',
213                    dyn: 0,
214                    conf: 1,
215                    extendNew: 0,
216                    extendRev: 0
217                }
218            };
219
220            // Deck config
221            const dconf = {
222                1: {
223                    id: 1,
224                    mod: 0,
225                    name: 'Default',
226                    usn: 0,
227                    maxTaken: 60,
228                    autoplay: true,
229                    timer: 0,
230                    replayq: true,
231                    new: {
232                        bury: false,
233                        delays: [1, 10],
234                        initialFactor: 2500,
235                        ints: [1, 4, 0],
236                        order: 1,
237                        perDay: 20
238                    },
239                    rev: {
240                        bury: false,
241                        ease4: 1.3,
242                        ivlFct: 1,
243                        maxIvl: 36500,
244                        perDay: 200,
245                        hardFactor: 1.2
246                    },
247                    lapse: {
248                        delays: [10],
249                        leechAction: 0,
250                        leechFails: 8,
251                        minInt: 1,
252                        mult: 0
253                    }
254                }
255            };
256
257            // Collection config
258            const conf = {
259                nextPos: 1,
260                estTimes: true,
261                activeDecks: [1],
262                sortType: 'noteFld',
263                timeLim: 0,
264                sortBackwards: false,
265                addToCur: true,
266                curDeck: 1,
267                newBury: true,
268                newSpread: 0,
269                dueCounts: true,
270                curModel: modelId,
271                collapseTime: 1200
272            };
273
274            // Insert collection data
275            sql += `INSERT INTO col VALUES(
276                1,
277                ${Math.floor(timestamp / 1000)},
278                ${Math.floor(timestamp / 1000)},
279                ${Math.floor(timestamp / 1000)},
280                11,
281                0,
282                0,
283                0,
284                '${JSON.stringify(conf).replace(/'/g, '\'\'')}',
285                '${JSON.stringify(models).replace(/'/g, '\'\'')}',
286                '${JSON.stringify(decks).replace(/'/g, '\'\'')}',
287                '${JSON.stringify(dconf).replace(/'/g, '\'\'')}',
288                '{}'
289            );\n\n`;
290
291            // Add notes and cards
292            flashcards.forEach((card, index) => {
293                const noteId = timestamp + 100 + index;
294                const cardId = timestamp + 1000 + index;
295                const guid = `${noteId}${Math.random().toString(36).substring(2, 10)}`;
296                
297                // Escape single quotes in content
298                const front = card.question.replace(/'/g, '\'\'');
299                const back = card.answer.replace(/'/g, '\'\'');
300                const fields = `${front}\x1f${back}`;
301                
302                // Calculate checksum (simple version)
303                const checksum = front.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
304
305                sql += `INSERT INTO notes VALUES(
306                    ${noteId},
307                    '${guid}',
308                    ${modelId},
309                    ${Math.floor(timestamp / 1000)},
310                    -1,
311                    '',
312                    '${fields}',
313                    '${front}',
314                    ${checksum},
315                    0,
316                    ''
317                );\n`;
318
319                sql += `INSERT INTO cards VALUES(
320                    ${cardId},
321                    ${noteId},
322                    ${deckId},
323                    0,
324                    ${Math.floor(timestamp / 1000)},
325                    -1,
326                    0,
327                    0,
328                    ${index + 1},
329                    0,
330                    0,
331                    0,
332                    0,
333                    0,
334                    0,
335                    0,
336                    0,
337                    ''
338                );\n`;
339            });
340
341            sql += 'COMMIT;';
342
343            // Add the database to the zip
344            zip.file('collection.anki21', sql);
345            
346            // Create media file (empty for now)
347            zip.file('media', '{}');
348
349            // Generate the zip file
350            const blob = await zip.generateAsync({ type: 'blob' });
351            
352            // Download the file
353            const url = URL.createObjectURL(blob);
354            const a = document.createElement('a');
355            a.href = url;
356            a.download = `${deckName.replace(/[^a-z0-9]/gi, '_')}.apkg`;
357            document.body.appendChild(a);
358            a.click();
359            document.body.removeChild(a);
360            URL.revokeObjectURL(url);
361            
362            console.log('Successfully exported to Anki format');
363            return true;
364        } catch (error) {
365            console.error('Error exporting to Anki:', error);
366            throw error;
367        }
368    }
369
370    // Create flashcard UI
371    function createFlashcardUI() {
372        const container = document.createElement('div');
373        container.id = 'flashcard-container';
374        container.style.cssText = `
375            position: fixed;
376            top: 50%;
377            left: 50%;
378            transform: translate(-50%, -50%);
379            background: white;
380            border: 2px solid #0645ad;
381            border-radius: 12px;
382            padding: 30px;
383            box-shadow: 0 4px 20px rgba(0,0,0,0.3);
384            z-index: 10000;
385            max-width: 600px;
386            width: 90%;
387            max-height: 80vh;
388            overflow-y: auto;
389            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
390        `;
391
392        container.innerHTML = `
393            <div style="text-align: center;">
394                <h2 style="margin: 0 0 20px 0; color: #202122; font-size: 24px;">Flashcard Generator</h2>
395                <div id="flashcard-status" style="margin-bottom: 20px; color: #54595d; font-size: 16px;">
396                    Click "Generate Flashcards" to create study materials from this article.
397                </div>
398                <div id="flashcard-content" style="display: none;">
399                    <div id="flashcard-display" style="
400                        background: #f8f9fa;
401                        border: 2px solid #a2a9b1;
402                        border-radius: 8px;
403                        padding: 30px;
404                        min-height: 200px;
405                        margin-bottom: 20px;
406                        cursor: pointer;
407                        transition: all 0.3s ease;
408                    ">
409                        <div id="flashcard-question" style="font-size: 18px; font-weight: 600; color: #202122; margin-bottom: 15px;"></div>
410                        <div id="flashcard-answer" style="font-size: 16px; color: #54595d; display: none;"></div>
411                        <div id="flashcard-hint" style="font-size: 14px; color: #72777d; margin-top: 10px; font-style: italic;">Click to reveal answer</div>
412                    </div>
413                    <div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
414                        <button id="prev-flashcard" style="
415                            background: #eaecf0;
416                            border: 1px solid #a2a9b1;
417                            padding: 10px 20px;
418                            border-radius: 6px;
419                            cursor: pointer;
420                            font-size: 14px;
421                            font-weight: 600;
422                            color: #202122;
423                        ">← Previous</button>
424                        <span id="flashcard-counter" style="align-self: center; color: #54595d; font-weight: 600;"></span>
425                        <button id="next-flashcard" style="
426                            background: #eaecf0;
427                            border: 1px solid #a2a9b1;
428                            padding: 10px 20px;
429                            border-radius: 6px;
430                            cursor: pointer;
431                            font-size: 14px;
432                            font-weight: 600;
433                            color: #202122;
434                        ">Next →</button>
435                    </div>
436                </div>
437                <div style="display: flex; gap: 10px; justify-content: center; flex-wrap: wrap;">
438                    <button id="generate-flashcards-btn" style="
439                        background: #0645ad;
440                        color: white;
441                        border: none;
442                        padding: 12px 24px;
443                        border-radius: 6px;
444                        cursor: pointer;
445                        font-size: 16px;
446                        font-weight: 600;
447                    ">Generate Flashcards</button>
448                    <button id="export-anki-btn" style="
449                        background: #6c757d;
450                        color: white;
451                        border: none;
452                        padding: 12px 24px;
453                        border-radius: 6px;
454                        cursor: pointer;
455                        font-size: 16px;
456                        font-weight: 600;
457                        display: none;
458                    ">📥 Export to Anki</button>
459                    <button id="start-quiz-btn" style="
460                        background: #00af89;
461                        color: white;
462                        border: none;
463                        padding: 12px 24px;
464                        border-radius: 6px;
465                        cursor: pointer;
466                        font-size: 16px;
467                        font-weight: 600;
468                        display: none;
469                    ">Start Quiz</button>
470                    <button id="close-flashcard-btn" style="
471                        background: #eaecf0;
472                        color: #202122;
473                        border: 1px solid #a2a9b1;
474                        padding: 12px 24px;
475                        border-radius: 6px;
476                        cursor: pointer;
477                        font-size: 16px;
478                        font-weight: 600;
479                    ">Close</button>
480                </div>
481            </div>
482        `;
483
484        document.body.appendChild(container);
485        return container;
486    }
487
488    // Create quiz UI
489    function createQuizUI(flashcards) {
490        const container = document.createElement('div');
491        container.id = 'quiz-container';
492        container.style.cssText = `
493            position: fixed;
494            top: 50%;
495            left: 50%;
496            transform: translate(-50%, -50%);
497            background: white;
498            border: 2px solid #00af89;
499            border-radius: 12px;
500            padding: 30px;
501            box-shadow: 0 4px 20px rgba(0,0,0,0.3);
502            z-index: 10001;
503            max-width: 600px;
504            width: 90%;
505            max-height: 80vh;
506            overflow-y: auto;
507            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
508        `;
509
510        container.innerHTML = `
511            <div style="text-align: center;">
512                <h2 style="margin: 0 0 20px 0; color: #202122; font-size: 24px;">Quiz Time!</h2>
513                <div id="quiz-progress" style="margin-bottom: 20px; color: #54595d; font-weight: 600;"></div>
514                <div id="quiz-question-container" style="
515                    background: #f8f9fa;
516                    border: 2px solid #a2a9b1;
517                    border-radius: 8px;
518                    padding: 30px;
519                    margin-bottom: 20px;
520                ">
521                    <div id="quiz-question" style="font-size: 18px; font-weight: 600; color: #202122; margin-bottom: 20px;"></div>
522                    <textarea id="quiz-answer-input" style="
523                        width: 100%;
524                        min-height: 100px;
525                        padding: 12px;
526                        border: 2px solid #a2a9b1;
527                        border-radius: 6px;
528                        font-size: 16px;
529                        font-family: inherit;
530                        resize: vertical;
531                        box-sizing: border-box;
532                    " placeholder="Type your answer here..."></textarea>
533                </div>
534                <div id="quiz-feedback" style="
535                    margin-bottom: 20px;
536                    padding: 15px;
537                    border-radius: 6px;
538                    display: none;
539                "></div>
540                <div style="display: flex; gap: 10px; justify-content: center; flex-wrap: wrap;">
541                    <button id="submit-answer-btn" style="
542                        background: #00af89;
543                        color: white;
544                        border: none;
545                        padding: 12px 24px;
546                        border-radius: 6px;
547                        cursor: pointer;
548                        font-size: 16px;
549                        font-weight: 600;
550                    ">Submit Answer</button>
551                    <button id="next-question-btn" style="
552                        background: #0645ad;
553                        color: white;
554                        border: none;
555                        padding: 12px 24px;
556                        border-radius: 6px;
557                        cursor: pointer;
558                        font-size: 16px;
559                        font-weight: 600;
560                        display: none;
561                    ">Next Question</button>
562                    <button id="close-quiz-btn" style="
563                        background: #eaecf0;
564                        color: #202122;
565                        border: 1px solid #a2a9b1;
566                        padding: 12px 24px;
567                        border-radius: 6px;
568                        cursor: pointer;
569                        font-size: 16px;
570                        font-weight: 600;
571                    ">Close Quiz</button>
572                </div>
573            </div>
574        `;
575
576        document.body.appendChild(container);
577        return container;
578    }
579
580    // Generate flashcards using AI
581    async function generateFlashcards(articleContent, articleTitle) {
582        console.log('Generating flashcards for:', articleTitle);
583        
584        const prompt = `Create 8-10 educational flashcards from this Wikipedia article about "${articleTitle}". 
585        
586Article content:
587${articleContent}
588
589Generate flashcards that test key concepts, definitions, important facts, and relationships. Make questions clear and answers concise but informative.`;
590
591        try {
592            const flashcards = await RM.aiCall(prompt, {
593                type: 'json_schema',
594                json_schema: {
595                    name: 'flashcard_generation',
596                    schema: {
597                        type: 'object',
598                        properties: {
599                            flashcards: {
600                                type: 'array',
601                                items: {
602                                    type: 'object',
603                                    properties: {
604                                        question: { type: 'string' },
605                                        answer: { type: 'string' }
606                                    },
607                                    required: ['question', 'answer']
608                                }
609                            }
610                        },
611                        required: ['flashcards']
612                    }
613                }
614            });
615
616            console.log('Generated flashcards:', flashcards);
617            return flashcards.flashcards;
618        } catch (error) {
619            console.error('Error generating flashcards:', error);
620            throw error;
621        }
622    }
623
624    // Flashcard manager
625    class FlashcardManager {
626        constructor(flashcards) {
627            this.flashcards = flashcards;
628            this.currentIndex = 0;
629            this.revealed = false;
630        }
631
632        getCurrentCard() {
633            return this.flashcards[this.currentIndex];
634        }
635
636        next() {
637            if (this.currentIndex < this.flashcards.length - 1) {
638                this.currentIndex++;
639                this.revealed = false;
640                return true;
641            }
642            return false;
643        }
644
645        previous() {
646            if (this.currentIndex > 0) {
647                this.currentIndex--;
648                this.revealed = false;
649                return true;
650            }
651            return false;
652        }
653
654        reveal() {
655            this.revealed = true;
656        }
657
658        isRevealed() {
659            return this.revealed;
660        }
661
662        getProgress() {
663            return `${this.currentIndex + 1} / ${this.flashcards.length}`;
664        }
665    }
666
667    // Quiz manager
668    class QuizManager {
669        constructor(flashcards) {
670            this.flashcards = [...flashcards].sort(() => Math.random() - 0.5); // Shuffle
671            this.currentIndex = 0;
672            this.score = 0;
673            this.answered = false;
674        }
675
676        getCurrentQuestion() {
677            return this.flashcards[this.currentIndex];
678        }
679
680        async checkAnswer(userAnswer) {
681            const correctAnswer = this.flashcards[this.currentIndex].answer;
682            
683            const prompt = `Compare the user's answer to the correct answer and determine if it's correct.
684
685Question: ${this.flashcards[this.currentIndex].question}
686Correct Answer: ${correctAnswer}
687User's Answer: ${userAnswer}
688
689Be lenient - if the user's answer captures the main idea or key points, consider it correct even if wording differs.`;
690
691            try {
692                const evaluation = await RM.aiCall(prompt, {
693                    type: 'json_schema',
694                    json_schema: {
695                        name: 'answer_evaluation',
696                        schema: {
697                            type: 'object',
698                            properties: {
699                                isCorrect: { type: 'boolean' },
700                                feedback: { type: 'string' },
701                                correctAnswer: { type: 'string' }
702                            },
703                            required: ['isCorrect', 'feedback', 'correctAnswer']
704                        }
705                    }
706                });
707
708                if (evaluation.isCorrect) {
709                    this.score++;
710                }
711
712                this.answered = true;
713                return evaluation;
714            } catch (error) {
715                console.error('Error evaluating answer:', error);
716                throw error;
717            }
718        }
719
720        next() {
721            if (this.currentIndex < this.flashcards.length - 1) {
722                this.currentIndex++;
723                this.answered = false;
724                return true;
725            }
726            return false;
727        }
728
729        getProgress() {
730            return `Question ${this.currentIndex + 1} / ${this.flashcards.length}`;
731        }
732
733        getScore() {
734            return `Score: ${this.score} / ${this.currentIndex + 1}`;
735        }
736
737        getFinalScore() {
738            return `Final Score: ${this.score} / ${this.flashcards.length}`;
739        }
740
741        isComplete() {
742            return this.currentIndex === this.flashcards.length - 1 && this.answered;
743        }
744    }
745
746    // Main initialization
747    async function init() {
748        console.log('Wikipedia Flashcard Generator initialized');
749
750        // Add floating button to trigger flashcard generation
751        const floatingBtn = document.createElement('button');
752        floatingBtn.id = 'flashcard-trigger-btn';
753        floatingBtn.innerHTML = '📚 Flashcards';
754        floatingBtn.style.cssText = `
755            position: fixed;
756            bottom: 20px;
757            right: 20px;
758            background: #0645ad;
759            color: white;
760            border: none;
761            padding: 14px 20px;
762            border-radius: 25px;
763            cursor: pointer;
764            font-size: 16px;
765            font-weight: 600;
766            box-shadow: 0 4px 12px rgba(0,0,0,0.2);
767            z-index: 9999;
768            transition: all 0.3s ease;
769        `;
770
771        floatingBtn.addEventListener('mouseenter', () => {
772            floatingBtn.style.transform = 'scale(1.05)';
773            floatingBtn.style.boxShadow = '0 6px 16px rgba(0,0,0,0.3)';
774        });
775
776        floatingBtn.addEventListener('mouseleave', () => {
777            floatingBtn.style.transform = 'scale(1)';
778            floatingBtn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)';
779        });
780
781        floatingBtn.addEventListener('click', () => {
782            const existingContainer = document.getElementById('flashcard-container');
783            if (existingContainer) {
784                existingContainer.remove();
785            }
786            showFlashcardUI();
787        });
788
789        document.body.appendChild(floatingBtn);
790    }
791
792    // Show flashcard UI
793    function showFlashcardUI() {
794        const container = createFlashcardUI();
795        let flashcardManager = null;
796        let currentFlashcards = null;
797
798        const generateBtn = container.querySelector('#generate-flashcards-btn');
799        const closeBtn = container.querySelector('#close-flashcard-btn');
800        const statusDiv = container.querySelector('#flashcard-status');
801        const contentDiv = container.querySelector('#flashcard-content');
802        const displayDiv = container.querySelector('#flashcard-display');
803        const questionDiv = container.querySelector('#flashcard-question');
804        const answerDiv = container.querySelector('#flashcard-answer');
805        const hintDiv = container.querySelector('#flashcard-hint');
806        const counterSpan = container.querySelector('#flashcard-counter');
807        const prevBtn = container.querySelector('#prev-flashcard');
808        const nextBtn = container.querySelector('#next-flashcard');
809        const startQuizBtn = container.querySelector('#start-quiz-btn');
810        const exportAnkiBtn = container.querySelector('#export-anki-btn');
811
812        generateBtn.addEventListener('click', async () => {
813            generateBtn.disabled = true;
814            generateBtn.textContent = 'Generating...';
815            statusDiv.textContent = 'AI is analyzing the article and creating flashcards...';
816
817            try {
818                const articleContent = extractArticleContent();
819                const articleTitle = getArticleTitle();
820
821                if (!articleContent) {
822                    throw new Error('Could not extract article content');
823                }
824
825                const flashcards = await generateFlashcards(articleContent, articleTitle);
826                currentFlashcards = flashcards;
827                flashcardManager = new FlashcardManager(flashcards);
828
829                // Save flashcards for this article
830                const pageUrl = window.location.href;
831                await GM.setValue('flashcards_' + btoa(pageUrl).slice(0, 50), JSON.stringify(flashcards));
832
833                statusDiv.style.display = 'none';
834                contentDiv.style.display = 'block';
835                startQuizBtn.style.display = 'inline-block';
836                exportAnkiBtn.style.display = 'inline-block';
837                generateBtn.style.display = 'none';
838
839                updateFlashcardDisplay();
840            } catch (error) {
841                console.error('Error generating flashcards:', error);
842                statusDiv.textContent = 'Error generating flashcards. Please try again.';
843                statusDiv.style.color = '#d33';
844                generateBtn.disabled = false;
845                generateBtn.textContent = 'Generate Flashcards';
846            }
847        });
848
849        function updateFlashcardDisplay() {
850            if (!flashcardManager) return;
851
852            const card = flashcardManager.getCurrentCard();
853            questionDiv.textContent = card.question;
854            answerDiv.textContent = card.answer;
855            counterSpan.textContent = flashcardManager.getProgress();
856
857            if (flashcardManager.isRevealed()) {
858                answerDiv.style.display = 'block';
859                hintDiv.style.display = 'none';
860                displayDiv.style.background = '#e8f4f8';
861            } else {
862                answerDiv.style.display = 'none';
863                hintDiv.style.display = 'block';
864                displayDiv.style.background = '#f8f9fa';
865            }
866
867            prevBtn.disabled = flashcardManager.currentIndex === 0;
868            nextBtn.disabled = flashcardManager.currentIndex === flashcardManager.flashcards.length - 1;
869        }
870
871        displayDiv.addEventListener('click', () => {
872            if (flashcardManager && !flashcardManager.isRevealed()) {
873                flashcardManager.reveal();
874                updateFlashcardDisplay();
875            }
876        });
877
878        prevBtn.addEventListener('click', () => {
879            if (flashcardManager && flashcardManager.previous()) {
880                updateFlashcardDisplay();
881            }
882        });
883
884        nextBtn.addEventListener('click', () => {
885            if (flashcardManager && flashcardManager.next()) {
886                updateFlashcardDisplay();
887            }
888        });
889
890        startQuizBtn.addEventListener('click', () => {
891            if (currentFlashcards) {
892                container.remove();
893                showQuizUI(currentFlashcards);
894            }
895        });
896
897        exportAnkiBtn.addEventListener('click', async () => {
898            if (currentFlashcards) {
899                exportAnkiBtn.disabled = true;
900                exportAnkiBtn.textContent = 'Exporting...';
901                
902                try {
903                    const articleTitle = getArticleTitle();
904                    await exportToAnki(currentFlashcards, articleTitle);
905                    exportAnkiBtn.textContent = '✓ Exported!';
906                    setTimeout(() => {
907                        exportAnkiBtn.textContent = '📥 Export to Anki';
908                        exportAnkiBtn.disabled = false;
909                    }, 2000);
910                } catch (error) {
911                    console.error('Error exporting to Anki:', error);
912                    alert('Error exporting to Anki. Please try again.');
913                    exportAnkiBtn.textContent = '📥 Export to Anki';
914                    exportAnkiBtn.disabled = false;
915                }
916            }
917        });
918
919        closeBtn.addEventListener('click', () => {
920            container.remove();
921        });
922    }
923
924    // Show quiz UI
925    function showQuizUI(flashcards) {
926        const container = createQuizUI(flashcards);
927        const quizManager = new QuizManager(flashcards);
928
929        const progressDiv = container.querySelector('#quiz-progress');
930        const questionDiv = container.querySelector('#quiz-question');
931        const answerInput = container.querySelector('#quiz-answer-input');
932        const feedbackDiv = container.querySelector('#quiz-feedback');
933        const submitBtn = container.querySelector('#submit-answer-btn');
934        const nextBtn = container.querySelector('#next-question-btn');
935        const closeBtn = container.querySelector('#close-quiz-btn');
936
937        function updateQuizDisplay() {
938            const question = quizManager.getCurrentQuestion();
939            questionDiv.textContent = question.question;
940            progressDiv.textContent = `${quizManager.getProgress()} | ${quizManager.getScore()}`;
941            answerInput.value = '';
942            answerInput.disabled = false;
943            feedbackDiv.style.display = 'none';
944            submitBtn.style.display = 'inline-block';
945            nextBtn.style.display = 'none';
946        }
947
948        submitBtn.addEventListener('click', async () => {
949            const userAnswer = answerInput.value.trim();
950            
951            if (!userAnswer) {
952                alert('Please enter an answer');
953                return;
954            }
955
956            submitBtn.disabled = true;
957            submitBtn.textContent = 'Checking...';
958            answerInput.disabled = true;
959
960            try {
961                const evaluation = await quizManager.checkAnswer(userAnswer);
962
963                feedbackDiv.style.display = 'block';
964                if (evaluation.isCorrect) {
965                    feedbackDiv.style.background = '#d4edda';
966                    feedbackDiv.style.border = '2px solid #28a745';
967                    feedbackDiv.style.color = '#155724';
968                    feedbackDiv.innerHTML = `
969                        <div style="font-weight: 600; margin-bottom: 8px;">✓ Correct!</div>
970                        <div>${evaluation.feedback}</div>
971                    `;
972                } else {
973                    feedbackDiv.style.background = '#f8d7da';
974                    feedbackDiv.style.border = '2px solid #dc3545';
975                    feedbackDiv.style.color = '#721c24';
976                    feedbackDiv.innerHTML = `
977                        <div style="font-weight: 600; margin-bottom: 8px;">✗ Not quite right</div>
978                        <div style="margin-bottom: 8px;">${evaluation.feedback}</div>
979                        <div style="font-weight: 600;">Correct answer:</div>
980                        <div>${evaluation.correctAnswer}</div>
981                    `;
982                }
983
984                progressDiv.textContent = `${quizManager.getProgress()} | ${quizManager.getScore()}`;
985                submitBtn.style.display = 'none';
986
987                if (quizManager.isComplete()) {
988                    nextBtn.textContent = 'View Results';
989                }
990                nextBtn.style.display = 'inline-block';
991
992            } catch (error) {
993                console.error('Error checking answer:', error);
994                alert('Error checking answer. Please try again.');
995                submitBtn.disabled = false;
996                submitBtn.textContent = 'Submit Answer';
997                answerInput.disabled = false;
998            }
999        });
1000
1001        nextBtn.addEventListener('click', () => {
1002            if (quizManager.isComplete()) {
1003                // Show final results
1004                feedbackDiv.style.display = 'block';
1005                feedbackDiv.style.background = '#d1ecf1';
1006                feedbackDiv.style.border = '2px solid #0c5460';
1007                feedbackDiv.style.color = '#0c5460';
1008                feedbackDiv.innerHTML = `
1009                    <div style="font-size: 20px; font-weight: 600; margin-bottom: 10px;">Quiz Complete! 🎉</div>
1010                    <div style="font-size: 18px;">${quizManager.getFinalScore()}</div>
1011                    <div style="margin-top: 10px;">Great job studying!</div>
1012                `;
1013                container.querySelector('#quiz-question-container').style.display = 'none';
1014                nextBtn.style.display = 'none';
1015                submitBtn.style.display = 'none';
1016            } else {
1017                if (quizManager.next()) {
1018                    submitBtn.disabled = false;
1019                    submitBtn.textContent = 'Submit Answer';
1020                    updateQuizDisplay();
1021                }
1022            }
1023        });
1024
1025        closeBtn.addEventListener('click', () => {
1026            container.remove();
1027        });
1028
1029        updateQuizDisplay();
1030    }
1031
1032    // Wait for page to load
1033    if (document.readyState === 'loading') {
1034        document.addEventListener('DOMContentLoaded', init);
1035    } else {
1036        init();
1037    }
1038})();
Wikipedia Flashcard Generator and Quiz | Robomonkey