0% found this document useful (0 votes)
5 views110 pages

IntelliStudy PDF Companion Tool

The document consists of HTML, TypeScript, and JSON files for the IntelliStudy PDF Companion application, which is designed to enhance learning from PDF materials. It includes various themes for UI customization, a structured approach to managing study materials, and a framework for generating practice questions and tracking progress. The TypeScript definitions outline the application's data structures, including lessons, practice questions, and content blocks.

Uploaded by

kirm4536
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views110 pages

IntelliStudy PDF Companion Tool

The document consists of HTML, TypeScript, and JSON files for the IntelliStudy PDF Companion application, which is designed to enhance learning from PDF materials. It includes various themes for UI customization, a structured approach to managing study materials, and a framework for generating practice questions and tracking progress. The TypeScript definitions outline the application's data structures, including lessons, practice questions, and content blocks.

Uploaded by

kirm4536
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

--- START OF FILE index.

html ---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>IntelliStudy PDF Companion</title>
<script src="[Link]
<script src="[Link]
<style>
@keyframes gradient-animation {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes aurora-animation {
0% { background-position: 0% 0%; }
50% { background-position: 100% 100%; }
100% { background-position: 0% 0%; }
}
@keyframes haze-animation {
0% { background-position: 0% 0%; }
100% { background-position: 100% 0%; }
}
@keyframes cyber-grid-animation {
0% { background-position: 0 0; }
100% { background-position: 0 -100px; }
}
@keyframes cyber-scanline-animation {
0% { transform: translateY(-10px); }
100% { transform: translateY(100vh); }
}
:root {
--bg-body: #111827;
--bg-main: rgba(17, 24, 39, 0.8);
--bg-alt: rgba(31, 41, 55, 0.8);
--bg-glass: rgba(31, 41, 55, 0.5);
--text-primary: #f9fafb;
--text-secondary: #d1d5db;
--text-accent: #60a5fa;
--border-color: rgba(55, 65, 81, 0.7);
--accent-primary: #3b82f6;
--accent-secondary: #10b981;
--accent-danger: #ef4444;
--accent-success: #22c55e;
--accent-warn: #f59e0b;
--shadow-color: rgba(0, 0, 0, 0.5);
--animatic-border: linear-gradient(120deg, #1e3a8a, #3b82f6, #60a5fa,
#3b82f6, #1e3a8a);
--border-animation: gradient-animation 15s ease infinite;
--status-green: var(--accent-success);
--status-yellow: var(--accent-warn);
--status-red: var(--accent-danger);
}
[Link]-space {
--bg-body: #0c0a1a;
--bg-main: rgba(20, 18, 41, 0.8);
--bg-alt: rgba(40, 36, 82, 0.8);
--bg-glass: rgba(40, 36, 82, 0.5);
--text-primary: #e0e0ff;
--text-secondary: #b0b0d0;
--text-accent: #a78bfa;
--border-color: rgba(76, 70, 138, 0.7);
--accent-primary: #8b5cf6;
--accent-secondary: #f472b6;
--animatic-border: linear-gradient(120deg, #4c1d95, #c026d3, #f472b6,
#7c3aed, #4c1d95);
--border-animation: gradient-animation 15s ease infinite;
--status-green: #22c55e;
--status-yellow: #f59e0b;
--status-red: #ef4444;
}
[Link]-dark {
--bg-body: #121212;
--bg-main: rgba(28, 28, 28, 0.8);
--bg-alt: rgba(44, 44, 44, 0.8);
--bg-glass: rgba(44, 44, 44, 0.5);
--text-primary: #e0e0e0;
--text-secondary: #a0a0a0;
--text-accent: #10b981;
--border-color: rgba(74, 74, 74, 0.7);
--accent-primary: #10b981;
--accent-secondary: #3b82f6;
--animatic-border: linear-gradient(-45deg, #059669, #10b981, #0ea5e9,
#3b82f6);
--border-animation: aurora-animation 20s ease infinite;
--status-green: #16a34a;
--status-yellow: #d97706;
--status-red: #dc2626;
}
[Link]-desert {
--bg-body: #281e16;
--bg-main: rgba(56, 40, 30, 0.8);
--bg-alt: rgba(87, 63, 48, 0.8);
--bg-glass: rgba(87, 63, 48, 0.5);
--text-primary: #fde68a;
--text-secondary: #fbbf24;
--text-accent: #f97316;
--border-color: rgba(120, 84, 62, 0.7);
--accent-primary: #f97316;
--accent-secondary: #ea580c;
--animatic-border: repeating-linear-gradient(45deg, #f59e0b, #f59e0b 10px,
#fbbf24 10px, #fbbf24 20px);
--border-animation: haze-animation 30s linear infinite;
--status-green: #65a30d;
--status-yellow: #f59e0b;
--status-red: #c2410c;
}
[Link]-nature {
--bg-body: #142117;
--bg-main: rgba(28, 51, 35, 0.8);
--bg-alt: rgba(44, 82, 54, 0.8);
--bg-glass: rgba(44, 82, 54, 0.5);
--text-primary: #d4d4d4;
--text-secondary: #a3a3a3;
--text-accent: #84cc16;
--border-color: rgba(62, 115, 76, 0.7);
--accent-primary: #84cc16;
--accent-secondary: #22c55e;
--animatic-border: linear-gradient(120deg, #166534, #22c55e, #a3e635,
#4d7c0f, #166534);
--border-animation: gradient-animation 20s ease infinite;
--status-green: #4d7c0f;
--status-yellow: #ca8a04;
--status-red: #be123c;
}
[Link]-aurora {
--bg-body: #010115;
--bg-main: rgba(10, 5, 30, 0.7);
--bg-alt: rgba(20, 15, 50, 0.7);
--bg-glass: rgba(20, 15, 50, 0.4);
--text-primary: #e6e6fa;
--text-secondary: #b0c4de;
--text-accent: #89fffd;
--border-color: rgba(60, 50, 120, 0.7);
--accent-primary: #ff79c6;
--accent-secondary: #50fa7b;
--accent-danger: #ff5555;
--accent-warn: #f1fa8c;
--shadow-color: rgba(0, 0, 0, 0.5);
--animatic-border: none;
--status-green: var(--accent-secondary);
--status-yellow: var(--accent-warn);
--status-red: var(--accent-danger);
}
[Link]-cyber {
--bg-body: #0d0d0d;
--bg-main: rgba(20, 20, 20, 0.75);
--bg-alt: rgba(30, 30, 30, 0.75);
--bg-glass: rgba(20, 20, 20, 0.5);
--text-primary: #00ffde;
--text-secondary: #a1a1a1;
--text-accent: #ff00ff;
--border-color: rgba(0, 255, 222, 0.3);
--accent-primary: #00ffde;
--accent-secondary: #ff00ff;
--accent-danger: #ff5555;
--accent-success: #50fa7b;
--accent-warn: #f1fa8c;
--shadow-color: rgba(0, 255, 222, 0.2);
--animatic-border: none;
--status-green: var(--accent-success);
--status-yellow: var(--accent-warn);
--status-red: var(--accent-danger);
}
body {
background-color: var(--bg-body);
color: var(--text-primary);
transition: background-color 0.5s ease;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
background: var(--animatic-border);
background-size: 400% 400%;
animation: var(--border-animation);
transition: background 0.5s ease;
}
[Link]-aurora::before {
background: radial-gradient(circle at 20% 20%, #89fffd20, transparent 40%),
radial-gradient(circle at 80% 70%, #ff79c620, transparent 40%),
radial-gradient(circle at 50% 50%, #50fa7b10, transparent 30%);
animation: aurora-animation 30s ease-in-out infinite;
}
[Link]-cyber::before {
background-image: linear-gradient(var(--accent-primary) 1px, transparent
1px), linear-gradient(to right, var(--accent-primary) 1px, var(--bg-body) 1px);
background-size: 50px 50px;
opacity: 0.1;
animation: cyber-grid-animation 3s linear infinite;
}
[Link]-cyber::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 5px;
background: linear-gradient(to bottom, rgba(17, 24, 39, 0) 0%, var(--
accent-primary) 75%, var(--accent-secondary) 100%);
opacity: 0.2;
z-index: -1;
animation: cyber-scanline-animation 8s linear infinite;
}
[Link]-cyber * {
text-shadow: 0 0 2px var(--shadow-color);
}
.[Link]-green { background-color: var(--status-green); }
.[Link]-yellow { background-color: var(--status-yellow); }
.[Link]-red { background-color: var(--status-red); }

/* Mobile View Overrides */


.view-mobile .study-view-grid {
display: flex;
flex-direction: column;
}
.view-mobile .study-view-sidebar {
position: static !important;
top: auto;
padding-top: 2rem;
}
.view-mobile .dashboard-stats-grid {
grid-template-columns: 1fr !important;
}
.view-mobile .dashboard-page-grid {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
</style>
<script
src="[Link]
<script type="importmap">
{
"imports": {
"react": "[Link]
"react-dom/": "[Link]
"react/": "[Link]
"@google/genai": "[Link]
"pdfjs-dist": "[Link]
}
}
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="./[Link]"></script>
</body>
</html>
--- END OF FILE [Link] ---

--- START OF FILE [Link] ---


import React from 'react';
import ReactDOM from 'react-dom/client';
// FIX: Added file extension to resolve module import error.
import App from './[Link]';

const startApp = () => {


const rootElement = [Link]('root');
if (!rootElement) {
throw new Error("Could not find root element to mount to");
}

const root = [Link](rootElement);


[Link](
<[Link]>
<App />
</[Link]>
);
};

if ([Link] === 'loading') {


[Link]('DOMContentLoaded', startApp);
} else {
startApp();
}
--- END OF FILE [Link] ---

--- START OF FILE [Link] ---


{
"name": "IntelliStudy PDF Companion",
"description": "An intelligent study assistant to enhance learning from PDF
materials by generating practice questions, tracking progress with a spaced-
repetition system, and testing recall to identify knowledge gaps.",
"requestFramePermissions": []
}
--- END OF FILE [Link] ---

--- START OF FILE [Link] ---


export type ThemeName = 'space' | 'dark' | 'desert' | 'nature' | 'aurora' |
'cyber';
export type AppMode = 'intellistudy' | 'notebook';

export type ViewMode = 'desktop' | 'mobile';

export type StudyStatus = 'red' | 'yellow' | 'green';

export interface Document {


fileName: string;
pages: string[];
}

export interface Lesson {


id: string;
title: string;
startPage: number;
endPage: number;
status: StudyStatus;
lastStudied: string; // ISO string
images?: string[]; // Array of base64 encoded images
formattedText?: string;
enhancedContent?: EnhancedStudyContent;
highYieldInfo?: string;
clinicalPearls?: string;
october6ExamHistory?: PracticeQuestion[];
studyMcqHistory?: PracticeQuestion[];
studyHighlightsHistory?: PracticeQuestion[];
realExamHistory?: PracticeQuestion[];
impossibleHistory?: PracticeQuestion[];
egyptianExamsHistory?: PracticeQuestion[];
clinicalPearlsHistory?: PracticeQuestion[];
mockExamHistory?: PracticeQuestion[];
efficientStudyingHistory?: PracticeQuestion[];
postGraduatePrepHistory?: PracticeQuestion[];
postGraduateExamHistory?: PracticeQuestion[];
conquestProgress?: string[];
}

export interface PracticeQuestion {


question: string;
options: string[];
correctAnswer: string;
explanation: string;
isTricky?: boolean;
source?: string;
sourceLessonId?: string;
userAnswer?: string;
isCorrect?: boolean;
tags?: string[];
}

export enum ContentBlockType {


KEY_CONCEPTS = "KEY_CONCEPTS",
TABLE = "TABLE",
}

// FIX: Defined and exported KeyConceptsBlock to be used in ContentBlock.


export interface KeyConceptsBlock {
type: ContentBlockType.KEY_CONCEPTS;
concept: string;
details: string[];
image?: string;
videoUrl?: string;
}

// FIX: Defined and exported TableBlock to be used in ContentBlock.


export interface TableBlock {
type: [Link];
title: string;
headers: string[];
rows: string[][];
image?: string;
videoUrl?: string;
}

export type ContentBlock = KeyConceptsBlock | TableBlock;

export interface EnhancedStudyContent {


content: ContentBlock[];
}

export interface Citation {


page: number;
source: string;
}

export interface GroundedResponse {


answer: string;
citations: Citation[];
}

export interface ChatMessage {


role: 'user' | 'model';
content: string;
citations?: Citation[];
}

export type QuizType = 'october6Exam' | 'studyMcq' | 'realExam' | 'impossible' |


'studyHighlights' | 'egyptianExams' | 'clinicalPearls' | 'mockExam' |
'efficientStudying' | 'postGraduatePrep' | 'postGraduateExam';

export interface QuizAnswers {


[questionIndex: number]: string; // The selected answer string
}

export interface QuizSession {


lessonId: string;
questions: PracticeQuestion[];
currentIndex: number;
answers: QuizAnswers;
}

export interface UserExam {


id: string;
name: string;
questions: PracticeQuestion[];
}
--- END OF FILE [Link] ---
--- START OF FILE [Link] ---
import React, { useState, useEffect, useContext, useMemo } from 'react';
import { ThemeProvider } from './contexts/[Link]';
import { ViewModeProvider, ViewModeContext } from './contexts/[Link]';
import ThemeSwitcher from './components/[Link]';
import ViewModeSwitcher from './components/[Link]';
import IntelliStudyApp from './[Link]';
import NotebookLMApp from './components/notebooklm/[Link]';
import PdfProcessor from './components/[Link]';
import { AppMode, Document } from './[Link]';
import { ExitIcon } from './components/icons/[Link]';

function AppContent() {
const { viewMode } = useContext(ViewModeContext);
const [appMode, setAppMode] = useState<AppMode>(() =>
([Link]('appMode') as AppMode) || 'intellistudy');
const [documents, setDocuments] = useState<Document[]>(() => {
try {
const stored = [Link]('sessionDocuments');
return stored ? [Link](stored) : [];
} catch {
return [];
}
});
const [sessionName, setSessionName] = useState<string>(() =>
[Link]('sessionName') || '');
const [isProcessing, setIsProcessing] = useState(false);

useEffect(() => {
[Link]('appMode', appMode);
}, [appMode]);

useEffect(() => {
try {
[Link]('sessionDocuments', [Link](documents));
} catch (e) {
[Link]("Error saving documents to localStorage:", e);
}
}, [documents]);

useEffect(() => {
[Link]('sessionName', sessionName);
}, [sessionName]);

const combinedPages = useMemo(() => {


return [Link](doc =>
[Link](pageText => `[Source: ${[Link]}]\n${pageText}`)
);
}, [documents]);

const handleFilesProcessed = (processedDocs: Document[]) => {


const newSessionName = [Link] > 1 ? `Session: $
{[Link](d => [Link]).join(', ')}` : processedDocs[0]?.fileName ||
'New Session';
setDocuments(processedDocs);
setSessionName(newSessionName);
setIsProcessing(false);
};
const handleProcessingStart = () => {
setIsProcessing(true);
};

const handleExitSession = () => {


setDocuments([]);
setSessionName('');
setAppMode('intellistudy');

const keysToRemove: string[] = [];


for (let i = 0; i < [Link]; i++) {
const key = [Link](i);
if (key && (
[Link]('lessonsData_') ||
[Link]('quizSessions_') ||
[Link]('notebook_') ||
[Link]('userExams_') ||
[Link]('mockExamLessons_') ||
key === 'sessionDocuments' ||
key === 'sessionName'
)) {
[Link](key);
}
}
[Link](key => [Link](key));
};

const renderContent = () => {


if (isProcessing || [Link] === 0) {
return <PdfProcessor onProcessingStart={handleProcessingStart}
onFilesProcessed={handleFilesProcessed} />;
}

return (
<div className="flex flex-col h-full">
<header style={{ backgroundColor: 'var(--bg-glass)' }} className="flex-
shrink-0 backdrop-blur-sm border-b border-[var(--border-color)] p-2 flex justify-
between items-center">
<div className="flex items-center gap-4">
<h1 className="text-xl font-bold text-[var(--text-
primary)]">IntelliStudy</h1>
<div className="flex items-center bg-[var(--bg-main)] rounded-full p-1
border border-[var(--border-color)]">
<button
onClick={() => setAppMode('intellistudy')}
className={`px-4 py-1 text-sm rounded-full transition-colors $
{appMode === 'intellistudy' ? 'bg-[var(--accent-primary)] text-white' : 'text-
[var(--text-secondary)] hover:text-[var(--text-primary)]'}`}
>
IntelliStudy
</button>
<button
onClick={() => setAppMode('notebook')}
className={`px-4 py-1 text-sm rounded-full transition-colors $
{appMode === 'notebook' ? 'bg-[var(--accent-primary)] text-white' : 'text-[var(--
text-secondary)] hover:text-[var(--text-primary)]'}`}
>
Notebook Assistant
</button>
</div>
</div>
<div className="flex items-center gap-2">
<ViewModeSwitcher />
<ThemeSwitcher />
<button
onClick={handleExitSession}
className="p-2 rounded-full bg-[var(--bg-main)] border border-[var(--
border-color)] text-[var(--text-secondary)] hover:text-[var(--text-primary)]
hover:border-[var(--accent-danger)] transition-colors"
aria-label="Exit session and start new"
title="Exit session and start new"
>
<ExitIcon />
</button>
</div>
</header>

<main className="flex-grow overflow-y-auto">


{appMode === 'intellistudy' ? (
<IntelliStudyApp combinedPages={combinedPages}
sessionName={sessionName} />
) : (
<NotebookLMApp documents={documents} sessionName={sessionName} />
)}
</main>
</div>
);
};

return (
<div className={`h-screen w-screen bg-[var(--bg-main)] text-[var(--text-
primary)] font-sans overflow-hidden ${viewMode === 'mobile' ? 'view-mobile' :
''}`}>
{renderContent()}
</div>
);
}

function App() {
return (
<ThemeProvider>
<ViewModeProvider>
<AppContent />
</ViewModeProvider>
</ThemeProvider>
);
}

export default App;


--- END OF FILE [Link] ---

--- START OF FILE [Link] ---


import React, { useState, useEffect } from 'react';
import useStudyData from './hooks/[Link]';
import useUserExams from './hooks/[Link]';
import Dashboard from './components/[Link]';
import StudyView from './components/[Link]';
import { Lesson } from './[Link]';
import { categorizePagesIntoLessons } from './services/[Link]';
import Loader from './components/[Link]';

interface IntelliStudyAppProps {
combinedPages: string[];
sessionName: string;
}

type View = 'dashboard' | 'study';

const sanitizeLessons = (lessons: Omit<Lesson, 'id' | 'status' | 'lastStudied'>[],


totalPages: number): Omit<Lesson, 'id' | 'status' | 'lastStudied'>[] => {
if (totalPages === 0) return [];
if (!lessons || [Link] === 0) {
return [{ title: `Full Document (Pages 1-${totalPages})`, startPage: 1,
endPage: totalPages }];
}

const sortedLessons = [...lessons]


.filter(l => typeof [Link] === 'number' && typeof [Link] === 'number'
&& [Link] >= [Link])
.sort((a, b) => [Link] - [Link]);

if ([Link] === 0) {
return [{ title: `Full Document (Pages 1-${totalPages})`, startPage: 1,
endPage: totalPages }];
}

const sanitized: Omit<Lesson, 'id' | 'status' | 'lastStudied'>[] = [];


let currentPage = 1;

for (const lesson of sortedLessons) {


if ([Link] < currentPage) {
continue;
}

if ([Link] > currentPage) {


[Link]({
title: `Uncategorized (Pages ${currentPage}-${[Link] - 1})`,
startPage: currentPage,
endPage: [Link] - 1,
});
}

const adjustedStartPage = [Link]([Link], currentPage);


[Link]({
...lesson,
startPage: adjustedStartPage,
});

currentPage = [Link] + 1;
}

if (currentPage <= totalPages) {


[Link]({
title: `Conclusion (Pages ${currentPage}-${totalPages})`,
startPage: currentPage,
endPage: totalPages,
});
}

if ([Link] > 0) {
const lastLesson = sanitized[[Link] - 1];
if ([Link] > totalPages) {
[Link] = totalPages;
}
}

return [Link](l => [Link] <= [Link]);


};

const IntelliStudyApp: [Link]<IntelliStudyAppProps> = ({ combinedPages,


sessionName }) => {
const { lessonsData, isLoading, setAllLessonsData, updateLesson, dailyGoal,
setDailyGoal, getLessonsStudiedToday, addImageToLesson, removeImageFromLesson,
addQuestionsToHistory } = useStudyData(sessionName);
const { userExams, addUserExams, deleteUserExam } = useUserExams(sessionName);
const [view, setView] = useState<View>('dashboard');
const [activeLesson, setActiveLesson] = useState<Lesson | null>(null);
const [isCategorizing, setIsCategorizing] = useState(false);

useEffect(() => {
if (view === 'study' && activeLesson) {
const updatedActiveLesson = [Link](l => [Link] === [Link]);
if (updatedActiveLesson) {
if ([Link](activeLesson) !== [Link](updatedActiveLesson)) {
setActiveLesson(updatedActiveLesson);
}
} else {
setActiveLesson(null);
setView('dashboard');
}
}
}, [lessonsData, view, activeLesson]);

useEffect(() => {
const initializeDocument = async () => {
if (isLoading) return;

if ([Link] > 0 && [Link] === 0) {


setIsCategorizing(true);
try {
const generatedLessons = await categorizePagesIntoLessons(combinedPages);
const sanitized = sanitizeLessons(generatedLessons,
[Link]);

const newLessonsData: Lesson[] = [Link]((l, index) => ({


...l,
id: `lesson-${index}-${[Link](/\s/g, '-')}`,
status: 'red',
lastStudied: new Date(0).toISOString(),
images: [],
conquestProgress: [],
}));
setAllLessonsData(newLessonsData);
} catch (error) {
[Link]("Failed to categorize lessons:", error);
const fallbackLessons = sanitizeLessons([], [Link]);
const newLessonsData: Lesson[] = [Link]((l, index) => ({
...l,
id: `lesson-${index}`,
status: 'red',
lastStudied: new Date(0).toISOString(),
images: [],
conquestProgress: [],
}));
setAllLessonsData(newLessonsData);
} finally {
setIsCategorizing(false);
}

setView('dashboard');
setActiveLesson(null);
}
};

initializeDocument();
}, [combinedPages, lessonsData, isLoading, setAllLessonsData]);

const handleSelectLesson = (lessonId: string) => {


const lesson = [Link](l => [Link] === lessonId);
if (lesson) {
setActiveLesson(lesson);
setView('study');
}
};

const handleMarkAsStudied = (lessonId: string) => {


updateLesson(lessonId, {
lastStudied: new Date().toISOString(),
});
setView('dashboard');
};

if (isCategorizing) {
return (
<div className="flex flex-col items-center justify-center h-full text-
center p-4">
<Loader />
<p className="mt-4 text-lg text-[var(--text-secondary)]">Analyzing
documents and creating lessons...</p>
<p className="text-sm text-[var(--text-secondary)]">This might take a
moment for large documents.</p>
</div>
);
}

const getLessonText = (lesson: Lesson) => {


if (!lesson) return '';
return [Link]([Link] - 1, [Link]).join('\n\
n');
}
return (
<div className="h-full">
<nav className="p-4 border-b border-[var(--border-color)] bg-[var(--bg-
main)]">
<div className="flex justify-center items-center gap-4">
<button
onClick={() => setView('dashboard')}
className={`px-4 py-2 text-sm font-medium rounded-md transition-colors
${view === 'dashboard' ? 'text-[var(--accent-primary)] border-b-2 border-[var(--
accent-primary)]' : 'text-[var(--text-secondary)] hover:text-[var(--text-
primary)]'}`}
>
Dashboard
</button>
<button
onClick={() => activeLesson && setView('study')}
disabled={!activeLesson}
className={`px-4 py-2 text-sm font-medium rounded-md transition-colors
${view === 'study' ? 'text-[var(--accent-primary)] border-b-2 border-[var(--accent-
primary)]' : 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'}
disabled:opacity-50 disabled:cursor-not-allowed`}
>
Study Mode
</button>
</div>
</nav>
<div className="h-[calc(100%-65px)] overflow-y-auto">
{view === 'dashboard' ? (
<Dashboard
combinedPages={combinedPages}
sessionName={sessionName}
lessonsData={lessonsData}
onSelectLesson={handleSelectLesson}
dailyGoal={dailyGoal}
setDailyGoal={setDailyGoal}
lessonsStudiedToday={getLessonsStudiedToday()}
updateLesson={updateLesson}
addQuestionsToHistory={addQuestionsToHistory}
userExams={userExams}
addUserExams={addUserExams}
deleteUserExam={deleteUserExam}
/>
) : activeLesson ? (
<StudyView
lesson={activeLesson}
rawText={getLessonText(activeLesson)}
sessionName={sessionName}
onMarkAsStudied={handleMarkAsStudied}
addImageToLesson={addImageToLesson}
removeImageFromLesson={removeImageFromLesson}
onUpdateLesson={updateLesson}
addQuestionsToHistory={addQuestionsToHistory}
/>
) : (
<div className="flex items-center justify-center h-full">
<p className="text-[var(--text-secondary)]">Please select a lesson
from the dashboard to begin studying.</p>
</div>
)}
</div>
</div>
);
};

export default IntelliStudyApp;


--- END OF FILE [Link] ---

--- START OF FILE contexts/[Link] ---


import React, { createContext, useState, useEffect, useMemo } from 'react';
import { ThemeName } from '../[Link]';

interface ThemeContextType {
theme: ThemeName;
setTheme: (theme: ThemeName) => void;
}

export const ThemeContext = createContext<ThemeContextType>({


theme: 'space',
setTheme: () => {},
});

export const ThemeProvider: [Link]<{ children: [Link] }> =


({ children }) => {
const [theme, setTheme] = useState<ThemeName>('space'); // Default theme

// Load theme from localStorage on mount


useEffect(() => {
try {
const storedTheme = [Link]('app-theme');
if (storedTheme && ['space', 'dark', 'desert', 'nature', 'aurora',
'cyber'].includes(storedTheme)) {
setTheme(storedTheme as ThemeName);
}
} catch (error) {
[Link]("Could not load theme from localStorage", error);
}
}, []);

useEffect(() => {
[Link] = `theme-${theme}`;
try {
[Link]('app-theme', theme);
} catch (error) {
[Link]("Could not save theme to localStorage", error)
}
}, [theme]);

const value = useMemo(() => ({ theme, setTheme }), [theme]);

return (
<[Link] value={value}>
{children}
</[Link]>
);
};
--- END OF FILE contexts/[Link] ---

--- START OF FILE contexts/[Link] ---


import React, { createContext, useState, useEffect, useMemo } from 'react';
import { ViewMode } from '../[Link]';

interface ViewModeContextType {
viewMode: ViewMode;
setViewMode: (mode: ViewMode) => void;
}

export const ViewModeContext = createContext<ViewModeContextType>({


viewMode: 'desktop',
setViewMode: () => {},
});

export const ViewModeProvider: [Link]<{ children: [Link] }> =


({ children }) => {
const [viewMode, setViewMode] = useState<ViewMode>('desktop');

useEffect(() => {
try {
const storedMode = [Link]('app-view-mode');
if (storedMode === 'mobile' || storedMode === 'desktop') {
setViewMode(storedMode as ViewMode);
}
} catch (error) {
[Link]("Could not load view mode from localStorage", error);
}
}, []);

const handleSetViewMode = (mode: ViewMode) => {


setViewMode(mode);
try {
[Link]('app-view-mode', mode);
} catch (error) {
[Link]("Could not save view mode to localStorage", error);
}
};

const value = useMemo(() => ({ viewMode, setViewMode: handleSetViewMode }),


[viewMode]);

return (
<[Link] value={value}>
{children}
</[Link]>
);
};
--- END OF FILE contexts/[Link] ---

--- START OF FILE hooks/[Link] ---


import { useState, useEffect, useCallback, Dispatch, SetStateAction } from 'react';
import { ChatMessage } from '../[Link]';

const useNotebookData = (fileName: string) => {


const messagesKey = `notebook_messages_${fileName}`;
const selectedPagesKey = `notebook_selectedPages_${fileName}`;

const [messages, setMessagesInternal] = useState<ChatMessage[]>([]);


const [selectedPages, setSelectedPagesInternal] = useState<Set<string>>(new
Set());
useEffect(() => {
if (!fileName) return;

try {
const storedMessages = [Link](messagesKey);
if (storedMessages) {
setMessagesInternal([Link](storedMessages));
} else {
setMessagesInternal([]);
}

const storedSelectedPages = [Link](selectedPagesKey);


if (storedSelectedPages) {
setSelectedPagesInternal(new Set([Link](storedSelectedPages)));
} else {
setSelectedPagesInternal(new Set());
}
} catch (error) {
[Link]("Failed to load notebook data from localStorage", error);
[Link](messagesKey);
[Link](selectedPagesKey);
setMessagesInternal([]);
setSelectedPagesInternal(new Set());
}
}, [fileName, messagesKey, selectedPagesKey]);

const setMessages: Dispatch<SetStateAction<ChatMessage[]>> =


useCallback((value) => {
if (!fileName) return;
setMessagesInternal(prevMessages => {
const newMessages = value instanceof Function ? value(prevMessages) :
value;
try {
[Link](messagesKey, [Link](newMessages));
} catch (error) {
[Link]("Failed to save messages to localStorage", error);
}
return newMessages;
});
}, [fileName, messagesKey]);

const setSelectedPages: Dispatch<SetStateAction<Set<string>>> =


useCallback((value) => {
if (!fileName) return;
setSelectedPagesInternal(prevSelectedPages => {
const newSelectedPages = value instanceof Function ?
value(prevSelectedPages) : value;
try {
[Link](selectedPagesKey,
[Link]([Link](newSelectedPages)));
} catch (error) {
[Link]("Failed to save selected pages to localStorage",
error);
}
return newSelectedPages;
});
}, [fileName, selectedPagesKey]);
return { messages, selectedPages, setMessages, setSelectedPages };
};

export default useNotebookData;


--- END OF FILE hooks/[Link] ---

--- START OF FILE hooks/[Link] ---


import { useCallback } from 'react';
import { QuizSession, QuizType } from '../[Link]';

interface AllQuizSessions {
[lessonId: string]: {
october6Exam?: QuizSession;
studyMcq?: QuizSession;
realExam?: QuizSession;
impossible?: QuizSession;
studyHighlights?: QuizSession;
egyptianExams?: QuizSession;
clinicalPearls?: QuizSession;
mockExam?: QuizSession;
efficientStudying?: QuizSession;
postGraduatePrep?: QuizSession;
postGraduateExam?: QuizSession;
}
}

const useQuizSession = (fileName: string) => {


const quizSessionKey = `quizSessions_${fileName}`;

const getSessions = useCallback((): AllQuizSessions => {


if (!fileName) return {};
try {
const stored = [Link](quizSessionKey);
return stored ? [Link](stored) : {};
} catch (error) {
[Link]("Failed to get quiz sessions:", error);
return {};
}
}, [fileName, quizSessionKey]);

const saveSessions = useCallback((sessions: AllQuizSessions) => {


if (!fileName) return;
try {
[Link](quizSessionKey, [Link](sessions));
} catch (error) {
[Link]("Failed to save quiz sessions:", error);
}
}, [fileName, quizSessionKey]);

const getQuizSession = useCallback((lessonId: string, type: QuizType):


QuizSession | undefined => {
const allSessions = getSessions();
return allSessions[lessonId]?.[type];
}, [getSessions]);

const saveQuizSession = useCallback((session: QuizSession, type: QuizType) => {


const allSessions = getSessions();
if (!allSessions[[Link]]) {
allSessions[[Link]] = {};
}
allSessions[[Link]][type] = session;
saveSessions(allSessions);
}, [getSessions, saveSessions]);

const clearQuizSession = useCallback((lessonId: string, type: QuizType) => {


const allSessions = getSessions();
if (allSessions[lessonId]?.[type]) {
delete allSessions[lessonId][type];
if ([Link](allSessions[lessonId]).length === 0) {
delete allSessions[lessonId];
}
saveSessions(allSessions);
}
}, [getSessions, saveSessions]);

return { getQuizSession, saveQuizSession, clearQuizSession };


};

export default useQuizSession;


--- END OF FILE hooks/[Link] ---

--- START OF FILE hooks/[Link] ---


import { useState, useEffect, useCallback } from 'react';
import { Lesson, StudyStatus, PracticeQuestion, QuizType } from '../[Link]';

const getStatus = (lastStudied: string): StudyStatus => {


const lastStudiedDate = new Date(lastStudied);
const daysSinceStudied = (new Date().getTime() - [Link]()) /
(1000 * 3600 * 24);

if (daysSinceStudied > 7) {
return 'red';
}
if (daysSinceStudied > 1) {
return 'yellow';
}
return 'green';
};

const useStudyData = (fileName: string) => {


const lessonsDataKey = `lessonsData_${fileName}`;
const [lessonsData, setLessonsData] = useState<Lesson[]>([]);
const [dailyGoal, setDailyGoal] = useState<number>(5); // Default goal in lessons
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
if (!fileName) {
setIsLoading(false);
return;
}
setIsLoading(true);
try {
const storedData = [Link](lessonsDataKey);
const storedGoal = [Link]('dailyGoal');

if (storedData) {
const parsedData: Lesson[] = [Link](storedData);
const updatedData = [Link](lesson => ({
...lesson,
status: getStatus([Link])
}));
setLessonsData(updatedData);
} else {
setLessonsData([]);
}

if (storedGoal) {
setDailyGoal([Link](storedGoal));
}
} catch (error) {
[Link]("Failed to load data from localStorage", error);
[Link](lessonsDataKey);
[Link]('dailyGoal');
} finally {
setIsLoading(false);
}
}, [fileName, lessonsDataKey]);

const updateAndStoreData = useCallback((updater: (prevData: Lesson[]) =>


Lesson[]) => {
if (!fileName) return;
setLessonsData(prevData => {
const newData = updater(prevData);
try {
[Link](lessonsDataKey, [Link](newData));
} catch (error) {
[Link]("Failed to save data to localStorage", error);
}
return newData;
});
}, [fileName, lessonsDataKey]);

const setAllLessonsData = useCallback((data: Lesson[]) => {


if (!fileName) return;
try {
setLessonsData(data);
[Link](lessonsDataKey, [Link](data));
} catch (error) {
[Link]("Failed to save data to localStorage", error);
}
}, [fileName, lessonsDataKey]);

const setDailyGoalInternal = (goal: number) => {


try {
setDailyGoal(goal);
[Link]('dailyGoal', [Link](goal));
} catch (error) {
[Link]("Failed to save goal to localStorage", error);
}
}

const updateLesson = useCallback((lessonId: string, updates: Partial<Lesson>) =>


{
updateAndStoreData(prevData => [Link](l => {
if ([Link] === lessonId) {
const newLesson = { ...l, ...updates };
if ([Link]) {
[Link] = getStatus([Link]);
}
return newLesson;
}
return l;
}));
}, [updateAndStoreData]);

const addImageToLesson = useCallback((lessonId: string, imageData: string) => {


updateAndStoreData(prevData => [Link](l => {
if ([Link] === lessonId) {
const images = [Link] ? [...[Link]] : [];
if ([Link] < 2) {
[Link](imageData);
}
return { ...l, images };
}
return l;
}));
}, [updateAndStoreData]);

const removeImageFromLesson = useCallback((lessonId: string, imageIndex: number)


=> {
updateAndStoreData(prevData => [Link](l => {
if ([Link] === lessonId) {
const images = [Link] ? [Link]((_, index) => index !==
imageIndex) : [];
return { ...l, images };
}
return l;
}));
}, [updateAndStoreData]);

const addQuestionsToHistory = useCallback((lessonId: string, questions:


PracticeQuestion[], type: QuizType) => {
const historyKey = `${type}History` as const;
updateAndStoreData(prevData => [Link](l => {
if ([Link] === lessonId) {
const existingHistory = l[historyKey as keyof Lesson] as PracticeQuestion[]
|| [];
const newHistory = [...existingHistory, ...questions];
return { ...l, [historyKey]: newHistory };
}
return l;
}));
}, [updateAndStoreData]);

const getLessonsStudiedToday = useCallback(() => {


const today = new Date().toISOString().split('T')[0];
return [Link](l => [Link](today)).length;
}, [lessonsData]);

return { lessonsData, isLoading, setAllLessonsData, updateLesson, dailyGoal,


setDailyGoal: setDailyGoalInternal, getLessonsStudiedToday, addImageToLesson,
removeImageFromLesson, addQuestionsToHistory };
};

export default useStudyData;


--- END OF FILE hooks/[Link] ---
--- START OF FILE hooks/[Link] ---
import { useState, useEffect, useCallback } from 'react';
import { UserExam } from '../[Link]';

const useUserExams = (fileName: string) => {


const userExamsKey = `userExams_${fileName}`;
const [userExams, setUserExams] = useState<UserExam[]>([]);

useEffect(() => {
if (!fileName) return;
try {
const stored = [Link](userExamsKey);
if (stored) {
setUserExams([Link](stored));
} else {
setUserExams([]);
}
} catch (error) {
[Link]("Failed to load user exams from localStorage", error);
}
}, [fileName, userExamsKey]);

const addUserExams = useCallback((newExams: UserExam[]) => {


if (!fileName) return;
setUserExams(prev => {
const updatedExams = [...prev, ...newExams];
try {
[Link](userExamsKey, [Link](updatedExams));
} catch (error) {
[Link]("Failed to save user exams to localStorage", error);
}
return updatedExams;
});
}, [fileName, userExamsKey]);

const deleteUserExam = useCallback((examId: string) => {


if (!fileName) return;
setUserExams(prev => {
const updatedExams = [Link](exam => [Link] !== examId);
try {
[Link](userExamsKey, [Link](updatedExams));
} catch (error) {
[Link]("Failed to save user exams to localStorage", error);
}
return updatedExams;
});
}, [fileName, userExamsKey]);

return { userExams, addUserExams, deleteUserExam };


};

export default useUserExams;


--- END OF FILE hooks/[Link] ---

--- START OF FILE services/[Link] ---


import { GoogleGenAI, Type } from "@google/genai";
import { PracticeQuestion, EnhancedStudyContent, ContentBlockType,
GroundedResponse, Lesson, Citation } from '../[Link]';
const ai = new GoogleGenAI({ apiKey: [Link].API_KEY });

const cleanAndExtractJson = (text: string): string => {


const fencedMatch = [Link](/```json\s*([\s\S]*?)\s*```/);
if (fencedMatch && fencedMatch[1]) {
return fencedMatch[1].trim();
}

const firstBracket = [Link]('{');


const firstSquare = [Link]('[');

let start = -1;


if (firstBracket === -1 && firstSquare === -1) return text;
if (firstBracket === -1) start = firstSquare;
else if (firstSquare === -1) start = firstBracket;
else start = [Link](firstBracket, firstSquare);

const lastBracket = [Link]('}');


const lastSquare = [Link](']');

let end = -1;


if (lastBracket === -1 && lastSquare === -1) return text;
if (lastBracket === -1) end = lastSquare;
else if (lastSquare === -1) end = lastBracket;
else end = [Link](lastBracket, lastSquare);

if (start !== -1 && end !== -1 && end > start) {


return [Link](start, end + 1);
}

return text;
};

const parseJsonGracefully = <T>(jsonString: string): T | null => {


try {
let textToParse = cleanAndExtractJson(jsonString);

const sanitizedForTitle = [Link](/"title"\s*:\s*"((?:\\"|


[^"])*)"/g, (match, titleContent) => {
const maxLength = 250;
if ([Link] > maxLength) {
const truncated = [Link](0, maxLength -
3).replace(/"/g, '\\"') + '...';
return `"title": "${truncated}"`;
}
return match;
});

let cleanedString = [Link](/,\s*([}\]])/g, '$1');

return [Link](cleanedString) as T;
} catch (e) {
[Link]("Failed to parse JSON:", e);
[Link]("Raw string:", jsonString);
try {
let fixedJson = cleanAndExtractJson(jsonString);
fixedJson = [Link](/,\s*([}\]])/g, '$1');
const parsed = [Link](fixedJson) as T;
[Link]("Successfully parsed after fixing JSON string.");
return parsed;
} catch (e2) {
[Link]("Failed to parse JSON even after fixing:", e2);
[Link]("Problematic JSON string:", jsonString);
return null;
}
}
};

export const extractTextFromImage = async (base64Image: string, mimeType: string):


Promise<string> => {
try {
const imagePart = {
inlineData: {
data: base64Image,
mimeType: mimeType,
},
};
const textPart = {
text: "You are an expert Optical Character Recognition (OCR) service.
Extract all text from the provided image. Preserve the original formatting,
including line breaks and paragraphs, as accurately as possible. Return only the
extracted text, with no additional commentary, introductions, or summaries.",
};

const response = await [Link]({


model: "gemini-2.5-flash",
contents: { parts: [imagePart, textPart] },
});

return [Link];
} catch (error) {
[Link]("Error extracting text from image:", error);
throw new Error("Could not extract text from the image. Please ensure the
image is clear and try again.");
}
};

export const formatStudyText = async (text: string): Promise<string> => {


try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Format the following text extracted from a study document
into clean, readable Markdown.
- Use headings, bullet points, bold text, and other formatting to
improve structure and readability.
- Correct any obvious OCR errors or formatting issues.
- Crucially, ensure that all Unicode emojis (e.g., ✅, 💡, 🌍) are
preserved in their original form and position.
- Do not add new information or summarize. The goal is to make the
existing content easier to read.

TEXT TO FORMAT:
---
${text}
---`,
});
return [Link];
} catch (error) {
[Link]("Error formatting study text:", error);
return [Link](/\n/g, '<br/>');
}
};

export const generateEnhancedStudyContent = async (text: string):


Promise<EnhancedStudyContent> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Analyze the following text. Extract the most important key
concepts and data tables.
Return ONLY a single valid JSON object with a root key "content".
The "content" key must be an array of objects. Do not exceed 5 objects.
Each object must have a "type" field: either "KEY_CONCEPTS" or "TABLE".

RULES:
- For "KEY_CONCEPTS" objects, include a "concept" (string) and "details" (array of
strings).
- For "TABLE" objects, include a "title" (string), "headers" (array of strings),
and "rows" (array of arrays of strings).
- The "title" for a table MUST BE a very short, descriptive string, strictly under
15 words.
- CRITICAL: The "title" field MUST NOT contain any legal disclaimers, medical
advice, warnings, or any text other than a brief description of the table's
content.
- Do not include any text, explanations, or markdown formatting like \`\`\`json
outside of the JSON object.

TEXT TO ANALYZE:
---
${text}
---`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
content: {
type: [Link],
items: {
type: [Link],
properties: {
type: {
type: [Link],
enum: [ContentBlockType.KEY_CONCEPTS,
[Link]],
},
concept: { type: [Link] },
details: {
type: [Link],
items: { type: [Link] },
},
title: { type: [Link] },
headers: {
type: [Link],
items: { type: [Link] },
},
rows: {
type: [Link],
items: { type: [Link], items: { type:
[Link] } },
},
},
required: ["type"]
}
}
},
required: ["content"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<EnhancedStudyContent>(jsonStr);

if (!parsed || ![Link]([Link])) {
[Link]("Parsed content is not valid:", parsed);
return { content: [] };
}
return parsed;

} catch (error) {
[Link]("Error generating enhanced study content:", error);
return { content: [] };
}
};

export const categorizePagesIntoLessons = async (pagesText: string[]):


Promise<Lesson[]> => {
try {
const MAX_SAMPLES = 75;
const pageSummaries: string[] = [];
const totalPages = [Link];

if (totalPages > 0) {
if (totalPages <= MAX_SAMPLES) {
for (let i = 0; i < totalPages; i++) {
[Link](`--- Page ${i + 1} Start ---\n$
{pagesText[i].substring(0, 300)}...`);
}
} else {
const step = [Link](totalPages / MAX_SAMPLES);
for (let i = 0; i < totalPages; i += step) {
[Link](`--- Page ${i + 1} Start ---\n$
{pagesText[i].substring(0, 300)}...`);
}
if ((totalPages - 1) % step !== 0) {
[Link](`--- Page ${totalPages} Start ---\n$
{pagesText[totalPages - 1].substring(0, 300)}...`);
}
}
}

const conciseText = [Link]('\n\n');


const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Analyze the following detailed summary of a document. Your
task is to act as an expert curriculum designer. Your goal is to organize the
document into a logical sequence of well-defined lessons.

DOCUMENT SUMMARY:
---
${conciseText}
---

RULES:
1. **Understand the Source:** The document summary is compiled from
one or more source files. The text for each page may start with a "[Source:
filename]" tag. Use this as a strong hint for topic changes, but you should still
group pages from different sources if the topic is clearly continuous (e.g., a
chapter in a book and its corresponding lecture slides).
2. **Find the Right Balance:** Your primary goal is to find a balance
between creating overly broad lessons that cover too many disparate topics and
creating too many small, fragmented lessons.
3. **Coherent Grouping:** Group pages that cover a single, coherent
topic into one lesson.
4. **Logical Lesson Size:** Aim for lessons that are typically between
3 and 15 pages long.
5. **Generate Specific Titles:** For each lesson, create a title that
is a concise and accurate summary of its content.
6. **Ensure Full Coverage:** Page ranges must be contiguous and cover
all pages from 1 to ${[Link]}. If lesson 1 ends at page 5, lesson 2 must
start at page 6. The final lesson must end on page ${[Link]}.
7. **Output Format:** Return a valid JSON object with a single root
key "lessons", which is an array of lesson objects. Each lesson object must have
"title" (string), "startPage" (number), and "endPage" (number).
8. **Output ONLY the JSON object.** No extra text or markdown.

EXAMPLE OUTPUT (for a medical text):


{
"lessons": [
{ "title": "Anatomy and Physiology of the Kidney", "startPage": 1,
"endPage": 15 },
{ "title": "Pathophysiology of Urinary Tract Infections",
"startPage": 16, "endPage": 22 },
{ "title": "Renal Trauma: Diagnosis and Management", "startPage":
23, "endPage": 28 }
]
}`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
lessons: {
type: [Link],
items: {
type: [Link],
properties: {
title: { type: [Link] },
startPage: { type: [Link] },
endPage: { type: [Link] },
},
required: ["title", "startPage", "endPage"]
}
}
},
required: ["lessons"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ lessons: Lesson[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
[Link]("Failed to parse lesson categories:", parsed);
return [];
}

if ([Link] > 0) {
const lastLesson = [Link][[Link] - 1];
if ([Link] !== [Link]) {
[Link](`Lesson categorization might be incomplete. Expected
last page ${[Link]}, but got ${[Link]}. Adjusting...`);
[Link] = [Link];
}
}

return [Link];

} catch (error) {
[Link]("Error generating lesson categories:", error);
return [];
}
};

export const generateOctober6ExamQuestions = async (text: string, highYieldText:


string | undefined, history: PracticeQuestion[] = []): Promise<PracticeQuestion[]>
=> {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Act as a specialist who has access to secretive, hard-to-
find online archives of past exam questions from October 6 University in Egypt.
Based on the provided text, generate 3-5 multiple-choice questions that perfectly
mimic the style, difficulty, and niche topics found in these exclusive past papers
from 2020 onwards.

RULES:
1. Generate 3-5 questions.
2. For each question, provide: "question", "options" (an array of 4
strings), "correctAnswer" (a string matching one of the options), "source" (string
indicating which specific past exam it's modeled after, e.g., 'October 6 University
- Final Year Cardiology Exam 2023'), and "tags" (an array of strings like "O6U-
Style", "High-Yield").
3. CRITICAL: The "explanation" must be in **Markdown format**. It
should be simple, direct, and clear, making it easy for a student to understand.
Use bolding for key terms. It must briefly explain why the correct answer is right
and why the other options are wrong.
4. Your questions MUST synthesize information from BOTH the 'MAIN
TEXT' and the 'HIGH-YIELD INFORMATION' when relevant. Prioritize testing concepts
from the high-yield info.
5. DO NOT repeat any questions from the provided history.

PRACTICE HISTORY (questions to avoid):


---
${[Link] > 0 ? [Link]([Link](q => [Link])) :
"None"}
---

KNOWLEDGE SOURCES:
---
MAIN TEXT:
${text}
---
HIGH-YIELD INFORMATION:
${highYieldText || 'No supplementary high-yield information was
provided for this topic.'}
---

Return the result as a JSON object with a single root key "questions"
which is an array of question objects.`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
source: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "source", "tags"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid exam questions. The AI's
response was not in the expected format.");
}
return [Link];

} catch (error) {
[Link]("Error generating exam questions:", error);
throw new Error("Could not generate exam questions. Please try again
later.");
}
};

export const generateEgyptianExamQuestions = async (text: string, highYieldText:


string | undefined, history: PracticeQuestion[] = []): Promise<PracticeQuestion[]>
=> {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Act as a specialist who has access to an archive of past
exam questions from all Egyptian medical universities (e.g., Cairo University, Ain
Shams University, Alexandria University). Based on the provided text, generate 3-5
multiple-choice questions that mimic the style of these exams from 2020 onwards.

IMPORTANT: The questions must be NON-CASE BASED and focus on direct


recall of facts, definitions, and key information (i.e., 'one best answer' style).
Do not create complex clinical scenarios.

RULES:
1. Generate 3-5 non-case-based questions.
2. For each question, provide: "question", "options" (an array of 4
strings), "correctAnswer" (a string matching one of the options), "source" (string
indicating the university and year, e.g., 'Cairo University - 2022'), and "tags"
(an array of strings like "Direct Recall", "Egyptian Exam").
3. CRITICAL: The "explanation" must be in **Markdown format**. It
should be simple, direct, and clear, making it easy for a student to understand.
Use bolding for key terms. It must briefly explain why the correct answer is right
and why the other options are wrong.
4. Your questions MUST synthesize information from BOTH the 'MAIN
TEXT' and the 'HIGH-YIELD INFORMATION' when relevant. Prioritize testing concepts
from the high-yield info.
5. DO NOT repeat any questions from the provided history.

PRACTICE HISTORY (questions to avoid):


---
${[Link] > 0 ? [Link]([Link](q => [Link])) :
"None"}
---

KNOWLEDGE SOURCES:
---
MAIN TEXT:
${text}
---
HIGH-YIELD INFORMATION:
${highYieldText || 'No supplementary high-yield information was
provided for this topic.'}
---

Return the result as a JSON object with a single root key "questions"
which is an array of question objects.`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
source: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "source", "tags"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid Egyptian exam questions. The
AI's response was not in the expected format.");
}

return [Link];

} catch (error) {
[Link]("Error generating Egyptian exam questions:", error);
throw new Error("Could not generate Egyptian exam questions. Please try
again later.");
}
};

export const generateStudyMcqs = async (text: string): Promise<PracticeQuestion[]>


=> {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Your task is to act as a meticulous study guide creator.
Based on the following text, generate a set of multiple-choice questions designed
to ensure the user learns and memorizes *every single piece of information*
present. The user will use these questions as their primary study method, so
comprehensiveness is critical.

RULES:
1. Scrutinize the text and create a question for every single fact,
definition, concept, or key detail. Do not skip any information.
2. The number of questions should not be fixed. Generate as many
questions as necessary to cover the entire text exhaustively.
3. For each question, provide: "question", "options" (an array of 4
strings), "correctAnswer" (a string matching one of the options), and a detailed
"explanation" that reinforces the learning point.
4. Frame questions to test direct recall of the information as
presented in the text.

TEXT TO USE FOR QUESTIONS:


---
${text}
---

Return the result as a JSON object with a single root key "questions"
which is an array of question objects.`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
},
required: ["question", "options", "correctAnswer",
"explanation"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid study questions. The AI's
response was not in the expected format.");
}

return [Link];

} catch (error) {
[Link]("Error generating study questions:", error);
throw new Error("Could not generate study questions. Please try again
later.");
}
};
export const generateStudyHighlightsMcqs = async (text: string, highYieldText:
string | undefined, history: PracticeQuestion[] = []): Promise<PracticeQuestion[]>
=> {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Act as a study assistant. Your goal is to help a user learn
the main points from the provided text by creating multiple-choice questions.

RULES:
1. Generate 5-10 multiple-choice questions that cover the most
important highlights, key concepts, and main ideas from the text.
2. Prioritize creating questions from the "HIGH-YIELD INFORMATION"
first, then supplement with key concepts from the "MAIN TEXT".
3. For each question, provide: "question", "options" (an array of 4
strings), "correctAnswer" (a string matching one of the options), and "tags" (an
array of strings like "Highlight", "Key Concept").
4. CRITICAL: The "explanation" must be in **Markdown format**. It must
be simple, direct, and clear, making it easy for a student to understand. Use
bolding for key terms.

KNOWLEDGE SOURCES:
---
MAIN TEXT:
${text}
---
HIGH-YIELD INFORMATION:
${highYieldText || 'No supplementary high-yield information was
provided for this topic.'}
---

Return the result as a JSON object with a single root key "questions"
which is an array of question objects.`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "tags"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid study highlight questions.
The AI's response was not in the expected format.");
}

return [Link];

} catch (error) {
[Link]("Error generating study highlight questions:", error);
throw new Error("Could not generate study highlight questions. Please try
again later.");
}
};

export const generateRealExamQuestions = async (text: string, highYieldText: string


| undefined, history: PracticeQuestion[] = []): Promise<PracticeQuestion[]> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Based on the provided text, act as a subject matter expert
and generate 3-5 multiple-choice questions that are similar in style and difficulty
to real past exam questions from 2020 onwards.

RULES:
1. Generate 3-5 questions.
2. For each question, provide: "question", "options" (an array of 4
strings), "correctAnswer" (a string matching one of the options), "source" (string
indicating which past exam style it's modeled after, e.g., 'Cardiology Exam 2022'),
and "tags" (an array of strings like "Case-based", "High-Yield").
3. CRITICAL: The "explanation" must be in **Markdown format**. It
should be simple, direct, and clear, making it easy for a student to understand.
Use bolding for key terms.
4. Your questions MUST synthesize information from BOTH the 'MAIN
TEXT' and the 'HIGH-YIELD INFORMATION' when relevant. Prioritize testing concepts
from the high-yield info.
5. DO NOT repeat any questions from the provided history.

PRACTICE HISTORY (questions to avoid):


---
${[Link] > 0 ? [Link]([Link](q => [Link])) :
"None"}
---

KNOWLEDGE SOURCES:
---
MAIN TEXT:
${text}
---
HIGH-YIELD INFORMATION:
${highYieldText || 'No supplementary high-yield information was
provided for this topic.'}
---
Return the result as a JSON object with a single root key "questions"
which is an array of question objects.`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
source: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "source", "tags"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid real exam questions. The
AI's response was not in the expected format.");
}

return [Link];

} catch (error) {
[Link]("Error generating real exam questions:", error);
throw new Error("Could not generate real exam questions. Please try again
later.");
}
};

export const generateImpossibleQuestions = async (text: string, highYieldText:


string | undefined, history: PracticeQuestion[] = []): Promise<PracticeQuestion[]>
=> {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Act as an expert medical examiner from a top-tier Egyptian
medical university (like Cairo University or Ain Shams University). Your task is to
generate 3-5 extremely challenging multiple-choice questions based on the provided
text. These questions should be modeled after the super hard, tricky questions from
past Egyptian medical curriculum exams from 2020 onwards—the kind that almost no
students were able to get right. They should reflect complex, multi-step reasoning.

RULES:
1. Generate 3-5 questions.
2. Include a mix of question styles: some requiring deep conceptual
understanding, some tricky questions with subtle distractors, and some standard-
difficulty questions to balance the set.
3. For each question, provide: "question", "options" (an array of 4-5
strings), "correctAnswer", a "source" (string indicating the exam style, e.g.,
'Cairo University Final Exam 2021'), and "tags" (an array of strings like "Tricky",
"Multi-step", "Impossible").
4. CRITICAL: The "explanation" must be very detailed but clear, in
**Markdown format**. It should thoroughly break down the complex reasoning,
explaining not just the correct answer but why the subtle distractors are
incorrect. Use bolding for key terms.
5. Your questions MUST synthesize information from BOTH the 'MAIN
TEXT' and the 'HIGH-YIELD INFORMATION' when relevant. Prioritize testing concepts
from the high-yield info.
6. DO NOT repeat any questions from the provided history.

PRACTICE HISTORY (questions to avoid):


---
${[Link] > 0 ? [Link]([Link](q => [Link])) :
"None"}
---

KNOWLEDGE SOURCES:
---
MAIN TEXT:
${text}
---
HIGH-YIELD INFORMATION:
${highYieldText || 'No supplementary high-yield information was
provided for this topic.'}
---

Return the result as a JSON object with a single root key "questions"
which is an array of question objects.`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
source: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "source", "tags"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid impossible questions. The
AI's response was not in the expected format.");
}

return [Link];

} catch (error) {
[Link]("Error generating impossible questions:", error);
throw new Error("Could not generate impossible questions. Please try again
later.");
}
};

export const generateMockExamQuestions = async (


lessonText: string,
highYieldText: string,
options: { caseBased: boolean, nonCase: boolean, misc: boolean, foundational:
boolean, numQuestions: number },
history: PracticeQuestion[] = []
): Promise<PracticeQuestion[]> => {
try {
const styles = [];
if ([Link]) [Link]('case-based clinical scenarios');
if ([Link]) [Link]('direct non-case-based questions');
if ([Link]) [Link]('miscellaneous/general knowledge questions on
clinical signs, symptoms, and findings');
if ([Link]) [Link]('foundational knowledge questions
(e.g., anatomy, pathology)');

if ([Link] === 0) {
return [];
}

let questionStyle = '';


if ([Link] > 2) {
const last = [Link]();
questionStyle = `a mix of ${[Link](', ')}, and ${last}`;
} else if ([Link] === 2) {
questionStyle = `a mix of ${styles[0]} and ${styles[1]}`;
} else {
questionStyle = styles[0];
}

const response = await [Link]({


model: "gemini-2.5-flash",
contents: `Act as an expert medical exam creator for a top-tier
university. Your task is to generate a highly realistic mock exam based on two
sources: the main 'LESSON TEXT' from a textbook and supplementary 'HIGH-YIELD
INFORMATION' which contains crucial details not found in the book.

Your questions MUST be representative of challenging university-level


exams. This means you should:
1. **Synthesize from Provided Text:** For some questions, create
challenges that require the student to connect concepts from the 'LESSON TEXT' with
facts from the 'HIGH-YIELD INFORMATION'.
2. **Test Broader Clinical Knowledge (Misc/General):** When asked for
miscellaneous/general questions, you MUST act as an experienced clinical professor
creating a university-level exam. Your questions in this category should test a
student's deeper understanding of clinical practice, focusing specifically on
**signs, symptoms, and clinical findings**.
* Generate questions about classic or subtle signs, symptoms, and
findings related to the topics in the provided text.
* These questions should be designed to bridge the gap between
textbook knowledge and real-world application. Therefore, they should cover
information that is:
* **Implicitly mentioned or hinted at in the text**, but
requires deeper synthesis to understand (e.g., connecting a described
pathophysiology to a classic clinical sign).
* **Not mentioned in the text at all**, but represents
essential clinical knowledge a student is expected to know for exams (e.g., a
pathognomonic finding or a common but unlisted symptom like "uremic frost" or an
"earthy look" in chronic kidney disease).
* These questions test the student's ability to think like a
clinician, identifying key diagnostic clues beyond what's explicitly written.
3. **Test Foundational Knowledge:** When asked for foundational
knowledge questions, you MUST act as a university professor setting an exam.
Generate questions that test the essential prerequisite knowledge required to
understand the main topic. For a medical topic like 'Nephrology', this would
include questions on renal anatomy, physiology, and relevant general pathology
(e.g., "Which kidney is typically lower, the right or the left?"). These questions
are designed to ensure the student has the fundamental building blocks mastered.
Like the 'Misc/General' questions, these SHOULD NOT be directly answerable from the
provided texts, as they test assumed prior knowledge from a broader curriculum.
4. **Mimic Real Exam Style:** Generate a mix of clinically oriented,
tricky questions. Use realistic patient vignettes (case-based), challenging direct
recall questions (non-case-based), and the broader general knowledge questions as
requested.
5. **Prioritize High-Yield Content:** The 'HIGH-YIELD INFORMATION' is
critical. Ensure a significant portion of the questions test this specific
material, either directly or in combination with the lesson text.
6. **Vague Questioning for Foundational/Misc Knowledge:** When
creating 'Foundational' or 'Misc/General' questions, intentionally make the
question stem direct and somewhat vague, just like in real exams. Do not include
the "why" or the underlying reason in the question itself. The student should be
tested on their recall of the fact. For example, a good question is "Which kidney
is lower?" not "Which kidney is lower due to the position of the liver?". The
detailed reason should then be provided in the "explanation" field.

RULES:
1. You MUST generate exactly ${[Link]} questions. No
more, no less.
2. The questions should be ${questionStyle}.
3. For each question, provide: "question", "options" (4 strings),
"correctAnswer", a "source" (e.g., 'Mock Exam - Case-Based'), "sourceLessonId"
(matching the ID from the lesson marker), and "tags" (an array of strings
indicating the question type, e.g., "Case-Based", "Foundational", "Clinical Sign").
4. CRITICAL: The "explanation" must be in **Markdown format**. For
questions based on the text, it must cite evidence from BOTH the lesson text and
high-yield info where applicable. For general knowledge questions, it should
provide a concise, accurate clinical explanation. Use bolding for key terms.
5. **Avoid Repetition:** DO NOT generate questions that are the same
as or very similar to the ones provided in the 'PRACTICE HISTORY'. CRITICAL: You
must ensure the new questions are substantively different. Do not just rephrase old
questions.

KNOWLEDGE SOURCES:
---
LESSON TEXT:
${lessonText}
---
HIGH-YIELD INFORMATION:
${highYieldText || 'No supplementary high-yield information was
provided for this topic.'}
---
PRACTICE HISTORY (questions to avoid):
---
${[Link] > 0 ? [Link]([Link](q => [Link])) :
"None"}
---

Return the result as a JSON object with a single root key "questions"
which is an array of question objects.`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
source: { type: [Link] },
sourceLessonId: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "source", "sourceLessonId", "tags"]
}
}
},
required: ["questions"]
}
}
});
const jsonStr = [Link]();
const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid mock exam questions. The
AI's response was not in the expected format.");
}

if ([Link] > [Link]) {


[Link](`AI returned ${[Link]} questions, but $
{[Link]} were requested. Truncating result.`);
[Link] = [Link](0, [Link]);
}

return [Link];

} catch (error) {
[Link]("Error generating mock exam questions:", error);
throw new Error("Could not generate mock exam questions. Please try again
later.");
}
};

export const analyzeRecall = async (originalText: string, userRecall: string):


Promise<string> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `A user is testing their memory. Below is the original text
and the user's attempt to recall it.
Compare the user's recall to the original text and provide constructive
feedback in Markdown format.
Focus on:
1. **Key Concepts Missed:** What important topics or details from the
original text did the user forget? List them as bullet points.
2. **Inaccuracies:** Were there any points in the user's recall that
contradict the original text? Point them out gently.
3. **What they got right:** Briefly mention topics the user remembered
correctly to be encouraging.

Keep the feedback concise and focused on helping the user improve their
memory of the material.

ORIGINAL TEXT:
---
${originalText}
---

USER'S RECALL ATTEMPT:


---
${userRecall}
---`,
});
return [Link];
} catch (error) {
[Link]("Error analyzing recall:", error);
throw new Error("Could not analyze your recall attempt. Please try again
later.");
}
};

export const generateHighYieldInfo = async (lessonText: string, lessonTitle:


string): Promise<string> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Act as an expert tutor and subject matter specialist. The
user is studying a lesson titled "${lessonTitle}" from a textbook. The content of
the lesson is provided below.

Your task is to generate "High-Yield Information" related to this


topic. This information should be concise, exam-focused, and highly relevant for a
student preparing for a tough exam.

RULES:
1. **Go Beyond the Text:** Your most important task is to include
crucial, high-yield facts, concepts, mnemonics, or clinical pearls that are related
to the lesson's topic but may NOT be explicitly mentioned in the provided text.
Think about what a professor would emphasize in a review session.
2. **Summarize Key Points:** Briefly summarize the absolute must-know
points from the provided text.
3. **Format for Learning:** Use Markdown for clear formatting. Use
headings, bold text, bullet points (especially for lists of symptoms, treatments,
etc.), and even tables if it helps organize information.
4. **Be Concise:** Keep the information dense and to the point. Avoid
long paragraphs.
5. **Return Only Markdown:** Do not include any introductory or
concluding sentences like "Here is the high-yield information...". Just return the
formatted Markdown content.

LESSON TEXT FOR CONTEXT:


---
${lessonText}
---
`,
});
return [Link];
} catch (error) {
[Link]("Error generating high-yield info:", error);
throw new Error("Could not generate high-yield information. Please try
again later.");
}
};

export const generateClinicalPearls = async (lessonText: string, lessonTitle:


string): Promise<string> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Act as a seasoned clinical professor and mentor reviewing a
textbook chapter on "${lessonTitle}" with a medical student. The chapter content is
provided below.

Your task is to provide "Clinical Pearls" — the kind of indispensable,


practical knowledge that bridges the gap between textbooks and real-world patient
care. This information is what an experienced physician would expect a student to
know for exams and rotations.
RULES:
1. **Prioritize Assumed Knowledge:** Focus on classic signs, symptoms
(e.g., "uremic frost" for CKD), pathognomonic findings, key anatomical
relationships (e.g., which kidney is lower), and essential physiological concepts
that are prerequisites for understanding the topic but may be absent from the text.
2. **Include Clinical Wisdom:** Add mnemonics, diagnostic shortcuts,
common pitfalls in diagnosis/treatment, and connections between seemingly disparate
facts.
3. **Synthesize, Don't Just Summarize:** Go beyond summarizing the
provided text. Extract the most critical points and then enrich them with this
external, experience-based knowledge.
4. **Format for Impact:** Use clear Markdown formatting. Use headings,
bold text, and bullet points. Keep it concise and highly scannable.
5. **Output Only Markdown:** Return only the formatted Markdown
content. Do not include introductory phrases like "Here are the clinical
pearls...".

TEXTBOOK CHAPTER CONTENT FOR CONTEXT:


---
${lessonText}
---
`,
});
return [Link];
} catch (error) {
[Link]("Error generating clinical pearls:", error);
throw new Error("Could not generate Clinical Pearls. Please try again
later.");
}
};

export const generateClinicalPearlsQuestions = async (clinicalPearlsText: string,


history: PracticeQuestion[] = []): Promise<PracticeQuestion[]> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Act as a clinical professor creating a challenging quiz for
medical students. Based ONLY on the provided "Clinical Pearls" text, generate 3-5
multiple-choice questions. These questions should test the students' understanding
of the high-yield, practical knowledge presented.

RULES:
1. Generate 3-5 questions.
2. For each question, provide: "question", "options" (an array of 4
strings), "correctAnswer", a "source" (string, which should be 'Clinical Pearl'),
and "tags" (an array of strings like "Clinical Pearl", "Anatomy", "Pro-Tip").
3. CRITICAL: The "explanation" must be in **Markdown format**. It
should be simple, direct, and clear, making it easy for a student to understand,
referencing the concepts from the provided Clinical Pearls. Use bolding for key
terms.
4. DO NOT repeat any questions from the provided history.

PRACTICE HISTORY (questions to avoid):


---
${[Link] > 0 ? [Link]([Link](q => [Link])) :
"None"}
---
SOURCE TEXT (CLINICAL PEARLS):
---
${clinicalPearlsText}
---

Return the result as a JSON object with a single root key "questions"
which is an array of question objects.`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
source: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "source", "tags"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid clinical pearls questions.
The AI's response was not in the expected format.");
}

return [Link];

} catch (error) {
[Link]("Error generating clinical pearls questions:", error);
throw new Error("Could not generate clinical pearls questions. Please try
again later.");
}
};

export const generateEfficientStudyQuestions = async (lessonText: string,


highYieldInfo: string, clinicalPearls: string, history: PracticeQuestion[] = []):
Promise<PracticeQuestion[]> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Act as a master educator creating a single, highly efficient
study quiz for a medical student. This quiz must be the ultimate review tool,
covering all available information for a lesson.

You are provided with three sources of information:


1. **MAIN TEXT:** The core textbook content.
2. **HIGH-YIELD INFO:** A summary of critical, must-know facts, some
of which may not be in the main text.
3. **CLINICAL PEARLS:** Practical, physician-level knowledge,
including assumed foundational concepts (like anatomy) and clinical signs (like
"uremic frost").

YOUR TASK:
Generate a balanced set of 10-15 multiple-choice questions that
intelligently synthesizes information from ALL THREE sources. This is not just a
random collection; it is a curated set designed for maximum learning efficiency.

RULES:
1. **Comprehensive Coverage:** Your questions MUST cover key concepts
from the Main Text, facts from the High-Yield Info, AND the practical wisdom from
the Clinical Pearls.
2. **Balanced Mix:** Create a mix of question types. Include direct
recall questions, questions that require synthesis of ideas, and questions testing
the "assumed" knowledge found in the Clinical Pearls.
3. **Prioritize Integration:** Create some questions that require the
student to connect a concept from the Main Text with a detail from the High-Yield
Info or a Clinical Pearl.
4. **For each question, provide:** "question", "options" (4 strings),
"correctAnswer", a "source" ('Efficient Study Mix'), and "tags" (an array
accurately describing the question's focus, e.g., "High-Yield", "Clinical Pearl",
"Core Concept", "Anatomy").
5. **CRITICAL:** The "explanation" for each question must be in
**Markdown format**. It must be exceptionally clear, concise, and use bolding for
key terms to aid memory.
6. **Avoid Repetition:** DO NOT repeat questions from the provided
history.

PRACTICE HISTORY (questions to avoid):


---
${[Link] > 0 ? [Link]([Link](q => [Link])) :
"None"}
---

KNOWLEDGE SOURCES:
---
MAIN TEXT:
${lessonText}
---
HIGH-YIELD INFO:
${highYieldInfo}
---
CLINICAL PEARLS:
${clinicalPearls}
---

Return the result as a JSON object with a single root key "questions"
which is an array of question objects.`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
source: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "source", "tags"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid efficient study questions.
The AI's response was not in the expected format.");
}

return [Link];

} catch (error) {
[Link]("Error generating efficient study questions:", error);
throw new Error("Could not generate efficient study questions. Please try
again later.");
}
};

export const generatePostGraduatePrepQuestions = async (lessonText: string,


highYieldText: string, history: PracticeQuestion[] = []):
Promise<PracticeQuestion[]> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Act as a board-certified clinical educator preparing a
resident for their final exams. Based on the provided clinical text, your task is
to create 3-5 challenging, post-graduate level, case-based multiple-choice
questions. These questions are for teaching and preparation, not just testing.

RULES:
1. **Generate 3-5 questions.**
2. **Create Realistic Cases:** Each question should present a clinical
vignette or scenario that requires multi-step reasoning.
3. **For each question, provide:** "question", "options" (4 strings),
"correctAnswer", a "source" ('Post-Graduate Prep'), and "tags" (e.g., "Case-Based",
"Clinical Reasoning", "PG-Prep").
4. **CRITICAL - Tutorial-Style Explanation:** The "explanation" is the
most important part. It MUST be a detailed, step-by-step tutorial in **Markdown
format**. It should:
* Clearly break down the clinical reasoning process.
* Explain why the correct answer is the best choice, citing
evidence from the provided text.
* Thoroughly analyze why EACH of the other options is incorrect.
* Use bolding, bullet points, and clear headings to make it an
effective learning tool.
5. **Synthesize Knowledge:** Questions should require synthesizing
information from both the 'MAIN TEXT' and the 'HIGH-YIELD INFORMATION'.
6. **Avoid Repetition:** Do not repeat questions from the provided
history.

PRACTICE HISTORY (questions to avoid):


---
${[Link] > 0 ? [Link]([Link](q => [Link])) :
"None"}
---

KNOWLEDGE SOURCES:
---
MAIN TEXT:
${lessonText}
---
HIGH-YIELD INFORMATION:
${highYieldText || 'No supplementary high-yield information was
provided for this topic.'}
---

Return the result as a JSON object with a single root key


"questions".`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
source: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "source", "tags"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);
if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid Post-Graduate Prep
questions.");
}
return [Link];

} catch (error) {
[Link]("Error generating Post-Graduate Prep questions:", error);
throw new Error("Could not generate Post-Graduate Prep questions. Please
try again later.");
}
};

export const generatePostGraduateExamQuestions = async (lessonText: string,


highYieldText: string, history: PracticeQuestion[] = []):
Promise<PracticeQuestion[]> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `Act as a board exam question writer. Your task is to create
3-5 difficult, post-graduate level multiple-choice questions suitable for a real
board exam. These questions should test for rapid recall and application of
knowledge under pressure.

RULES:
1. **Generate 3-5 questions.**
2. **Style:** Questions can be case-based or direct, but must be
challenging and reflect the complexity of post-graduate exams. Focus on subtle
distinctions and high-yield facts.
3. **For each question, provide:** "question", "options" (4 strings),
"correctAnswer", a "source" ('Post-Graduate Exam'), and "tags" (e.g., "Board-
Style", "High-Yield", "PG-Exam").
4. **CRITICAL - Concise Explanation:** The "explanation" must be in
**Markdown format**, but it should be concise, direct, and to the point. Use
bolding for key terms. This is for rapid-fire review, not a deep-dive tutorial.
5. **Synthesize Knowledge:** Questions should require synthesizing
information from both the 'MAIN TEXT' and the 'HIGH-YIELD INFORMATION'.
6. **Avoid Repetition:** Do not repeat questions from the provided
history.

PRACTICE HISTORY (questions to avoid):


---
${[Link] > 0 ? [Link]([Link](q => [Link])) :
"None"}
---

KNOWLEDGE SOURCES:
---
MAIN TEXT:
${lessonText}
---
HIGH-YIELD INFORMATION:
${highYieldText || 'No supplementary high-yield information was
provided for this topic.'}
---

Return the result as a JSON object with a single root key


"questions".`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
source: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "source", "tags"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);
if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid Post-Graduate Exam
questions.");
}
return [Link];

} catch (error) {
[Link]("Error generating Post-Graduate Exam questions:", error);
throw new Error("Could not generate Post-Graduate Exam questions. Please
try again later.");
}
};

export const generateGroundedResponse = async (documentText: string, question:


string): Promise<GroundedResponse> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `You are a helpful assistant who answers questions based
*only* on the provided source document.
The document text is provided below. Each page is prefixed by its
source and page number, like "[Source: [Link] | page X]".
Your task is to answer the user's question and cite the source file and
page number(s) where you found the information.

RULES:
1. Base your answer *exclusively* on the provided text. Do not use
outside knowledge.
2. If the answer cannot be found in the text, state that clearly.
3. Identify the source and page numbers that support your answer.
4. Return a JSON object with two keys: "answer" (string) and
"citations" (an array of objects).
5. Each citation object must have a "source" (string, the filename)
and a "page" (number).

SOURCE DOCUMENT:
---
${documentText}
---

USER QUESTION: "${question}"


`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
answer: { type: [Link] },
citations: {
type: [Link],
items: {
type: [Link],
properties: {
source: { type: [Link] },
page: { type: [Link] }
},
required: ["source", "page"]
}
}
},
required: ["answer", "citations"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<GroundedResponse>(jsonStr);

if (!parsed) {
throw new Error("The AI's response was not in the expected JSON
format.");
}

return parsed;

} catch (error) {
[Link]("Error generating grounded response:", error);
throw new Error("Could not generate a response. Please try again.");
}
};

export const generateQuestionsFromExamFile = async (text: string):


Promise<PracticeQuestion[]> => {
try {
const response = await [Link]({
model: "gemini-2.5-flash",
contents: `You are an expert AI assistant that converts text from exam
documents into a structured JSON format. Your task is to analyze the provided text,
identify each multiple-choice question, its options, and the correct answer. For
each question, you MUST also generate a brief but clear explanation for why the
answer is correct.

RULES:
1. Scan the entire text to find questions and answers.
2. The correct answer might be indicated by an asterisk (*), bolding,
underlining, or listed in an answer key (often at the end of the document).
Prioritize finding an explicitly marked answer.
3. If you cannot determine the correct answer from any explicit
marking or answer key, use the context of the entire document to infer the most
likely correct answer.
4. For each question, provide: "question", "options" (array of 4-5
strings), "correctAnswer" (a string exactly matching one of the options), and
"explanation" formatted in **Markdown**. Also include "tags" as an empty array.
5. The output must be a valid JSON object with a single root key
"questions", which is an array of question objects. Do not include any other text
or markdown.

EXAM TEXT TO PARSE:


---
${text}
---`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: [Link],
properties: {
questions: {
type: [Link],
items: {
type: [Link],
properties: {
question: { type: [Link] },
options: { type: [Link], items: { type:
[Link] } },
correctAnswer: { type: [Link] },
explanation: { type: [Link] },
tags: { type: [Link], items: { type:
[Link] } },
},
required: ["question", "options", "correctAnswer",
"explanation", "tags"]
}
}
},
required: ["questions"]
}
}
});

const jsonStr = [Link]();


const parsed = parseJsonGracefully<{ questions:
PracticeQuestion[] }>(jsonStr);

if (!parsed || ![Link]([Link])) {
throw new Error("Failed to generate valid questions from the exam
file. The AI's response was not in the expected format.");
}

return [Link];

} catch (error) {
[Link]("Error generating questions from exam file:", error);
throw new Error("Could not process the exam file. Please ensure it is a
text-based document and try again.");
}
};
--- END OF FILE services/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState, useMemo, useEffect } from 'react';
import { StudyStatus, Lesson, PracticeQuestion, QuizType, UserExam } from
'../[Link]';
import { generateMockExamQuestions, generateHighYieldInfo,
generatePostGraduatePrepQuestions, generatePostGraduateExamQuestions } from
'../services/[Link]';
import { TargetIcon, BookOpenIcon, CheckCircleIcon, ClockIcon, AlertTriangleIcon,
DownloadIcon, ClipboardListIcon, ChevronDownIcon, ZoomInIcon, HistoryIcon,
ClipboardCheckIcon, Trash2Icon, FileTextIcon, GraduationCapIcon } from
'./icons/[Link]';
import MockExamModal from './[Link]';
import ReviewMistakesModal from './[Link]';
import Loader from './[Link]';
import ExamUploader from './[Link]';
import UserExamModal from './[Link]';

interface DashboardProps {
combinedPages: string[];
sessionName: string;
lessonsData: Lesson[];
onSelectLesson: (lessonId: string) => void;
dailyGoal: number;
setDailyGoal: (goal: number) => void;
lessonsStudiedToday: number;
updateLesson: (lessonId: string, updates: Partial<Lesson>) => void;
addQuestionsToHistory: (lessonId: string, questions: PracticeQuestion[], type:
QuizType) => void;
userExams: UserExam[];
addUserExams: (exams: UserExam[]) => void;
deleteUserExam: (examId: string) => void;
}

const statusConfig: { [key in StudyStatus]: { label: string; description: string;


color: string; icon: [Link] } } = {
green: { label: "Strong Recall", description: "Studied recently.", color: 'var(--
status-green)', icon: CheckCircleIcon },
yellow: { label: "Fading", description: "Needs review soon.", color: 'var(--
status-yellow)', icon: ClockIcon },
red: { label: "Needs Review", description: "Not studied in a while.", color:
'var(--status-red)', icon: AlertTriangleIcon },
};

const CONQUEST_STEP_IDS = ['review', 'efficient', 'deep_dive', 'impossible'];

const Dashboard: [Link]<DashboardProps> = ({ combinedPages, sessionName,


lessonsData, onSelectLesson, dailyGoal, setDailyGoal, lessonsStudiedToday,
updateLesson, addQuestionsToHistory, userExams, addUserExams, deleteUserExam }) =>
{
const [isEditingGoal, setIsEditingGoal] = useState(false);
const [newGoal, setNewGoal] = useState(dailyGoal);

// Mock Exam State


const [isMockLessonSelectorOpen, setIsMockLessonSelectorOpen] = useState(false);
const mockExamLessonsKey = `mockExamLessons_${sessionName}`;
const [mockExamLessons, setMockExamLessons] = useState<Set<string>>(() => {
try {
const stored = [Link](mockExamLessonsKey);
return stored ? new Set([Link](stored)) : new Set();
} catch {
return new Set();
}
});
const [mockExamQuestionTypes, setMockExamQuestionTypes] = useState({ case: true,
nonCase: true, misc: true, foundational: false });
const [isMockExamModalOpen, setIsMockExamModalOpen] = useState(false);
const [mockExamQuestions, setMockExamQuestions] =
useState<PracticeQuestion[]>([]);
const [mockExamNumQuestions, setMockExamNumQuestions] = useState(10);
const [isGeneratingMockExam, setIsGeneratingMockExam] = useState(false);
const [mockExamError, setMockExamError] = useState<string | null>(null);

// Post-Graduate Prep State


const [pgPrepLessons, setPgPrepLessons] = useState<Set<string>>(new Set());
const [pgPrepNumQuestions, setPgPrepNumQuestions] = useState(5);
const [isGeneratingPgPrep, setIsGeneratingPgPrep] = useState(false);
const [pgPrepError, setPgPrepError] = useState<string | null>(null);

// Post-Graduate Exam State


const [pgExamLessons, setPgExamLessons] = useState<Set<string>>(new Set());
const [pgExamNumQuestions, setPgExamNumQuestions] = useState(5);
const [isGeneratingPgExam, setIsGeneratingPgExam] = useState(false);
const [pgExamError, setPgExamError] = useState<string | null>(null);

// Review State
const [isReviewModalOpen, setIsReviewModalOpen] = useState(false);
const [reviewModalTitle, setReviewModalTitle] = useState('');
const [questionsToReview, setQuestionsToReview] =
useState<PracticeQuestion[]>([]);
const allMistakes = useMemo(() => {
return [Link](lesson => {
const histories = [
lesson.october6ExamHistory,
[Link],
[Link],
[Link],
[Link],
[Link],
[Link],
];
return [Link](history => history?.filter(q => [Link] ===
false) || []);
});
}, [lessonsData]);

// Deep Dive State


const allStudyMcqs = useMemo(() => {
return [Link](lesson => [Link] || []);
}, [lessonsData]);

const correctStudyMcqsCount = useMemo(() => {


return [Link](q => [Link] === true).length;
}, [allStudyMcqs]);

const deepDiveCoverage = [Link] > 0 ? (correctStudyMcqsCount /


[Link]) * 100 : 0;

const incorrectStudyMcqs = useMemo(() => {


return [Link](q => [Link] === false);
}, [allStudyMcqs]);

// Custom User Exams State


const [activeUserExam, setActiveUserExam] = useState<UserExam | null>(null);

useEffect(() => {
try {
[Link](mockExamLessonsKey,
[Link]([Link](mockExamLessons)));
} catch (error) {
[Link]("Failed to save mock exam lessons selection:", error);
}
}, [mockExamLessons, mockExamLessonsKey]);

const totalLessons = [Link];


const lessonsStudied = [Link](s => [Link] > new
Date(0).toISOString()).length;

const progressPercent = totalLessons > 0 ? (lessonsStudied / totalLessons) *


100 : 0;
const dailyProgressPercent = dailyGoal > 0 ? (lessonsStudiedToday / dailyGoal) *
100 : 0;

const lessonsByStatus = {
green: [Link](p => [Link] === 'green').length,
yellow: [Link](p => [Link] === 'yellow').length,
red: [Link](p => [Link] === 'red').length,
};

const selectedMockExamHistory = useMemo(() => {


if ([Link] === 0) return [];
return lessonsData
.filter(l => [Link]([Link]))
.flatMap(l => [Link] || []);
}, [mockExamLessons, lessonsData]);

const correctMockQuestions = useMemo(() => [Link](q =>


[Link] === true), [selectedMockExamHistory]);
const wrongMockQuestions = useMemo(() => [Link](q =>
[Link] === false), [selectedMockExamHistory]);

const handleGoalSave = () => {


if (newGoal > 0) {
setDailyGoal(newGoal);
}
setIsEditingGoal(false);
};

const handleExportData = () => {


const data: { [key: string]: any } = {};
const appKeys = new Set<string>();

for (let i = 0; i < [Link]; i++) {


const key = [Link](i);
if (key && ([Link]('lessonsData_') || [Link]('notebook_')
|| [Link]('quizSessions_') || [Link]('userExams_') || ['dailyGoal',
'app-theme', 'appMode', 'sessionName', 'sessionDocuments'].includes(key))) {
[Link](key);
}
}

[Link](key => {
const value = [Link](key);
if (value !== null) {
try {
data[key] = [Link](value);
} catch (e) {
data[key] = value;
}
}
});

const blob = new Blob([[Link](data, null, 2)], { type:


'application/json' });
const url = [Link](blob);
const a = [Link]('a');
[Link] = url;
[Link] = `intellistudy-backup-${new Date().toISOString().split('T')
[0]}.json`;
[Link](a);
[Link]();
[Link](a);
[Link](url);
};

const getLessonText = (lesson: Lesson) => {


if (!lesson) return '';
return [Link]([Link] - 1, [Link]).join('\n\n');
};

const prepareLessonsForGeneration = async (lessonIds: Set<string>) => {


const selectedLessonsData = [Link](l => [Link]([Link]));
let updatedLessonsData = [...selectedLessonsData];

const lessonsToUpdate = [Link](l => ![Link]);


if ([Link] > 0) {
await [Link]([Link](async lesson => {
const info = await generateHighYieldInfo(getLessonText(lesson),
[Link]);
updateLesson([Link], { highYieldInfo: info });
const lessonInCopy = [Link](l => [Link] === [Link]);
if (lessonInCopy) {
[Link] = info;
}
}));
}

const combinedText = [Link](l => `--- LESSON START (ID: $


{[Link]}) ---\n${getLessonText(l)}`).join('\n\n\n--- NEW LESSON ---\n\n\n');
const combinedHighYieldInfo = [Link](l =>
[Link]).filter(Boolean).join('\n\n---\n\n');

if (![Link]()) throw new Error("Please select lessons with content


to generate an exam.");

return { updatedLessonsData, combinedText, combinedHighYieldInfo };


};

const handleGenerateMockExam = async () => {


setMockExamError(null);
setIsGeneratingMockExam(true);
try {
const { updatedLessonsData, combinedText, combinedHighYieldInfo } = await
prepareLessonsForGeneration(mockExamLessons);
const history = [Link](l => [Link] || []);
const questions = await generateMockExamQuestions(combinedText,
combinedHighYieldInfo, {
caseBased: [Link],
nonCase: [Link],
misc: [Link],
foundational: [Link],
numQuestions: mockExamNumQuestions,
}, history);

if (!questions || [Link] === 0) throw new Error("The AI could not


generate questions. Try selecting more lessons or changing question types.");

setMockExamQuestions([Link](() => [Link]() - 0.5));


setIsMockExamModalOpen(true);
} catch (err) {
setMockExamError(err instanceof Error ? [Link] : "An unknown error
occurred.");
} finally {
setIsGeneratingMockExam(false);
}
};

const handleGeneratePgPrep = async () => {


setPgPrepError(null);
setIsGeneratingPgPrep(true);
try {
const { updatedLessonsData, combinedText, combinedHighYieldInfo } = await
prepareLessonsForGeneration(pgPrepLessons);
const history = [Link](l => [Link]
|| []);
const questions = await generatePostGraduatePrepQuestions(combinedText,
combinedHighYieldInfo, history);
if (!questions || [Link] === 0) throw new Error("The AI failed to
generate preparation questions.");

setMockExamQuestions(questions);
setIsMockExamModalOpen(true);
} catch (err) {
setPgPrepError(err instanceof Error ? [Link] : "An unknown error
occurred.");
} finally {
setIsGeneratingPgPrep(false);
}
};

const handleGeneratePgExam = async () => {


setPgExamError(null);
setIsGeneratingPgExam(true);
try {
const { updatedLessonsData, combinedText, combinedHighYieldInfo } = await
prepareLessonsForGeneration(pgExamLessons);
const history = [Link](l => [Link]
|| []);
const questions = await generatePostGraduateExamQuestions(combinedText,
combinedHighYieldInfo, history);
if (!questions || [Link] === 0) throw new Error("The AI failed to
generate exam questions.");

setMockExamQuestions(questions);
setIsMockExamModalOpen(true);
} catch (err) {
setPgExamError(err instanceof Error ? [Link] : "An unknown error
occurred.");
} finally {
setIsGeneratingPgExam(false);
}
};

const handleMockExamComplete = (answeredQuestions: PracticeQuestion[]) => {


const questionsByLesson: { [lessonId: string]: PracticeQuestion[] } = {};
[Link](q => {
const lessonId = [Link] || [Link](l =>
[Link]?.includes([Link]))?.id;
if (lessonId) {
if (!questionsByLesson[lessonId]) {
questionsByLesson[lessonId] = [];
}
questionsByLesson[lessonId].push(q);
}
});

[Link](questionsByLesson).forEach(([lessonId, questions]) => {


const type = questions[0]?.source?.includes('Post-Graduate Prep') ?
'postGraduatePrep'
: questions[0]?.source?.includes('Post-Graduate Exam') ?
'postGraduateExam'
: 'mockExam';
addQuestionsToHistory(lessonId, questions, type);
});

setIsMockExamModalOpen(false);
};

const handleReviewAllMistakes = () => {


setReviewModalTitle('Review All Mistakes');
setQuestionsToReview(allMistakes);
setIsReviewModalOpen(true);
};

const handleReviewCorrectMock = () => {


setReviewModalTitle('Review Correct Mock Exam Questions');
setQuestionsToReview(correctMockQuestions);
setIsReviewModalOpen(true);
};

const handleReviewWrongMock = () => {


setReviewModalTitle('Review Incorrect Mock Exam Questions');
setQuestionsToReview(wrongMockQuestions);
setIsReviewModalOpen(true);
};

const handleReviewDeepDiveMistakes = () => {


setReviewModalTitle('Review Deep Dive Mistakes');
setQuestionsToReview(incorrectStudyMcqs);
setIsReviewModalOpen(true);
};

return (
<div className="p-4 sm:p-6 lg:p-8">
{isMockExamModalOpen && <MockExamModal title="Generated Exam"
questions={mockExamQuestions} onClose={() => setIsMockExamModalOpen(false)}
onComplete={handleMockExamComplete} />}
{isReviewModalOpen && <ReviewMistakesModal title={reviewModalTitle}
questions={questionsToReview} onClose={() => setIsReviewModalOpen(false)} />}
{activeUserExam && <UserExamModal exam={activeUserExam} onClose={() =>
setActiveUserExam(null)} />}
<div className="flex justify-between items-center mb-6">
<h1 className="text-3xl font-bold
text-[var(--text-primary)]">Dashboard</h1>
<button
onClick={handleExportData}
className="flex items-center gap-2 px-4 py-2 text-sm font-semibold text-
[var(--text-secondary)] bg-[var(--bg-alt)] border border-[var(--border-color)]
rounded-lg hover:text-[var(--text-primary)] hover:border-[var(--accent-primary)]
transition-colors"
>
<DownloadIcon />
Export Progress
</button>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8


dashboard-stats-grid">
<div className="bg-[var(--bg-alt)] p-6 rounded-lg border border-[var(--
border-color)]">
<div className="flex items-center gap-4 mb-4">
<div className="w-8 h-8 text-[var(--text-accent)]"><TargetIcon /></div>
<h2 className="text-xl font-bold text-[var(--text-primary)]">Daily
Goal</h2>
</div>
<div className="w-full bg-[var(--bg-main)] rounded-full h-4">
<div className="bg-[var(--accent-secondary)] h-4 rounded-full" style={{
width: `${[Link](dailyProgressPercent, 100)}%` }}></div>
</div>
<p className="text-right mt-2 text-sm text-[var(--text-
secondary)]">{lessonsStudiedToday} / {dailyGoal} lessons studied today</p>
{isEditingGoal ? (
<div className="mt-4 flex items-center gap-2">
<input type="number" value={newGoal} onChange={(e) =>
setNewGoal(Number([Link]))} className="w-20 bg-[var(--bg-main)] border
border-[var(--border-color)] rounded-md p-1 text-center"/>
<button onClick={handleGoalSave} className="px-3 py-1 bg-
[var(--accent-primary)] text-white text-sm rounded-md">Save</button>
<button onClick={() => setIsEditingGoal(false)} className="px-3
py-1 bg-[var(--border-color)] text-white text-sm rounded-md">Cancel</button>
</div>
) : (
<button onClick={() => setIsEditingGoal(true)} className="mt-4
text-sm text-[var(--text-accent)] hover:underline">Edit Goal</button>
)}
</div>

<div className="bg-[var(--bg-alt)] p-6 rounded-lg border border-[var(--


border-color)] lg:col-span-2">
<div className="flex items-center gap-4 mb-4">
<div className="w-8 h-8 text-[var(--text-
accent)]"><BookOpenIcon /></div>
<h2 className="text-xl font-bold text-[var(--text-primary)]">Total
Progress</h2>
</div>
<div className="w-full bg-[var(--bg-main)] rounded-full h-4">
<div className="bg-[var(--accent-primary)] h-4 rounded-full"
style={{ width: `${progressPercent}%` }}></div>
</div>
<p className="text-right mt-2 text-sm text-[var(--text-
secondary)]">{lessonsStudied} / {totalLessons} lessons mastered</p>
</div>
</div>

<h2 className="text-2xl font-bold mb-4 text-[var(--text-primary)]">Content


Mastery</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
{(['green', 'yellow', 'red'] as StudyStatus[]).map(status => (
<div key={status} className="bg-[var(--bg-alt)] p-6 rounded-lg border
border-[var(--border-color)]">
<div className="flex items-center gap-4 mb-2">
<div className="w-7 h-7" style={{color:
statusConfig[status].color}}>{[Link](statusConfig[status].icon)}</div>
<h3 className="text-lg font-bold" style={{color:
statusConfig[status].color}}>{statusConfig[status].label}</h3>
</div>
<p className="text-3xl font-bold text-[var(--text-primary)] mb-
2">{lessonsByStatus[status]} <span className="text-lg font-normal text-[var(--text-
secondary)]">lessons</span></p>
<p className="text-sm text-[var(--text-
secondary)]">{statusConfig[status].description}</p>
</div>
))}
</div>

<h2 className="text-2xl font-bold mb-4 text-[var(--text-primary)]">Review


Tools</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div className="bg-[var(--bg-alt)] p-6 rounded-lg border border-[var(--
border-color)]">
<div className="flex items-start gap-4 mb-4">
<div className="w-8 h-8 text-[var(--text-accent)] flex-shrink-
0"><ClipboardListIcon /></div>
<div>
<h3 className="text-xl font-bold">Mock Exam Generator</h3>
<p className="text-sm text-[var(--text-secondary)]">Create a
custom mock exam from selected lessons.</p>
</div>
</div>
<div className="mb-4">
<button onClick={() => setIsMockLessonSelectorOpen(!
isMockLessonSelectorOpen)} className="w-full flex justify-between items-center p-3
bg-[var(--bg-main)] rounded-lg border border-[var(--border-color)] hover:border-
[var(--text-accent)] transition-colors">
<span className="font-semibold text-[var(--text-
primary)]">{[Link] > 0 ? `${[Link]} Lessons Selected` :
'Select Lessons'}</span>
<ChevronDownIcon className={`transition-transform $
{isMockLessonSelectorOpen ? 'rotate-180' : ''}`} />
</button>
{isMockLessonSelectorOpen && (
<div className="mt-2 p-3 bg-[var(--bg-main)] rounded-lg
border border-[var(--border-color)] max-h-40 overflow-y-auto">
{[Link](lesson => (<div key={[Link]}
className="flex items-center p-1.5 rounded-md hover:bg-[var(--bg-alt)]">
<input type="checkbox" id={`mock-lesson-$
{[Link]}`} checked={[Link]([Link])} onChange={() =>
setMockExamLessons(p => {const n = new Set(p); [Link]([Link]) ?
[Link]([Link]) : [Link]([Link]); return n;})} className="w-4 h-4 rounded
text-[var(--accent-primary)] bg-[var(--bg-main)] border-[var(--border-color)]
focus:ring-[var(--accent-primary)]" />
<label htmlFor={`mock-lesson-${[Link]}`}
className="ml-3 text-sm text-[var(--text-secondary)] cursor-pointer flex-
1">{[Link]}</label>
</div>))}
</div>
)}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6 mb-4">
<div>
<h4 className="font-semibold text-[var(--text-primary)] mb-
2">Question Types</h4>
<div className="flex flex-col gap-2">
<div className="flex items-center">
<input type="checkbox" id="mock-type-case"
checked={[Link]} onChange={() => setMockExamQuestionTypes(p =>
({ ...p, case: ![Link] }))} className="w-4 h-4 rounded text-[var(--accent-primary)]
bg-[var(--bg-main)] border-[var(--border-color)] focus:ring-[var(--accent-
primary)]"/>
<label htmlFor="mock-type-case" className="ml-2 text-sm
text-[var(--text-secondary)] cursor-pointer">Case-based</label>
</div>
<div className="flex items-center">
<input type="checkbox" id="mock-type-noncase"
checked={[Link]} onChange={() => setMockExamQuestionTypes(p
=> ({ ...p, nonCase: ![Link] }))} className="w-4 h-4 rounded text-[var(--accent-
primary)] bg-[var(--bg-main)] border-[var(--border-color)] focus:ring-[var(--
accent-primary)]" />
<label htmlFor="mock-type-noncase" className="ml-2 text-
sm text-[var(--text-secondary)] cursor-pointer">Non-case</label>
</div>
<div className="flex items-center">
<input type="checkbox" id="mock-type-misc"
checked={[Link]} onChange={() => setMockExamQuestionTypes(p =>
({ ...p, misc: ![Link] }))} className="w-4 h-4 rounded text-[var(--accent-primary)]
bg-[var(--bg-main)] border-[var(--border-color)] focus:ring-[var(--accent-
primary)]" />
<label htmlFor="mock-type-misc" className="ml-2 text-sm
text-[var(--text-secondary)] cursor-pointer">Misc/General <span className="text-xs
opacity-70">(Tests related concepts not in text)</span></label>
</div>
<div className="flex items-center">
<input type="checkbox" id="mock-type-foundational"
checked={[Link]} onChange={() =>
setMockExamQuestionTypes(p => ({ ...p, foundational: ![Link] }))}
className="w-4 h-4 rounded text-[var(--accent-primary)] bg-[var(--bg-main)] border-
[var(--border-color)] focus:ring-[var(--accent-primary)]" />
<label htmlFor="mock-type-foundational" className="ml-2
text-sm text-[var(--text-secondary)] cursor-pointer">Foundational <span
className="text-xs opacity-70">(Anatomy, pathology, etc.)</span></label>
</div>
</div>
</div>
<div>
<label htmlFor="mock-num-questions" className="font-semibold
text-[var(--text-primary)] mb-2 block">Questions</label>
<input type="number" id="mock-num-questions"
value={mockExamNumQuestions} onChange={(e) => setMockExamNumQuestions([Link](1,
parseInt([Link], 10) || 1))} className="w-24 p-2 bg-[var(--bg-main)]
rounded-lg border border-[var(--border-color)] focus:ring-[var(--accent-primary)]
focus:border-[var(--accent-primary)]"/>
</div>
</div>
<div className="grid grid-cols-2 gap-2 mb-4">
<button onClick={handleReviewCorrectMock}
disabled={[Link] === 0} className="w-full py-2 px-2 text-xs
bg-green-600/20 text-green-300 font-semibold rounded-lg hover:bg-green-600/40
transition-colors disabled:opacity-50 disabled:cursor-not-allowed">
Review Correct ({[Link]})
</button>
<button onClick={handleReviewWrongMock}
disabled={[Link] === 0} className="w-full py-2 px-2 text-xs bg-
red-600/20 text-red-300 font-semibold rounded-lg hover:bg-red-600/40 transition-
colors disabled:opacity-50 disabled:cursor-not-allowed">
Review Wrong ({[Link]})
</button>
</div>
{mockExamError && <p className="mb-4 text-sm text-center text-[var(--
accent-danger)]">{mockExamError}</p>}
<button onClick={handleGenerateMockExam}
disabled={isGeneratingMockExam || [Link] === 0 || (!
[Link] && ![Link] && !
[Link] && ![Link])} className="w-
full py-3 px-4 bg-[var(--accent-primary)] text-white font-bold rounded-lg
hover:opacity-90 transition-opacity disabled:opacity-50 flex justify-center items-
center">
{isGeneratingMockExam ? <Loader /> : 'Generate Exam'}
</button>
</div>
<div className="bg-[var(--bg-alt)] p-6 rounded-lg border border-[var(--
border-color)]">
<div className="flex items-start gap-4 mb-4">
<div className="w-8 h-8 text-[var(--text-accent)] flex-shrink-
0"><ZoomInIcon /></div>
<div>
<h3 className="text-xl font-bold">Deep Dive Review</h3>
<p className="text-sm text-[var(--text-secondary)]">Review
exhaustive quizzes on all lesson content.</p>
</div>
</div>
<div className="w-full bg-[var(--bg-main)] rounded-full h-4 mb-2">
<div className="bg-[var(--accent-primary)] h-4 rounded-full"
style={{ width: `${deepDiveCoverage}%` }}></div>
</div>
<p className="text-center text-sm text-[var(--text-secondary)] mb-
4">{correctStudyMcqsCount} / {[Link]} questions correct
({[Link](0)}%)</p>
<button onClick={handleReviewDeepDiveMistakes}
disabled={[Link] === 0} className="w-full py-3 px-4 bg-[var(--
accent-primary)] text-white font-bold rounded-lg hover:opacity-90 transition-
opacity disabled:opacity-50">
Review {[Link]} Mistakes
</button>
</div>
<div className="bg-[var(--bg-alt)] p-6 rounded-lg border border-[var(--
border-color)]">
<div className="flex items-start gap-4 mb-4">
<div className="w-8 h-8 text-[var(--text-accent)] flex-shrink-
0"><HistoryIcon /></div>
<div>
<h3 className="text-xl font-bold">Review Mistakes</h3>
<p className="text-sm text-[var(--text-secondary)]">Focus on
questions you've answered incorrectly.</p>
</div>
</div>
<p className="text-center text-lg text-[var(--text-primary)] mb-4">
You have <span className="font-bold text-[var(--accent-
warn)]">{[Link]}</span> questions to review.
</p>
<button onClick={handleReviewAllMistakes}
disabled={[Link] === 0} className="w-full py-3 px-4 bg-[var(--accent-
primary)] text-white font-bold rounded-lg hover:opacity-90 transition-opacity
disabled:opacity-50 flex justify-center items-center">
Start Review
</button>
</div>
</div>

<h2 className="text-2xl font-bold my-4 text-[var(--text-primary)]">Post-


Graduate Training</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
{/* PG Prep Card */}
<div className="bg-[var(--bg-alt)] p-6 rounded-lg border border-[var(--
border-color)]">
<div className="flex items-start gap-4 mb-4">
<div className="w-8 h-8 text-[var(--text-accent)] flex-shrink-
0"><ClipboardCheckIcon /></div>
<div>
<h3 className="text-xl font-bold">Prepare for Post-Graduate
Questions</h3>
<p className="text-sm text-[var(--text-secondary)]">Generates
case-based questions with detailed, tutorial-style explanations to build clinical
reasoning.</p>
</div>
</div>
{/* Lesson Selector */}
<div className="mb-4">
<p className="font-semibold text-[var(--text-primary)] mb-
2">Select Lessons</p>
<div className="p-3 bg-[var(--bg-main)] rounded-lg border border-
[var(--border-color)] max-h-32 overflow-y-auto">
{[Link](lesson => (<div key={[Link]}
className="flex items-center p-1.5 rounded-md hover:bg-[var(--bg-alt)]">
<input type="checkbox" id={`pg-prep-lesson-${[Link]}`}
checked={[Link]([Link])} onChange={() => setPgPrepLessons(p => {const
n = new Set(p); [Link]([Link]) ? [Link]([Link]) : [Link]([Link]); return
n;})} className="w-4 h-4 rounded text-[var(--accent-primary)] bg-[var(--bg-main)]
border-[var(--border-color)] focus:ring-[var(--accent-primary)]" />
<label htmlFor={`pg-prep-lesson-${[Link]}`}
className="ml-3 text-sm text-[var(--text-secondary)] cursor-pointer flex-
1">{[Link]}</label>
</div>))}
</div>
</div>
{pgPrepError && <p className="mb-4 text-sm text-center text-[var(--
accent-danger)]">{pgPrepError}</p>}
<button onClick={handleGeneratePgPrep} disabled={isGeneratingPgPrep
|| [Link] === 0} className="w-full py-3 px-4 bg-[var(--accent-primary)]
text-white font-bold rounded-lg hover:opacity-90 transition-opacity
disabled:opacity-50 flex justify-center items-center">
{isGeneratingPgPrep ? <Loader /> : 'Generate Prep Questions'}
</button>
</div>

{/* PG Exam Card */}


<div className="bg-[var(--bg-alt)] p-6 rounded-lg border border-[var(--
border-color)]">
<div className="flex items-start gap-4 mb-4">
<div className="w-8 h-8 text-[var(--text-accent)] flex-shrink-
0"><GraduationCapIcon /></div>
<div>
<h3 className="text-xl font-bold">Post-Graduate Exam
Practice</h3>
<p className="text-sm text-[var(--text-secondary)]">Simulates
a board exam with difficult questions and concise explanations for rapid-fire
practice.</p>
</div>
</div>
{/* Lesson Selector */}
<div className="mb-4">
<p className="font-semibold text-[var(--text-primary)] mb-
2">Select Lessons</p>
<div className="p-3 bg-[var(--bg-main)] rounded-lg border border-
[var(--border-color)] max-h-32 overflow-y-auto">
{[Link](lesson => (<div key={[Link]}
className="flex items-center p-1.5 rounded-md hover:bg-[var(--bg-alt)]">
<input type="checkbox" id={`pg-exam-lesson-${[Link]}`}
checked={[Link]([Link])} onChange={() => setPgExamLessons(p => {const
n = new Set(p); [Link]([Link]) ? [Link]([Link]) : [Link]([Link]); return
n;})} className="w-4 h-4 rounded text-[var(--accent-primary)] bg-[var(--bg-main)]
border-[var(--border-color)] focus:ring-[var(--accent-primary)]" />
<label htmlFor={`pg-exam-lesson-${[Link]}`}
className="ml-3 text-sm text-[var(--text-secondary)] cursor-pointer flex-
1">{[Link]}</label>
</div>))}
</div>
</div>
{pgExamError && <p className="mb-4 text-sm text-center text-[var(--
accent-danger)]">{pgExamError}</p>}
<button onClick={handleGeneratePgExam} disabled={isGeneratingPgExam
|| [Link] === 0} className="w-full py-3 px-4 bg-[var(--accent-primary)]
text-white font-bold rounded-lg hover:opacity-90 transition-opacity
disabled:opacity-50 flex justify-center items-center">
{isGeneratingPgExam ? <Loader /> : 'Generate Exam Questions'}
</button>
</div>
</div>

<div className="mb-8">
<h2 className="text-2xl font-bold mb-4 text-[var(--text-primary)]">Exam
Practice Center</h2>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-1">
<ExamUploader onExamsProcessed={addUserExams} />
</div>
<div className="lg:col-span-2 bg-[var(--bg-alt)] p-4 rounded-lg border
border-[var(--border-color)]">
<h3 className="text-lg font-bold mb-3 text-[var(--text-
primary)]">My Custom Exams</h3>
{[Link] > 0 ? (
<div className="space-y-3 max-h-60 overflow-y-auto">
{[Link](exam => (
<div key={[Link]} className="bg-[var(--bg-main)] p-3
rounded-lg flex items-center justify-between">
<div className="flex items-center gap-3">
<FileTextIcon className="text-[var(--text-
accent)]"/>
<div>
<p className="font-semibold text-sm text-
[var(--text-primary)]">{[Link]}</p>
<p className="text-xs text-[var(--text-
secondary)]">{[Link]} questions</p>
</div>
</div>
<div className="flex items-center gap-2">
<button onClick={() => setActiveUserExam(exam)}
className="px-4 py-1.5 text-sm bg-[var(--accent-secondary)] text-white font-bold
rounded-md hover:opacity-90 transition-opacity">
Start
</button>
<button onClick={() => deleteUserExam([Link])}
className="p-2 text-[var(--accent-danger)] hover:bg-[var(--accent-danger)]/20
rounded-md transition-colors">
<Trash2Icon />
</button>
</div>
</div>
))}
</div>
) : (
<p className="text-center text-sm text-[var(--text-secondary)]
py-8">Upload an exam file to get started.</p>
)}
</div>
</div>
</div>

<div>
<h2 className="text-2xl font-bold mb-4 text-[var(--text-
primary)]">Lessons</h2>
{lessonsData && [Link] > 0 ? (
<div className="space-y-4">
{[Link](lesson => {
const isConquered = CONQUEST_STEP_IDS.every(stepId =>
[Link]?.includes(stepId));
return (
<div key={[Link]} className={`bg-[var(--bg-alt)] rounded-lg
border-l-4 transition-all duration-300`}
style={{ borderColor: statusConfig[[Link]].color }}
>
<button
onClick={() => onSelectLesson([Link])}
className="w-full p-4 flex justify-between items-center text-
left"
aria-label={`Study lesson: ${[Link]}`}
>
<div className="flex-1 pr-4">
<div className="flex items-center gap-3">
<h3 className="text-lg font-bold text-[var(--text-
primary)]">{[Link]}</h3>
{isConquered && (
<span className="px-2 py-0.5 text-xs font-bold
text-yellow-300 bg-yellow-500/20 border border-yellow-500/50 rounded-full">
CONQUERED
</span>
)}
</div>
<p className="text-sm text-[var(--text-secondary)]">Pages
{[Link]} - {[Link]}</p>
</div>
<div className="flex items-center gap-4 flex-shrink-0">
<div className="flex items-center gap-2">
<div className="w-5 h-5" style={{ color:
statusConfig[[Link]].color }}>{[Link](statusConfig[[Link]
s].icon)}</div>
<span className="text-sm font-semibold" style={{ color:
statusConfig[[Link]].color }}>{statusConfig[[Link]].label}</span>
</div>
</div>
</button>
</div>
)
})}
</div>
) : (
<p className="text-center text-[var(--text-secondary)] py-8">No lessons
have been categorized yet.</p>
)}
</div>
</div>
);
};

export default Dashboard;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React from 'react';
import { PracticeQuestion } from '../[Link]';
import { XIcon } from './icons/[Link]';

declare const marked: any;

interface EgyptianExamHistoryModalProps {
questions: PracticeQuestion[];
onClose: () => void;
}

const EgyptianExamHistoryModal: [Link]<EgyptianExamHistoryModalProps> =


({ questions, onClose }) => {
const renderExplanation = (explanation: string) => {
try {
return [Link](explanation);
} catch (e) {
return explanation;
}
};

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)]">
<h2 className="text-xl font-bold text-[var(--text-primary)]">Review
Egyptian University Exam Questions</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto">
{[Link] === 0 ? (
<p className="text-center text-[var(--text-secondary)]">No Egyptian
exam questions have been solved for this page yet.</p>
) : (
<div className="space-y-6">
{[Link]((q, qIndex) => (
<div key={qIndex} className="bg-[var(--bg-main)] p-4 rounded-lg">
<p className="text-lg text-[var(--text-primary)] mb-2">{qIndex +
1}. {[Link]}</p>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-
2">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-3">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-
[var(--accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, oIndex) => {
const isCorrect = option === [Link];
return (
<div
key={oIndex}
className={`w-full p-3 text-left rounded-lg border $
{isCorrect ? 'bg-green-500/30 border-green-500' : 'bg-[var(--bg-alt)] border-
[var(--border-color)] opacity-70'}`}
>
{option}
</div>
);
})}
</div>
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4
border-green-500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose
prose-invert max-w-none" dangerouslySetInnerHTML={{ __html:
renderExplanation([Link]) }}/>
</div>
</div>
))}
</div>
)}
</main>
<footer className="p-4 border-t border-[var(--border-color)]">
<button
onClick={onClose}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white font-
bold rounded-lg hover:opacity-90 transition-opacity"
>
Close
</button>
</footer>
</div>
</div>
);
};

export default EgyptianExamHistoryModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState, useEffect, useMemo } from 'react';
import { QuizSession, PracticeQuestion } from '../[Link]';
import Loader from './[Link]';
import { XIcon } from './icons/[Link]';
import useQuizSession from '../hooks/[Link]';

declare const marked: any;

interface EgyptianExamModalProps {
fileName: string;
lessonId: string;
onClose: () => void;
onComplete: (answeredQuestions: PracticeQuestion[]) => void;
}

const EgyptianExamModal: [Link]<EgyptianExamModalProps> = ({ fileName, lessonId,


onClose, onComplete }) => {
const { getQuizSession, saveQuizSession } = useQuizSession(fileName);
const [session, setSession] = useState<QuizSession | null>(null);

useEffect(() => {
const loadedSession = getQuizSession(lessonId, 'egyptianExams');
if (loadedSession) {
setSession(loadedSession);
}
}, [lessonId, fileName, getQuizSession]);

const currentQuestion = session?.questions[[Link] ?? 0];


const explanationHtml = useMemo(() => {
if (!currentQuestion?.explanation) return '';
try {
return [Link]([Link]);
} catch (e) {
return [Link];
}
}, [currentQuestion]);

const handleAnswerSelect = (option: string) => {


if (!session || [Link][[Link]] !== undefined) return;

const newSession: QuizSession = {


...session,
answers: {
...[Link],
[[Link]]: option,
},
};
setSession(newSession);
saveQuizSession(newSession, 'egyptianExams');
};

const handleNextQuestion = () => {


if (!session) return;
if ([Link] < [Link] - 1) {
const newSession: QuizSession = {
...session,
currentIndex: [Link] + 1,
};
setSession(newSession);
saveQuizSession(newSession, 'egyptianExams');
} else {
const answeredQuestions = [Link]((q, index) => ({
...q,
userAnswer: [Link][index],
isCorrect: [Link][index] === [Link],
}));
onComplete(answeredQuestions);
}
};

const getButtonClass = (option: string) => {


if (!session) return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';

const currentQuestion = [Link][[Link]];


const selectedAnswer = [Link][[Link]];
const isAnswered = selectedAnswer !== undefined;

if (!isAnswered) {
return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';
}
if (option === [Link]) {
return 'bg-green-500/50 border-green-500';
}
if (option === selectedAnswer) {
return 'bg-red-500/50 border-red-500';
}
return 'bg-[var(--bg-alt)] opacity-60';
};

const renderContent = () => {


if (!session) {
return (
<div className="flex justify-center items-center h-48">
<Loader />
</div>
);
}

const currentQuestion = [Link][[Link]];


const isAnswered = [Link][[Link]] !== undefined;

if (!currentQuestion) return <div className="text-center text-[var(--text-


secondary)]">Question not found.</div>;
return (
<div>
<div className="flex items-start gap-3 mb-2">
<p className="text-lg text-[var(--text-primary)] font-bold pt-
1.5">{[Link] + 1}.</p>
<p className="text-lg text-[var(--text-
primary)]">{[Link]}</p>
</div>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-2 ml-8">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-4 ml-8">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-[var(--
accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, index) => (
<button
key={index}
onClick={() => handleAnswerSelect(option)}
disabled={isAnswered}
className={`w-full p-3 text-left rounded-lg border border-[var(--
border-color)] transition-all ${getButtonClass(option)}`}
>
{option}
</button>
))}
</div>
{isAnswered && (
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4 border-green-
500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose prose-invert
max-w-none" dangerouslySetInnerHTML={{ __html: explanationHtml }}/>
{!([Link] ||
[]).includes([Link]) && (
<div className="mt-2 pt-2 border-t border-[var(--border-color)]">
<h5 className="font-bold text-yellow-400">Correct Answer</h5>
<p className="text-sm text-[var(--text-
secondary)]">{[Link]}</p>
</div>
)}
</div>
)}
</div>
);
};

const isAnswered = session ? [Link][[Link]] !==


undefined : false;

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)] flex-shrink-0">
<h2 className="text-xl font-bold text-[var(--text-primary)]">Egyptian
University Exams</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto flex-grow">
{renderContent()}
</main>
<footer className="p-4 border-t border-[var(--border-color)] flex-shrink-
0">
{isAnswered && session && (
<button
onClick={handleNextQuestion}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white
font-bold rounded-lg hover:opacity-90 transition-opacity"
>
{[Link] < [Link] - 1 ? 'Next
Question' : 'Finish'}
</button>
)}
</footer>
</div>
</div>
);
};

export default EgyptianExamModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState } from 'react';
import * as pdfjsLib from 'pdfjs-dist';
import { generateQuestionsFromExamFile, extractTextFromImage } from
'../services/[Link]';
import { UserExam } from '../[Link]';
import Loader from './[Link]';
import { UploadCloudIcon } from './icons/[Link]';

declare const mammoth: any;

interface ExamUploaderProps {
onExamsProcessed: (exams: UserExam[]) => void;
}

[Link] = '[Link]
dist@4.5.136/build/[Link]';

const fileToBase64 = (file: File): Promise<{ base64: string; mimeType: string }> =>
{
return new Promise((resolve, reject) => {
const reader = new FileReader();
[Link] = () => {
const result = [Link] as string;
resolve({ base64: [Link](',')[1], mimeType: [Link] });
};
[Link] = error => reject(error);
[Link](file);
});
};

const ExamUploader: [Link]<ExamUploaderProps> = ({ onExamsProcessed }) => {


const [isProcessing, setIsProcessing] = useState(false);
const [error, setError] = useState<string | null>(null);
const [statusMessage, setStatusMessage] = useState('');

const processPdf = async (file: File): Promise<string> => {


const arrayBuffer = await [Link]();
const pdf = await [Link]({ data: arrayBuffer }).promise;
const numPages = [Link];
let fullText = '';
for (let i = 1; i <= numPages; i++) {
const page = await [Link](i);
const textContent = await [Link]();
fullText += [Link]((item: any) => [Link]).join(' ');
fullText += '\n\n'; // Page break
}
return fullText;
};

const processDocx = async (file: File): Promise<string> => {


const arrayBuffer = await [Link]();
const { value: rawText } = await [Link]({ arrayBuffer });
return rawText || '';
};

const handleFileChange = async (event: [Link]<HTMLInputElement>) => {


const files = [Link];
if (!files || [Link] === 0) return;

setError(null);
setIsProcessing(true);
setStatusMessage('Starting exam processing...');

const processedExams: UserExam[] = [];

for (let i = 0; i < [Link]; i++) {


const file = files[i];
setStatusMessage(`Processing file ${i + 1} of ${[Link]}: $
{[Link]}`);
try {
let fileText = '';
const fileName = [Link]();
const fileType = [Link];

if ([Link]('.pdf')) {
fileText = await processPdf(file);
} else if ([Link]('.docx')) {
fileText = await processDocx(file);
} else if ([Link]('image/')) {
setStatusMessage(`Extracting text from image ${[Link]}... (this may
take a moment)`);
const { base64, mimeType } = await fileToBase64(file);
fileText = await extractTextFromImage(base64, mimeType);
} else {
[Link](`Unsupported file type: ${[Link]}. Skipping.`);
continue;
}

if (![Link]()) {
[Link](`No text extracted from ${[Link]}. Skipping.`);
continue;
}

setStatusMessage(`Generating questions for ${[Link]}...`);


const questions = await generateQuestionsFromExamFile(fileText);

if ([Link] > 0) {
[Link]({
id: `exam-${[Link]()}-${i}`,
name: [Link],
questions: questions,
});
}
} catch (err) {
const errorMessage = err instanceof Error ? [Link] : String(err);
[Link](`Failed to process ${[Link]}:`, err);
setError(prev => `${prev ? prev + '\n' : ''}Error on ${[Link]}: $
{errorMessage}`);
}
}

if ([Link] > 0) {
onExamsProcessed(processedExams);
}

setIsProcessing(false);
setStatusMessage('');
if ([Link]) {
[Link] = '';
}
};

return (
<div className="bg-[var(--bg-main)] p-4 rounded-lg border border-[var(--border-
color)] text-center">
{isProcessing ? (
<>
<Loader />
<p className="mt-2 text-sm text-[var(--text-
secondary)]">{statusMessage}</p>
</>
) : (
<>
<div className="mx-auto h-12 w-12 text-[var(--accent-primary)]">
<UploadCloudIcon />
</div>
<h4 className="mt-2 text-md font-semibold text-[var(--text-
primary)]">Upload Exam Materials</h4>
<p className="mt-1 text-xs text-[var(--text-secondary)]">Select
individual files or a whole folder.</p>
<div className="mt-4 flex flex-col sm:flex-row gap-2 justify-center">
<label htmlFor="exam-file-upload" className="cursor-pointer w-full
flex-1 text-center px-4 py-2 text-sm bg-[var(--accent-primary)] text-white font-
bold rounded-lg hover:opacity-90 transition-opacity flex items-center justify-
center">
Select Files
<input
id="exam-file-upload"
name="exam-file-upload"
type="file"
className="sr-only"
multiple
accept=".pdf,.docx,application/pdf,application/[Link]-
[Link],image/png,image/jpeg"
onChange={handleFileChange}
/>
</label>
<label htmlFor="exam-folder-upload" className="cursor-pointer w-full
flex-1 text-center px-4 py-2 text-sm bg-[var(--bg-main)] border border-[var(--
border-color)] text-[var(--text-secondary)] font-bold rounded-lg hover:text-[var(--
text-primary)] hover:border-[var(--text-accent)] transition-colors flex items-
center justify-center">
Select Folder
<input
id="exam-folder-upload"
name="exam-folder-upload"
type="file"
className="sr-only"
// FIX: The `webkitdirectory` attribute is non-standard and not in
React's TypeScript types.
// Using an object spread is a workaround to bypass the type check.
{...{ webkitdirectory: "" }}
onChange={handleFileChange}
/>
</label>
</div>
</>
)}
{error && <p className="mt-2 text-xs text-left text-[var(--accent-danger)]
whitespace-pre-wrap">{error}</p>}
</div>
);
};

export default ExamUploader;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React from 'react';
import { PracticeQuestion } from '../[Link]';
import { XIcon, SkullIcon } from './icons/[Link]';

declare const marked: any;

interface ImpossibleHistoryModalProps {
questions: PracticeQuestion[];
onClose: () => void;
}
const ImpossibleHistoryModal: [Link]<ImpossibleHistoryModalProps> = ({ questions,
onClose }) => {
const renderExplanation = (explanation: string) => {
try {
return [Link](explanation);
} catch (e) {
return explanation;
}
};

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)]">
<div className="flex items-center gap-2">
<span className="text-red-500"><SkullIcon /></span>
<h2 className="text-xl font-bold text-[var(--text-primary)]">Review
Impossible Questions</h2>
</div>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto">
{[Link] === 0 ? (
<p className="text-center text-[var(--text-secondary)]">No impossible
questions have been solved for this page yet.</p>
) : (
<div className="space-y-6">
{[Link]((q, qIndex) => (
<div key={qIndex} className="bg-[var(--bg-main)] p-4 rounded-lg">
<p className="text-lg text-[var(--text-primary)] mb-2">{qIndex +
1}. {[Link]}</p>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-
2">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-3">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-
[var(--accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, oIndex) => {
const isCorrect = option === [Link];
return (
<div
key={oIndex}
className={`w-full p-3 text-left rounded-lg border $
{isCorrect ? 'bg-green-500/30 border-green-500' : 'bg-[var(--bg-alt)] border-
[var(--border-color)] opacity-70'}`}
>
{option}
</div>
);
})}
</div>
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4
border-green-500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose
prose-invert max-w-none" dangerouslySetInnerHTML={{ __html:
renderExplanation([Link]) }}/>
</div>
</div>
))}
</div>
)}
</main>
<footer className="p-4 border-t border-[var(--border-color)]">
<button
onClick={onClose}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white font-
bold rounded-lg hover:opacity-90 transition-opacity"
>
Close
</button>
</footer>
</div>
</div>
);
};

export default ImpossibleHistoryModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState, useEffect, useMemo } from 'react';
import { QuizSession, PracticeQuestion } from '../[Link]';
import Loader from './[Link]';
import { XIcon, SkullIcon } from './icons/[Link]';
import useQuizSession from '../hooks/[Link]';

declare const marked: any;

interface ImpossibleModalProps {
fileName: string;
lessonId: string;
onClose: () => void;
onComplete: (answeredQuestions: PracticeQuestion[]) => void;
}

const ImpossibleModal: [Link]<ImpossibleModalProps> = ({ fileName, lessonId,


onClose, onComplete }) => {
const { getQuizSession, saveQuizSession } = useQuizSession(fileName);
const [session, setSession] = useState<QuizSession | null>(null);

useEffect(() => {
const loadedSession = getQuizSession(lessonId, 'impossible');
if (loadedSession) {
setSession(loadedSession);
}
}, [lessonId, fileName, getQuizSession]);

const currentQuestion = session?.questions[[Link] ?? 0];


const explanationHtml = useMemo(() => {
if (!currentQuestion?.explanation) return '';
try {
return [Link]([Link]);
} catch (e) {
return [Link];
}
}, [currentQuestion]);

const handleAnswerSelect = (option: string) => {


if (!session || [Link][[Link]] !== undefined) return;

const newSession: QuizSession = {


...session,
answers: {
...[Link],
[[Link]]: option,
},
};
setSession(newSession);
saveQuizSession(newSession, 'impossible');
};

const handleNextQuestion = () => {


if (!session) return;
if ([Link] < [Link] - 1) {
const newSession: QuizSession = {
...session,
currentIndex: [Link] + 1,
};
setSession(newSession);
saveQuizSession(newSession, 'impossible');
} else {
const answeredQuestions = [Link]((q, index) => ({
...q,
userAnswer: [Link][index],
isCorrect: [Link][index] === [Link],
}));
onComplete(answeredQuestions);
}
};

const getButtonClass = (option: string) => {


if (!session) return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';

const currentQuestion = [Link][[Link]];


const selectedAnswer = [Link][[Link]];
const isAnswered = selectedAnswer !== undefined;

if (!isAnswered) {
return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';
}
if (option === [Link]) {
return 'bg-green-500/50 border-green-500';
}
if (option === selectedAnswer) {
return 'bg-red-500/50 border-red-500';
}
return 'bg-[var(--bg-alt)] opacity-60';
};

const renderContent = () => {


if (!session) {
return (
<div className="flex justify-center items-center h-48">
<Loader />
</div>
);
}

const currentQuestion = [Link][[Link]];


const isAnswered = [Link][[Link]] !== undefined;

if (!currentQuestion) return <div className="text-center text-[var(--text-


secondary)]">Question not found.</div>;

return (
<div>
<div className="flex items-start gap-3 mb-2">
<p className="text-lg text-[var(--text-primary)] font-bold pt-
1.5">{[Link] + 1}.</p>
<p className="text-lg text-[var(--text-
primary)]">{[Link]}</p>
</div>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-2 ml-8">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-4 ml-8">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-[var(--
accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, index) => (
<button
key={index}
onClick={() => handleAnswerSelect(option)}
disabled={isAnswered}
className={`w-full p-3 text-left rounded-lg border border-[var(--
border-color)] transition-all ${getButtonClass(option)}`}
>
{option}
</button>
))}
</div>
{isAnswered && (
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4 border-green-
500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose prose-invert
max-w-none" dangerouslySetInnerHTML={{ __html: explanationHtml }}/>
{!([Link] ||
[]).includes([Link]) && (
<div className="mt-2 pt-2 border-t border-[var(--border-color)]">
<h5 className="font-bold text-yellow-400">Correct Answer</h5>
<p className="text-sm text-[var(--text-
secondary)]">{[Link]}</p>
</div>
)}
</div>
)}
</div>
);
};

const isAnswered = session ? [Link][[Link]] !==


undefined : false;

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)] flex-shrink-0">
<div className="flex items-center gap-2">
<span className="text-red-500"><SkullIcon /></span>
<h2 className="text-xl font-bold text-[var(--text-primary)]">Impossible
Questions</h2>
</div>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto flex-grow">
{renderContent()}
</main>
<footer className="p-4 border-t border-[var(--border-color)] flex-shrink-
0">
{isAnswered && session && (
<button
onClick={handleNextQuestion}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white
font-bold rounded-lg hover:opacity-90 transition-opacity"
>
{[Link] < [Link] - 1 ? 'Next
Question' : 'Finish'}
</button>
)}
</footer>
</div>
</div>
);
};
export default ImpossibleModal;
--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React from 'react';

const Loader: [Link] = () => {


return (
<div className="flex justify-center items-center">
<div
className="w-8 h-8 border-4 border-t-transparent rounded-full animate-spin"
style={{ borderColor: 'var(--border-color)', borderTopColor: 'var(--accent-
primary)' }}
></div>
</div>
);
};

export default Loader;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React from 'react';
import { PracticeQuestion } from '../[Link]';
import { XIcon } from './icons/[Link]';

declare const marked: any;

interface McqHistoryModalProps {
questions: PracticeQuestion[];
onClose: () => void;
}

const McqHistoryModal: [Link]<McqHistoryModalProps> = ({ questions, onClose }) =>


{
const renderExplanation = (explanation: string) => {
try {
return [Link](explanation);
} catch (e) {
return explanation;
}
};

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)]">
<h2 className="text-xl font-bold text-[var(--text-primary)]">Review Study
Questions</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto">
{[Link] === 0 ? (
<p className="text-center text-[var(--text-secondary)]">No study
questions have been saved for this page yet.</p>
) : (
<div className="space-y-6">
{[Link]((q, qIndex) => (
<div key={qIndex} className="bg-[var(--bg-main)] p-4 rounded-lg">
<p className="text-lg text-[var(--text-primary)] mb-2">{qIndex +
1}. {[Link]}</p>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-
2">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-3">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-
[var(--accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, oIndex) => {
const isCorrect = option === [Link];
return (
<div
key={oIndex}
className={`w-full p-3 text-left rounded-lg border $
{isCorrect ? 'bg-green-500/30 border-green-500' : 'bg-[var(--bg-alt)] border-
[var(--border-color)] opacity-70'}`}
>
{option}
</div>
);
})}
</div>
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4
border-green-500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose
prose-invert max-w-none" dangerouslySetInnerHTML={{ __html:
renderExplanation([Link]) }}/>
</div>
</div>
))}
</div>
)}
</main>
<footer className="p-4 border-t border-[var(--border-color)]">
<button
onClick={onClose}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white font-
bold rounded-lg hover:opacity-90 transition-opacity"
>
Close
</button>
</footer>
</div>
</div>
);
};

export default McqHistoryModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState, useMemo } from 'react';
import { PracticeQuestion } from '../[Link]';
import { XIcon } from './icons/[Link]';

declare const marked: any;

interface MockExamModalProps {
questions: PracticeQuestion[];
onClose: () => void;
onComplete: (answeredQuestions: PracticeQuestion[]) => void;
title?: string;
}

const MockExamModal: [Link]<MockExamModalProps> = ({ questions, onClose,


onComplete, title }) => {
const [currentIndex, setCurrentIndex] = useState(0);
const [answers, setAnswers] = useState<{ [key: number]: string }>({});
const [isFinished, setIsFinished] = useState(false);

const currentQuestion = questions[currentIndex];


const explanationHtml = useMemo(() => {
if (!currentQuestion?.explanation) return '';
try {
return [Link]([Link]);
} catch (e) {
return [Link];
}
}, [currentQuestion]);

const handleAnswerSelect = (option: string) => {


if (answers[currentIndex] !== undefined) return;
setAnswers(prev => ({ ...prev, [currentIndex]: option }));
};

const handleFinishAndClose = () => {


const answeredQuestions = [Link]((q, index) => ({
...q,
userAnswer: answers[index],
isCorrect: answers[index] === [Link],
}));
onComplete(answeredQuestions);
};

const handleNextQuestion = () => {


if (currentIndex < [Link] - 1) {
setCurrentIndex(prev => prev + 1);
} else {
setIsFinished(true);
}
};
const getButtonClass = (option: string, question: PracticeQuestion,
selectedAnswer?: string) => {
const isAnswered = selectedAnswer !== undefined;

if (!isAnswered) {
return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';
}
if (option === [Link]) {
return 'bg-green-500/50 border-green-500';
}
if (option === selectedAnswer) {
return 'bg-red-500/50 border-red-500';
}
return 'bg-[var(--bg-alt)] opacity-60';
};

if ([Link] === 0) {
return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center
p-4 z-50">
<div className="w-full max-w-lg bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)]">
<header className="p-4 flex justify-between items-center border-b
border-[var(--border-color)]">
<h2 className="text-xl font-bold text-[var(--text-primary)]">Exam
Notice</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]"><XIcon /></button>
</header>
<main className="p-6 text-center">
<p className="text-[var(--text-secondary)]">No questions were
generated. Please check your selections and try again.</p>
</main>
<footer className="p-4 border-t border-[var(--border-color)]">
<button onClick={onClose} className="w-full py-2 px-4 bg-[var(--
accent-primary)] text-white font-bold rounded-lg hover:opacity-90 transition-
opacity">Close</button>
</footer>
</div>
</div>
);
}

const renderQuestionView = () => {


const isAnswered = answers[currentIndex] !== undefined;

return (
<>
<div>
<div className="flex items-start gap-3 mb-2">
<p className="text-lg text-[var(--text-primary)] font-bold pt-
1.5">{currentIndex + 1}.</p>
<p className="text-lg text-[var(--text-
primary)]">{[Link]}</p>
</div>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-2
ml-8">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-4 ml-8">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-
[var(--accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, index) => (
<button
key={index}
onClick={() => handleAnswerSelect(option)}
disabled={isAnswered}
className={`w-full p-3 text-left rounded-lg border border-
[var(--border-color)] transition-all ${getButtonClass(option, currentQuestion,
answers[currentIndex])}`}
>
{option}
</button>
))}
</div>
{isAnswered && (
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4 border-
green-500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose
prose-invert max-w-none" dangerouslySetInnerHTML={{ __html: explanationHtml }}/>
</div>
)}
</div>
<footer className="p-4 border-t border-[var(--border-color)] flex-
shrink-0 mt-6 -mx-6 -mb-6">
{isAnswered && (
<button onClick={handleNextQuestion} className="w-full py-2 px-4
bg-[var(--accent-primary)] text-white font-bold rounded-lg hover:opacity-90
transition-opacity">
{currentIndex < [Link] - 1 ? 'Next Question' : 'Finish
Exam'}
</button>
)}
</footer>
</>
);
};

const renderResultsView = () => {


const correctAnswersCount = [Link](answers).reduce((count, key) => {
const index = parseInt(key, 10);
if (answers[index] === questions[index].correctAnswer) return count + 1;
return count;
}, 0);

const renderExplanation = (explanation: string) => {


try {
return [Link](explanation);
} catch(e) {
return explanation;
}
};

return (
<>
<div className="text-center">
<h3 className="text-2xl font-bold text-[var(--text-primary)]">Exam
Complete!</h3>
<p className="text-lg text-[var(--text-accent)] mt-2">Your Score:
{correctAnswersCount} / {[Link]}</p>
</div>
<div className="mt-6 space-y-6">
<h4 className="text-xl font-bold text-[var(--text-primary)]">Review
Your Answers</h4>
{[Link]((q, qIndex) => (
<div key={qIndex} className="bg-[var(--bg-main)] p-4 rounded-
lg">
<p className="text-lg text-[var(--text-primary)] mb-
2">{qIndex + 1}. {[Link]}</p>
{[Link] && <p className="text-xs text-[var(--text-
secondary)] italic mb-2">({[Link]})</p>}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-3">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs
bg-[var(--accent-primary)]/20 text-[var(--accent-primary)]
rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, oIndex) => {
const isCorrect = option === [Link];
const isUserChoice = option === answers[qIndex];
let borderClass = 'border-[var(--border-color)]';
if (isCorrect) borderClass = 'border-green-500';
else if (isUserChoice) borderClass = 'border-red-
500';

return (
<div key={oIndex} className={`w-full p-3 text-
left rounded-lg border-2 ${borderClass} ${isCorrect ? 'bg-green-500/20' :
isUserChoice ? 'bg-red-500/20' : 'bg-[var(--bg-alt)] opacity-80'}`}>
{option}
{isCorrect && <span className="text-xs
font-bold ml-2 text-green-400">(Correct Answer)</span>}
{!isCorrect && isUserChoice && <span
className="text-xs font-bold ml-2 text-red-400">(Your Answer)</span>}
</div>
);
})}
</div>
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4
border-green-500">
<h4 className="font-bold
text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)]
prose prose-invert max-w-none" dangerouslySetInnerHTML={{ __html:
renderExplanation([Link]) }}/>
</div>
</div>
))}
</div>
<footer className="p-4 border-t border-[var(--border-color)] flex-
shrink-0 mt-6 -mx-6 -mb-6">
<button onClick={handleFinishAndClose} className="w-full py-2 px-4
bg-[var(--accent-primary)] text-white font-bold rounded-lg hover:opacity-90
transition-opacity">
Close
</button>
</footer>
</>
);
};

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-3xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)] flex-shrink-0">
<h2 className="text-xl font-bold text-[var(--text-primary)]">{title ||
'Mock Exam'}</h2>
{!isFinished && <div className="text-sm text-[var(--text-
secondary)]">Question {currentIndex + 1} of {[Link]}</div>}
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]"><XIcon /></button>
</header>
<main className="p-6 overflow-y-auto flex-grow">
{isFinished ? renderResultsView() : renderQuestionView()}
</main>
</div>
</div>
);
};

export default MockExamModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState } from 'react';
import * as pdfjsLib from 'pdfjs-dist';
import Loader from './[Link]';
import { UploadCloudIcon, UploadIcon } from './icons/[Link]';
import { Document } from '../[Link]';
import { extractTextFromImage } from '../services/[Link]';

// For [Link] loaded from a script tag


declare const mammoth: any;

interface PdfProcessorProps {
onProcessingStart: () => void;
onFilesProcessed: (documents: Document[]) => void;
}
// Set the worker source for the ES module version of [Link]
[Link] = '[Link]
dist@4.5.136/build/[Link]';

const fileToBase64 = (file: File): Promise<{ base64: string; mimeType: string }> =>
{
return new Promise((resolve, reject) => {
const reader = new FileReader();
[Link] = () => {
const result = [Link] as string;
resolve({ base64: [Link](',')[1], mimeType: [Link] });
};
[Link] = error => reject(error);
[Link](file);
});
};

const PdfProcessor: [Link]<PdfProcessorProps> = ({ onProcessingStart,


onFilesProcessed }) => {
const [error, setError] = useState<string | null>(null);
const [isProcessing, setIsProcessing] = useState(false);
const [statusMessage, setStatusMessage] = useState('');

const processPdf = async (file: File) => {


const arrayBuffer = await [Link]();
const pdf = await [Link]({ data: arrayBuffer }).promise;
const numPages = [Link];
const pagesText: string[] = [];

for (let i = 1; i <= numPages; i++) {


const page = await [Link](i);
const textContent = await [Link]();
const pageText = [Link]((item: any) => [Link]).join(' ');
[Link](pageText);
}
return pagesText;
};

const processDocx = async (file: File): Promise<string[]> => {


const arrayBuffer = await [Link]();
const { value: rawText } = await [Link]({ arrayBuffer });

if (!rawText || [Link]().length === 0) {


return [];
}

const pages: string[] = [];


const TARGET_PAGE_SIZE = 1800;
const SEARCH_RANGE = 500;

let textCursor = 0;

while (textCursor < [Link]) {


if (([Link] - textCursor) < (TARGET_PAGE_SIZE * 1.5)) {
[Link]([Link](textCursor));
break;
}
const searchEnd = textCursor + TARGET_PAGE_SIZE;
const searchStart = [Link](textCursor, searchEnd - SEARCH_RANGE);

let splitIndex = -1;

splitIndex = [Link]('\n\n', searchEnd);


if (splitIndex < searchStart) {
splitIndex = [Link]('\n', searchEnd);
}
if (splitIndex < searchStart) {
splitIndex = [Link]('.', searchEnd);
}
if (splitIndex < searchStart) {
splitIndex = [Link](' ', searchEnd);
}

let pageBreakIndex;
if (splitIndex > textCursor) {
pageBreakIndex = splitIndex + 1;
} else {
pageBreakIndex = textCursor + TARGET_PAGE_SIZE;
}

const pageText = [Link](textCursor, pageBreakIndex);


[Link](pageText);
textCursor = pageBreakIndex;
}

if ([Link] === 0 && [Link]().length > 0) {


[Link]([Link]());
}

return [Link](p => [Link]().length > 0);


};

const handleFileChange = async (event: [Link]<HTMLInputElement>) => {


const files = [Link];
if (!files || [Link] === 0) return;

setError(null);
setIsProcessing(true);
onProcessingStart();

const processedDocuments: Document[] = [];

for (let i = 0; i < [Link]; i++) {


const file = files[i];
setStatusMessage(`Processing file ${i + 1} of ${[Link]}: $
{[Link]}`);

try {
const fileType = [Link];
const fileName = [Link]();
let pages: string[] = [];

if (fileType === 'application/pdf' || [Link]('.pdf')) {


pages = await processPdf(file);
} else if (
[Link]('.docx') ||
fileType === 'application/[Link]-
[Link]'
) {
pages = await processDocx(file);
} else if ([Link]('image/')) {
setStatusMessage(`Extracting text from image: ${[Link]}...`);
const { base64, mimeType } = await fileToBase64(file);
const text = await extractTextFromImage(base64, mimeType);
pages = [text]; // Treat image text as a single page
} else {
[Link](`Unsupported file type: ${[Link]}. Skipping.`);
continue;
}

if ([Link] > 0 && [Link](p => [Link]().length > 0)) {


[Link]({ fileName: [Link], pages });
}

} catch (err) {
[Link](`Error processing ${[Link]}:`, err);
let errorMessage = err instanceof Error ? [Link] : String(err);
setError(prev => `${prev ? prev + '\n' : ''}Failed to process $
{[Link]}. ${errorMessage}`);
}
}

if ([Link] > 0) {
onFilesProcessed(processedDocuments);
} else if (!error) {
setError("No content could be extracted from the selected files. Please
try different files.");
}

setIsProcessing(false);
setStatusMessage('');
};

const handleImportData = (event: [Link]<HTMLInputElement>) => {


const file = [Link]?.[0];
if (!file) return;

const reader = new FileReader();


[Link] = (e) => {
try {
const text = [Link]?.result;
if (typeof text !== 'string') {
throw new Error("Failed to read file.");
}
const data = [Link](text);

[Link]();

for (const key in data) {


if ([Link](data, key)) {
const value = data[key];
[Link](key, typeof value === 'string' ? value :
[Link](value));
}
}
[Link]();
} catch (err) {
[Link]("Error importing data:", err);
const errorMessage = err instanceof Error ? [Link] : String(err);
setError(`Failed to import data. Please check the file format. Error: $
{errorMessage}`);
}
};
[Link](file);
if ([Link]) {
[Link] = '';
}
};

return (
<div className="flex items-center justify-center h-full w-full p-4">
<div className="w-full max-w-lg text-center p-8 rounded-2xl border border-
[var(--border-color)] bg-[var(--bg-alt)] shadow-2xl shadow-[var(--shadow-color)]">
{isProcessing ? (
<>
<h2 className="text-2xl font-bold text-[var(--text-primary)] mb-
4">Processing Your Documents...</h2>
<p className="text-[var(--text-secondary)] mb-6">{statusMessage ||
"Please wait."}</p>
<Loader />
</>
) : (
<>
<div className="mx-auto h-20 w-20 text-[var(--accent-primary)]">
<UploadCloudIcon />
</div>
<h2 className="mt-6 text-2xl font-bold tracking-tight text-[var(--text-
primary)]">Upload your study materials</h2>
<p className="mt-2 text-base text-[var(--text-secondary)]">Drag and
drop your PDF, .docx, or image files here, or click to browse.</p>
{error && <p className="mt-4 text-sm text-[var(--accent-danger)]
whitespace-pre-wrap">{error}</p>}
<div className="mt-6 flex flex-col items-center gap-4">
<label htmlFor="file-upload" className="cursor-pointer relative
inline-flex items-center justify-center px-8 py-3 text-lg font-bold text-white bg-
[var(--accent-primary)] rounded-full transition-transform duration-300 hover:scale-
105 shadow-lg w-full max-w-xs">
<span>Select Documents</span>
<input id="file-upload" name="file-upload" type="file"
className="sr-only"
accept=".pdf,.docx,application/pdf,application/[Link]-
[Link],image/png,image/jpeg"
onChange={handleFileChange} multiple />
</label>
<div className="text-sm text-[var(--text-secondary)]">or</div>
<label htmlFor="import-data" className="cursor-pointer inline-flex
items-center justify-center px-6 py-2 text-sm font-bold text-[var(--text-
secondary)] bg-[var(--bg-main)] border border-[var(--border-color)] rounded-full
transition-colors hover:text-[var(--text-primary)] hover:border-[var(--text-
accent)] w-full max-w-xs">
<UploadIcon />
<span className="ml-2">Import Progress</span>
<input id="import-data" name="import-data" type="file"
className="sr-only" accept=".json" onChange={handleImportData} />
</label>
</div>
</>
)}
</div>
</div>
);
};

export default PdfProcessor;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState, useEffect, useMemo } from 'react';
import { QuizSession, PracticeQuestion } from '../[Link]';
import Loader from './[Link]';
import { XIcon } from './icons/[Link]';
import useQuizSession from '../hooks/[Link]';

declare const marked: any;

interface PracticeModalProps {
fileName: string;
lessonId: string;
onClose: () => void;
onComplete: (answeredQuestions: PracticeQuestion[]) => void;
}

const PracticeModal: [Link]<PracticeModalProps> = ({ fileName, lessonId, onClose,


onComplete }) => {
const { getQuizSession, saveQuizSession } = useQuizSession(fileName);
const [session, setSession] = useState<QuizSession | null>(null);

useEffect(() => {
const loadedSession = getQuizSession(lessonId, 'october6Exam');
if (loadedSession) {
setSession(loadedSession);
}
}, [lessonId, fileName, getQuizSession]);

const currentQuestion = session?.questions[[Link] ?? 0];


const explanationHtml = useMemo(() => {
if (!currentQuestion?.explanation) return '';
try {
return [Link]([Link]);
} catch (e) {
return [Link];
}
}, [currentQuestion]);

const handleAnswerSelect = (option: string) => {


if (!session || [Link][[Link]] !== undefined) return;

const newSession: QuizSession = {


...session,
answers: {
...[Link],
[[Link]]: option,
},
};
setSession(newSession);
saveQuizSession(newSession, 'october6Exam');
};

const handleNextQuestion = () => {


if (!session) return;
if ([Link] < [Link] - 1) {
const newSession: QuizSession = {
...session,
currentIndex: [Link] + 1,
};
setSession(newSession);
saveQuizSession(newSession, 'october6Exam');
} else {
const answeredQuestions = [Link]((q, index) => ({
...q,
userAnswer: [Link][index],
isCorrect: [Link][index] === [Link],
}));
onComplete(answeredQuestions);
}
};

const getButtonClass = (option: string) => {


if (!session) return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';

const currentQuestion = [Link][[Link]];


const selectedAnswer = [Link][[Link]];
const isAnswered = selectedAnswer !== undefined;

if (!isAnswered) {
return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';
}
if (option === [Link]) {
return 'bg-green-500/50 border-green-500';
}
if (option === selectedAnswer) {
return 'bg-red-500/50 border-red-500';
}
return 'bg-[var(--bg-alt)] opacity-60';
};

const renderContent = () => {


if (!session) {
return (
<div className="flex justify-center items-center h-48">
<Loader />
</div>
);
}

const currentQuestion = [Link][[Link]];


const isAnswered = [Link][[Link]] !== undefined;

if (!currentQuestion) return <div className="text-center text-[var(--text-


secondary)]">Question not found.</div>;
return (
<div>
<div className="flex items-start gap-3 mb-2">
<p className="text-lg text-[var(--text-primary)] font-bold pt-
1.5">{[Link] + 1}.</p>
<p className="text-lg text-[var(--text-
primary)]">{[Link]}</p>
</div>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-2 ml-8">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-4 ml-8">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-[var(--
accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}

<div className="space-y-3">
{([Link] || []).map((option, index) => (
<button
key={index}
onClick={() => handleAnswerSelect(option)}
disabled={isAnswered}
className={`w-full p-3 text-left rounded-lg border border-[var(--
border-color)] transition-all ${getButtonClass(option)}`}
>
{option}
</button>
))}
</div>
{isAnswered && (
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4 border-green-
500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose prose-invert
max-w-none" dangerouslySetInnerHTML={{ __html: explanationHtml }}/>
{!([Link] ||
[]).includes([Link]) && (
<div className="mt-2 pt-2 border-t border-[var(--border-color)]">
<h5 className="font-bold text-yellow-400">Correct Answer</h5>
<p className="text-sm text-[var(--text-
secondary)]">{[Link]}</p>
</div>
)}
</div>
)}
</div>
);
};

const isAnswered = session ? [Link][[Link]] !==


undefined : false;
return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)] flex-shrink-0">
<h2 className="text-xl font-bold text-[var(--text-primary)]">October 6
University Exam</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto flex-grow">
{renderContent()}
</main>
<footer className="p-4 border-t border-[var(--border-color)] flex-shrink-
0">
{isAnswered && session && (
<button
onClick={handleNextQuestion}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white
font-bold rounded-lg hover:opacity-90 transition-opacity"
>
{[Link] < [Link] - 1 ? 'Next
Question' : 'Finish'}
</button>
)}
</footer>
</div>
</div>
);
};

export default PracticeModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React from 'react';
import { PracticeQuestion } from '../[Link]';
import { XIcon } from './icons/[Link]';

declare const marked: any;

interface RealExamHistoryModalProps {
questions: PracticeQuestion[];
onClose: () => void;
}

const RealExamHistoryModal: [Link]<RealExamHistoryModalProps> = ({ questions,


onClose }) => {
const renderExplanation = (explanation: string) => {
try {
return [Link](explanation);
} catch (e) {
return explanation;
}
};
return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)]">
<h2 className="text-xl font-bold text-[var(--text-primary)]">Review Real
Exam Questions</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto">
{[Link] === 0 ? (
<p className="text-center text-[var(--text-secondary)]">No real exam
questions have been solved for this page yet.</p>
) : (
<div className="space-y-6">
{[Link]((q, qIndex) => (
<div key={qIndex} className="bg-[var(--bg-main)] p-4 rounded-lg">
<p className="text-lg text-[var(--text-primary)] mb-2">{qIndex +
1}. {[Link]}</p>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-
2">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-3">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-
[var(--accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, oIndex) => {
const isCorrect = option === [Link];
return (
<div
key={oIndex}
className={`w-full p-3 text-left rounded-lg border $
{isCorrect ? 'bg-green-500/30 border-green-500' : 'bg-[var(--bg-alt)] border-
[var(--border-color)] opacity-70'}`}
>
{option}
</div>
);
})}
</div>
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4
border-green-500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose
prose-invert max-w-none" dangerouslySetInnerHTML={{ __html:
renderExplanation([Link]) }}/>
</div>
</div>
))}
</div>
)}
</main>
<footer className="p-4 border-t border-[var(--border-color)]">
<button
onClick={onClose}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white font-
bold rounded-lg hover:opacity-90 transition-opacity"
>
Close
</button>
</footer>
</div>
</div>
);
};

export default RealExamHistoryModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState, useEffect, useMemo } from 'react';
import { QuizSession, PracticeQuestion } from '../[Link]';
import Loader from './[Link]';
import { XIcon } from './icons/[Link]';
import useQuizSession from '../hooks/[Link]';

declare const marked: any;

interface RealExamModalProps {
fileName: string;
lessonId: string;
onClose: () => void;
onComplete: (answeredQuestions: PracticeQuestion[]) => void;
}

const RealExamModal: [Link]<RealExamModalProps> = ({ fileName, lessonId, onClose,


onComplete }) => {
const { getQuizSession, saveQuizSession } = useQuizSession(fileName);
const [session, setSession] = useState<QuizSession | null>(null);

useEffect(() => {
const loadedSession = getQuizSession(lessonId, 'realExam');
if (loadedSession) {
setSession(loadedSession);
}
}, [lessonId, fileName, getQuizSession]);

const currentQuestion = session?.questions[[Link] ?? 0];


const explanationHtml = useMemo(() => {
if (!currentQuestion?.explanation) return '';
try {
return [Link]([Link]);
} catch (e) {
return [Link];
}
}, [currentQuestion]);

const handleAnswerSelect = (option: string) => {


if (!session || [Link][[Link]] !== undefined) return;

const newSession: QuizSession = {


...session,
answers: {
...[Link],
[[Link]]: option,
},
};
setSession(newSession);
saveQuizSession(newSession, 'realExam');
};

const handleNextQuestion = () => {


if (!session) return;
if ([Link] < [Link] - 1) {
const newSession: QuizSession = {
...session,
currentIndex: [Link] + 1,
};
setSession(newSession);
saveQuizSession(newSession, 'realExam');
} else {
const answeredQuestions = [Link]((q, index) => ({
...q,
userAnswer: [Link][index],
isCorrect: [Link][index] === [Link],
}));
onComplete(answeredQuestions);
}
};

const getButtonClass = (option: string) => {


if (!session) return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';

const currentQuestion = [Link][[Link]];


const selectedAnswer = [Link][[Link]];
const isAnswered = selectedAnswer !== undefined;

if (!isAnswered) {
return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';
}
if (option === [Link]) {
return 'bg-green-500/50 border-green-500';
}
if (option === selectedAnswer) {
return 'bg-red-500/50 border-red-500';
}
return 'bg-[var(--bg-alt)] opacity-60';
};

const renderContent = () => {


if (!session) {
return (
<div className="flex justify-center items-center h-48">
<Loader />
</div>
);
}

const currentQuestion = [Link][[Link]];


const isAnswered = [Link][[Link]] !== undefined;

if (!currentQuestion) return <div className="text-center text-[var(--text-


secondary)]">Question not found.</div>;

return (
<div>
<div className="flex items-start gap-3 mb-2">
<p className="text-lg text-[var(--text-primary)] font-bold pt-
1.5">{[Link] + 1}.</p>
<p className="text-lg text-[var(--text-
primary)]">{[Link]}</p>
</div>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-2 ml-8">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-4 ml-8">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-[var(--
accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, index) => (
<button
key={index}
onClick={() => handleAnswerSelect(option)}
disabled={isAnswered}
className={`w-full p-3 text-left rounded-lg border border-[var(--
border-color)] transition-all ${getButtonClass(option)}`}
>
{option}
</button>
))}
</div>
{isAnswered && (
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4 border-green-
500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose prose-invert
max-w-none" dangerouslySetInnerHTML={{ __html: explanationHtml }}/>
{!([Link] ||
[]).includes([Link]) && (
<div className="mt-2 pt-2 border-t border-[var(--border-color)]">
<h5 className="font-bold text-yellow-400">Correct Answer</h5>
<p className="text-sm text-[var(--text-
secondary)]">{[Link]}</p>
</div>
)}
</div>
)}
</div>
);
};

const isAnswered = session ? [Link][[Link]] !==


undefined : false;

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)] flex-shrink-0">
<h2 className="text-xl font-bold text-[var(--text-primary)]">Real Exam
Questions</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto flex-grow">
{renderContent()}
</main>
<footer className="p-4 border-t border-[var(--border-color)] flex-shrink-
0">
{isAnswered && session && (
<button
onClick={handleNextQuestion}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white
font-bold rounded-lg hover:opacity-90 transition-opacity"
>
{[Link] < [Link] - 1 ? 'Next
Question' : 'Finish'}
</button>
)}
</footer>
</div>
</div>
);
};

export default RealExamModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState, useMemo } from 'react';
import { analyzeRecall } from '../services/[Link]';
import Loader from './[Link]';
import { XIcon } from './icons/[Link]';

declare const marked: any;

interface RecallModalProps {
originalText: string;
onClose: () => void;
}
const RecallModal: [Link]<RecallModalProps> = ({ originalText, onClose }) => {
const [recallText, setRecallText] = useState('');
const [analysis, setAnalysis] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const handleSubmit = async () => {


setIsLoading(true);
setError(null);
try {
const result = await analyzeRecall(originalText, recallText);
setAnalysis(result);
} catch (err) {
setError(err instanceof Error ? [Link] : "An unknown error occurred.");
} finally {
setIsLoading(false);
}
};

const analysisHtml = useMemo(() => {


if (!analysis) return '';
return [Link](analysis);
}, [analysis]);

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-3xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)] flex-shrink-0">
<h2 className="text-xl font-bold text-[var(--text-primary)]">Test Your
Recall</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto flex-grow">
{analysis ? (
<div>
<h3 className="text-lg font-bold mb-2 text-[var(--text-accent)]">AI
Feedback</h3>
<div className="prose prose-invert max-w-none p-4 bg-[var(--bg-main)]
rounded-lg" dangerouslySetInnerHTML={{ __html: analysisHtml }} />
</div>
) : (
<div>
<label htmlFor="recall-input" className="block text-sm font-medium
text-[var(--text-secondary)] mb-2">
Write down everything you remember from this page. Don't worry
about getting it perfect.
</label>
<textarea
id="recall-input"
rows={10}
value={recallText}
onChange={(e) => setRecallText([Link])}
className="w-full p-3 bg-[var(--bg-main)] border border-[var(--
border-color)] rounded-lg focus:ring-2 focus:ring-[var(--accent-primary)]
focus:outline-none"
placeholder="Start writing here..."
disabled={isLoading}
/>
{error && <p className="mt-2 text-sm text-[var(--accent-
danger)]">{error}</p>}
</div>
)}
</main>
<footer className="p-4 border-t border-[var(--border-color)] flex-shrink-
0">
{analysis ? (
<button
onClick={onClose}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white
font-bold rounded-lg hover:opacity-90 transition-opacity"
>
Close
</button>
) : (
<button
onClick={handleSubmit}
disabled={isLoading || [Link]().length === 0}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white
font-bold rounded-lg hover:opacity-90 transition-opacity disabled:opacity-50
disabled:cursor-not-allowed flex justify-center items-center"
>
{isLoading ? <Loader /> : 'Analyze My Recall'}
</button>
)}
</footer>
</div>
</div>
);
};

export default RecallModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React from 'react';
import { PracticeQuestion } from '../[Link]';
import { XIcon, HistoryIcon } from './icons/[Link]';

declare const marked: any;

interface ReviewMistakesModalProps {
title: string;
questions: PracticeQuestion[];
onClose: () => void;
}

const ReviewMistakesModal: [Link]<ReviewMistakesModalProps> = ({ title,


questions, onClose }) => {
const renderExplanation = (explanation: string) => {
try {
return [Link](explanation);
} catch (e) {
return explanation;
}
};

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-3xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)]">
<div className="flex items-center gap-2">
<HistoryIcon />
<h2 className="text-xl font-bold
text-[var(--text-primary)]">{title}</h2>
</div>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto">
{[Link] === 0 ? (
<p className="text-center text-[var(--text-secondary)] py-8">No
questions to review in this category.</p>
) : (
<div className="space-y-6">
{[Link]((q, qIndex) => (
<div key={qIndex} className="bg-[var(--bg-main)] p-4 rounded-lg">
<p className="text-lg text-[var(--text-primary)] mb-2">{qIndex +
1}. {[Link]}</p>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-
2">
(From: {[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-3">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-
[var(--accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{[Link]((option, oIndex) => {
const isCorrect = option === [Link];
const isUserAnswer = option === [Link];

let optionClass = 'bg-[var(--bg-alt)] border-[var(--border-


color)] opacity-70';
if (isCorrect) {
optionClass = 'bg-green-500/30 border-green-500';
} else if (isUserAnswer) {
optionClass = 'bg-red-500/30 border-red-500';
}
return (
<div key={oIndex} className={`w-full p-3 text-left rounded-
lg border ${optionClass}`}>
{option}
</div>
);
})}
</div>
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4
border-green-500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose
prose-invert max-w-none" dangerouslySetInnerHTML={{ __html:
renderExplanation([Link]) }}/>
</div>
</div>
))}
</div>
)}
</main>
<footer className="p-4 border-t border-[var(--border-color)]">
<button
onClick={onClose}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white font-
bold rounded-lg hover:opacity-90 transition-opacity"
>
Close
</button>
</footer>
</div>
</div>
);
};

export default ReviewMistakesModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState, useEffect, useMemo } from 'react';
import { PracticeQuestion, QuizSession } from '../[Link]';
import { XIcon } from './icons/[Link]';
import useQuizSession from '../hooks/[Link]';
import Loader from './[Link]';

declare const marked: any;

interface StudyByQuestionsModalProps {
fileName: string;
lessonId: string;
onClose: () => void;
onComplete: (answeredQuestions: PracticeQuestion[]) => void;
}

const StudyByQuestionsModal: [Link]<StudyByQuestionsModalProps> = ({ fileName,


lessonId, onClose, onComplete }) => {
const { getQuizSession, saveQuizSession } = useQuizSession(fileName);
const [session, setSession] = useState<QuizSession | null>(null);

useEffect(() => {
const loadedSession = getQuizSession(lessonId, 'studyMcq');
if (loadedSession) {
setSession(loadedSession);
}
}, [lessonId, fileName, getQuizSession]);

const currentQuestion = session?.questions[[Link] ?? 0];


const explanationHtml = useMemo(() => {
if (!currentQuestion?.explanation) return '';
try {
return [Link]([Link]);
} catch (e) {
return [Link];
}
}, [currentQuestion]);

const handleAnswerSelect = (option: string) => {


if (!session || [Link][[Link]] !== undefined) return;

const newSession: QuizSession = {


...session,
answers: {
...[Link],
[[Link]]: option,
},
};
setSession(newSession);
saveQuizSession(newSession, 'studyMcq');
};

const handleNextQuestion = () => {


if (!session) return;
if ([Link] < [Link] - 1) {
const newSession: QuizSession = {
...session,
currentIndex: [Link] + 1,
};
setSession(newSession);
saveQuizSession(newSession, 'studyMcq');
} else {
const answeredQuestions = [Link]((q, index) => ({
...q,
userAnswer: [Link][index],
isCorrect: [Link][index] === [Link],
}));
onComplete(answeredQuestions);
}
};

const getButtonClass = (option: string) => {


if (!session) return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';

const currentQuestion = [Link][[Link]];


const selectedAnswer = [Link][[Link]];
const isAnswered = selectedAnswer !== undefined;

if (!isAnswered) {
return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';
}
if (option === [Link]) {
return 'bg-green-500/50 border-green-500';
}
if (option === selectedAnswer) {
return 'bg-red-500/50 border-red-500';
}
return 'bg-[var(--bg-alt)] opacity-60';
};

const renderContent = () => {


if (!session) {
return (
<div className="flex justify-center items-center h-48">
<Loader />
</div>
);
}
const currentQuestion = [Link][[Link]];
const isAnswered = [Link][[Link]] !== undefined;

if (!currentQuestion) return <div className="text-center text-[var(--text-


secondary)]">Question not found.</div>;

return (
<div>
<div className="flex items-start gap-3 mb-2">
<p className="text-lg text-[var(--text-primary)] font-bold pt-
1.5">{[Link] + 1}.</p>
<p className="text-lg text-[var(--text-
primary)]">{[Link]}</p>
</div>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-2 ml-8">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-4 ml-8">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-[var(--
accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, index) => (
<button
key={index}
onClick={() => handleAnswerSelect(option)}
disabled={isAnswered}
className={`w-full p-3 text-left rounded-lg border border-[var(--
border-color)] transition-all ${getButtonClass(option)}`}
>
{option}
</button>
))}
</div>
{isAnswered && (
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4 border-green-
500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose prose-invert
max-w-none" dangerouslySetInnerHTML={{ __html: explanationHtml }}/>
{!([Link] ||
[]).includes([Link]) && (
<div className="mt-2 pt-2 border-t border-[var(--border-color)]">
<h5 className="font-bold text-yellow-400">Correct Answer</h5>
<p className="text-sm text-[var(--text-
secondary)]">{[Link]}</p>
</div>
)}
</div>
)}
</div>
);
};

const isAnswered = session ? [Link][[Link]] !==


undefined : false;

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)] flex-shrink-0">
<h2 className="text-xl font-bold text-[var(--text-primary)]">Study by
Questions</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto flex-grow">
{renderContent()}
</main>
<footer className="p-4 border-t border-[var(--border-color)] flex-shrink-
0">
{isAnswered && session && (
<button
onClick={handleNextQuestion}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white
font-bold rounded-lg hover:opacity-90 transition-opacity"
>
{[Link] < [Link] - 1 ? 'Next
Question' : 'Finish'}
</button>
)}
</footer>
</div>
</div>
);
};

export default StudyByQuestionsModal;


--- END OF FILE components/[Link] ---
--- START OF FILE components/[Link] ---
import React from 'react';
import { PracticeQuestion } from '../[Link]';
import { XIcon } from './icons/[Link]';

declare const marked: any;

interface StudyHighlightsHistoryModalProps {
questions: PracticeQuestion[];
onClose: () => void;
title?: string;
}

const StudyHighlightsHistoryModal: [Link]<StudyHighlightsHistoryModalProps> =


({ questions, onClose, title = 'Review Study Highlights' }) => {
const renderExplanation = (explanation: string) => {
try {
return [Link](explanation);
} catch (e) {
return explanation;
}
};

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)]">
<h2 className="text-xl font-bold text-[var(--text-primary)]">{title}</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto">
{[Link] === 0 ? (
<p className="text-center text-[var(--text-secondary)]">No questions
have been saved for this page yet.</p>
) : (
<div className="space-y-6">
{[Link]((q, qIndex) => (
<div key={qIndex} className="bg-[var(--bg-main)] p-4 rounded-lg">
<p className="text-lg text-[var(--text-primary)] mb-2">{qIndex +
1}. {[Link]}</p>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-
2">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-3">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-
[var(--accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, oIndex) => {
const isCorrect = option === [Link];
return (
<div
key={oIndex}
className={`w-full p-3 text-left rounded-lg border $
{isCorrect ? 'bg-green-500/30 border-green-500' : 'bg-[var(--bg-alt)] border-
[var(--border-color)] opacity-70'}`}
>
{option}
</div>
);
})}
</div>
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4
border-green-500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose
prose-invert max-w-none" dangerouslySetInnerHTML={{ __html:
renderExplanation([Link]) }}/>
</div>
</div>
))}
</div>
)}
</main>
<footer className="p-4 border-t border-[var(--border-color)]">
<button
onClick={onClose}
className="w-full py-2 px-4 bg-[var(--accent-primary)] text-white font-
bold rounded-lg hover:opacity-90 transition-opacity"
>
Close
</button>
</footer>
</div>
</div>
);
};

export default StudyHighlightsHistoryModal;


--- END OF FILE components/[Link] ---

--- START OF FILE components/[Link] ---


import React, { useState, useEffect, useMemo } from 'react';
import { QuizSession, PracticeQuestion, QuizType } from '../[Link]';
import { XIcon } from './icons/[Link]';
import useQuizSession from '../hooks/[Link]';
import Loader from './[Link]';

declare const marked: any;

interface StudyHighlightsModalProps {
fileName: string;
lessonId: string;
onClose: () => void;
onComplete: (answeredQuestions: PracticeQuestion[]) => void;
quizType?: QuizType;
title?: string;
}

const StudyHighlightsModal: [Link]<StudyHighlightsModalProps> = ({ fileName,


lessonId, onClose, onComplete, quizType = 'studyHighlights', title = 'Study
Highlights' }) => {
const { getQuizSession, saveQuizSession } = useQuizSession(fileName);
const [session, setSession] = useState<QuizSession | null>(null);

useEffect(() => {
const loadedSession = getQuizSession(lessonId, quizType);
if (loadedSession) {
setSession(loadedSession);
}
}, [lessonId, fileName, getQuizSession, quizType]);

const currentQuestion = session?.questions[[Link] ?? 0];


const explanationHtml = useMemo(() => {
if (!currentQuestion?.explanation) return '';
try {
return [Link]([Link]);
} catch (e) {
return [Link];
}
}, [currentQuestion]);

const handleAnswerSelect = (option: string) => {


if (!session || [Link][[Link]] !== undefined) return;

const newSession: QuizSession = {


...session,
answers: {
...[Link],
[[Link]]: option,
},
};
setSession(newSession);
saveQuizSession(newSession, quizType);
};

const handleNextQuestion = () => {


if (!session) return;
if ([Link] < [Link] - 1) {
const newSession: QuizSession = {
...session,
currentIndex: [Link] + 1,
};
setSession(newSession);
saveQuizSession(newSession, quizType);
} else {
const answeredQuestions = [Link]((q, index) => ({
...q,
userAnswer: [Link][index],
isCorrect: [Link][index] === [Link],
}));
onComplete(answeredQuestions);
}
};
const getButtonClass = (option: string) => {
if (!session) return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';

const currentQuestion = [Link][[Link]];


const selectedAnswer = [Link][[Link]];
const isAnswered = selectedAnswer !== undefined;

if (!isAnswered) {
return 'bg-[var(--bg-alt)] hover:bg-[var(--border-color)]';
}
if (option === [Link]) {
return 'bg-green-500/50 border-green-500';
}
if (option === selectedAnswer) {
return 'bg-red-500/50 border-red-500';
}
return 'bg-[var(--bg-alt)] opacity-60';
};

const renderContent = () => {


if (!session) {
return (
<div className="flex justify-center items-center h-48">
<Loader />
</div>
);
}
const currentQuestion = [Link][[Link]];
const isAnswered = [Link][[Link]] !== undefined;

if (!currentQuestion) return <div className="text-center text-[var(--text-


secondary)]">Question not found.</div>;

return (
<div>
<div className="flex items-start gap-3 mb-2">
<p className="text-lg text-[var(--text-primary)] font-bold pt-
1.5">{[Link] + 1}.</p>
<p className="text-lg text-[var(--text-
primary)]">{[Link]}</p>
</div>
{[Link] && (
<p className="text-xs text-[var(--text-secondary)] italic mb-2 ml-8">
({[Link]})
</p>
)}
{[Link] && [Link] > 0 && (
<div className="flex flex-wrap gap-2 mb-4 ml-8">
{[Link](tag => (
<span key={tag} className="px-2 py-0.5 text-xs bg-[var(--
accent-primary)]/20 text-[var(--accent-primary)] rounded-full">{tag}</span>
))}
</div>
)}
<div className="space-y-3">
{([Link] || []).map((option, index) => (
<button
key={index}
onClick={() => handleAnswerSelect(option)}
disabled={isAnswered}
className={`w-full p-3 text-left rounded-lg border border-[var(--
border-color)] transition-all ${getButtonClass(option)}`}
>
{option}
</button>
))}
</div>
{isAnswered && (
<div className="mt-4 p-3 bg-black/20 rounded-lg border-l-4 border-green-
500">
<h4 className="font-bold text-green-400">Explanation</h4>
<div className="text-sm text-[var(--text-secondary)] prose prose-invert
max-w-none" dangerouslySetInnerHTML={{ __html: explanationHtml }}/>
{!([Link] ||
[]).includes([Link]) && (
<div className="mt-2 pt-2 border-t border-[var(--border-color)]">
<h5 className="font-bold text-yellow-400">Correct Answer</h5>
<p className="text-sm text-[var(--text-
secondary)]">{[Link]}</p>
</div>
)}
</div>
)}
</div>
);
};

const isAnswered = session ? [Link][[Link]] !==


undefined : false;

return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center p-4
z-50">
<div className="w-full max-w-2xl bg-[var(--bg-alt)] rounded-2xl shadow-2xl
border border-[var(--border-color)] flex flex-col max-h-[90vh]">
<header className="p-4 flex justify-between items-center border-b border-
[var(--border-color)] flex-shrink-0">
<h2 className="text-xl font-bold text-[var(--text-primary)]">{title}</h2>
<button onClick={onClose} className="text-[var(--text-secondary)]
hover:text-[var(--text-primary)]">
<XIcon />
</button>
</header>
<main className="p-6 overflow-y-auto flex-grow">
{renderContent()}

You might also like