diff --git a/.htaccess b/.htaccess index e2bbc23..bbdf7ab 100644 --- a/.htaccess +++ b/.htaccess @@ -1,18 +1,2 @@ DirectoryIndex index.php index.html Options -Indexes -Options -MultiViews - -RewriteEngine On - -# 0) Serve existing files/directories as-is -RewriteCond %{REQUEST_FILENAME} -f [OR] -RewriteCond %{REQUEST_FILENAME} -d -RewriteRule ^ - [L] - -# 1) Internal map: /page or /page/ -> /page.php (if such PHP file exists) -RewriteCond %{REQUEST_FILENAME}.php -f -RewriteRule ^(.+?)/?$ $1.php [L] - -# 2) Optional: strip trailing slash for non-directories (keeps .php links working) -RewriteCond %{REQUEST_FILENAME} !-d -RewriteRule ^(.+)/$ $1 [R=301,L] diff --git a/.htaccess_old b/.htaccess_old new file mode 100644 index 0000000..e2bbc23 --- /dev/null +++ b/.htaccess_old @@ -0,0 +1,18 @@ +DirectoryIndex index.php index.html +Options -Indexes +Options -MultiViews + +RewriteEngine On + +# 0) Serve existing files/directories as-is +RewriteCond %{REQUEST_FILENAME} -f [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^ - [L] + +# 1) Internal map: /page or /page/ -> /page.php (if such PHP file exists) +RewriteCond %{REQUEST_FILENAME}.php -f +RewriteRule ^(.+?)/?$ $1.php [L] + +# 2) Optional: strip trailing slash for non-directories (keeps .php links working) +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^(.+)/$ $1 [R=301,L] diff --git a/activate_institution.php b/activate_institution.php new file mode 100644 index 0000000..9728501 --- /dev/null +++ b/activate_institution.php @@ -0,0 +1,18 @@ +connect_error) { + die("DB Connection failed"); +} + +if (isset($_GET['id'])) { + $id = intval($_GET['id']); + + $sql = "UPDATE institutions SET status='active' WHERE id=$id"; + $conn->query($sql); +} + +$conn->close(); + +// Redirect back to dashboard +header("Location: admin_dashboard.php"); +exit(); diff --git a/active_learners.php b/active_learners.php new file mode 100644 index 0000000..5925f1f --- /dev/null +++ b/active_learners.php @@ -0,0 +1,116 @@ +connect_error) { + die("Connection failed: " . $conn->connect_error); +} + +/* + ACTIVE LEARNERS: + - Practiced at least once in last 7 days +*/ + +$sql = " +SELECT + pa.roll_no, + MAX(pa.last_attempt_at) AS last_practice_date, + DATEDIFF(NOW(), MAX(pa.last_attempt_at)) AS days_gap +FROM practice_attempts pa +GROUP BY pa.roll_no +HAVING + MAX(pa.last_attempt_at) >= NOW() - INTERVAL 7 DAY +ORDER BY last_practice_date DESC +"; + +$result = $conn->query($sql); +?> + + + + + Active Learners + + + + +

🟒 Active Learners (Last 7 Days)

+
+ These students are maintaining learning continuity. +
+ + + + + + + + + num_rows > 0) { + while ($row = $result->fetch_assoc()) { + echo " + + + + "; + } + } else { + echo " + + "; + } + ?> + +
Roll NoLast Practice DateDays Since Last Practice
{$row['roll_no']}{$row['last_practice_date']}{$row['days_gap']} day(s)
No active learners yet
+ + + + +close(); ?> diff --git a/admin/admin_dashboard.php b/admin/admin_dashboard.php new file mode 100644 index 0000000..9e82c3c --- /dev/null +++ b/admin/admin_dashboard.php @@ -0,0 +1,147 @@ +prepare(" + SELECT institution_name, institution_type + FROM institutions + WHERE id = ? + "); + $stmt->execute([$institution_id]); + $inst = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($inst) { + + // username pattern + $username = "inst_" . $institution_id; + + // temporary password + $temp_password = strtoupper(substr(md5(time()), 0, 6)); + $hashed_password = password_hash($temp_password, PASSWORD_DEFAULT); + + // update institution record + $update = $pdo->prepare(" + UPDATE institutions + SET username = ?, password = ?, status = 'active' + WHERE id = ? + "); + $update->execute([$username, $hashed_password, $institution_id]); + + // login URL (FINAL) + $login_url = "http://localhost/rs_lab/institution/login.php"; + + // activation message + $message = " +Hello πŸ‘‹ + +Your institution has been activated on RS Learning Lab. + +🏫 Institution Type: {$inst['institution_type']} + +πŸ” Institution Login Details: +🌐 Login URL: {$login_url} +πŸ‘€ Username: {$username} +πŸ”‘ Temporary Password: {$temp_password} + +Please log in and change your password immediately after first login. + +– RS Learning Lab Team + "; + } +} + +// fetch all institutions +$list = $pdo->query(" + SELECT id, institution_name, email, institution_type, status + FROM institutions + ORDER BY id DESC +")->fetchAll(PDO::FETCH_ASSOC); + +?> + + + + + Founder Dashboard | RS Learning Lab + + + + + +

🧠 RS Learning Lab – Founder Dashboard

+ + + + + + + + + + + + + + + + + + + + + +
IDInstitutionEmailTypeStatusAction
+
+ + +
+
+ + +

πŸ“© Activation Message

+

+ + + + + + + diff --git a/admin/admin_login.php b/admin/admin_login.php new file mode 100644 index 0000000..2db6242 --- /dev/null +++ b/admin/admin_login.php @@ -0,0 +1,53 @@ + + +
+
+
+ +
+

Institution Admin Login

+

+ Secure access for registered schools and colleges +

+ +
+
+ + +
+ +
+ + +
+ +
+ +
+
+ +

+ Only registered institutions can access the dashboard. +

+
+ +
+
+
+ + diff --git a/admin/computer_quiz_setup.php b/admin/computer_quiz_setup.php new file mode 100644 index 0000000..f5ff108 --- /dev/null +++ b/admin/computer_quiz_setup.php @@ -0,0 +1,229 @@ +prepare( + "UPDATE computer_quizzes + SET is_active = 0 + WHERE institution_id = ?" + ); + $stmt->bind_param("i", $institutionId); + $stmt->execute(); + $stmt->close(); + + // 2️⃣ Create new active quiz + $stmt = $conn->prepare( + "INSERT INTO computer_quizzes + (institution_id, quiz_name, total_questions, is_active, created_at) + VALUES (?, ?, ?, 1, NOW())" + ); + $stmt->bind_param("isi", $institutionId, $quiz_name, $total_questions); + $stmt->execute(); + $stmt->close(); + + $message = "Computer quiz started successfully."; + + } catch (Throwable $e) { + $error = "Something went wrong. Please try again."; + } + } +} +?> + + + + +Start Computer Quiz | RS Learning Lab + + + + + + + + +
+ + + + + +
+ + +
+
Start Computer Quiz
+
+ Β· Admin + Logout +
+
+ + +
+
+

Create Learning Style Quiz

+

+ This quiz identifies learning patterns and behaviour.
+ No marks. No answer key. No subject. +

+ + +
+ + + +
+ + +
+ + + +
+ β€’ Total questions fixed at 20
+ β€’ Only one active quiz per institution +
+ + +
+
+
+ +
+
+ + diff --git a/admin/create_credentials.php b/admin/create_credentials.php new file mode 100644 index 0000000..7a1f97f --- /dev/null +++ b/admin/create_credentials.php @@ -0,0 +1,107 @@ +prepare("SELECT institution_name, institution_type FROM institutions WHERE id = ?"); +$stmt->execute([$institution_id]); +$inst = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$inst) { + die("Institution not found"); +} + +/* Generate credentials */ +$username = strtoupper(substr($inst['institution_type'], 0, 3)) . "_RS_" . $institution_id; +$password_plain = substr(str_shuffle("ABCDEFGHJKLMNPQRSTUVWXYZ23456789"), 0, 8); +$password_hash = password_hash($password_plain, PASSWORD_DEFAULT); + +/* Save credentials */ +$update = $pdo->prepare(" + UPDATE institutions + SET username = ?, password_hash = ?, credentials_created = 1 + WHERE id = ? +"); +$update->execute([$username, $password_hash, $institution_id]); +?> + + + + + Credentials Generated + < style > + body { + background: #050b14; + color: #eaf2ff; + font-family: Inter, sans-serif; + display: flex; + align-items: center; + justify-content: center; + height: 100vh; + } + + .card { + background: rgba(255,255,255,0.05); + padding: 30px 40px; + border-radius: 14px; + text-align: center; + width: 420px; + } + + h2 { + margin-bottom: 10px; + } + + p { + color: #b8c6de; + font-size: 14px; + } + + .cred { + margin-top: 20px; + font-size: 15px; + background: rgba(255,255,255,0.08); + padding: 14px; + border-radius: 10px; + } + + a { + display: inline-block; + margin-top: 20px; + color: #7fd4ff; + text-decoration: none; + } + + + + + +
+

Credentials Created

+

Send these credentials to the institution

+ +
+ Username:


+ Password:
+
+ +

+ Institution will be forced to change password on first login. +

+ + ← Back to Dashboard +
+ + + diff --git a/admin/index.php b/admin/index.php index 6c2b6cd..37cd814 100644 --- a/admin/index.php +++ b/admin/index.php @@ -1,66 +1,70 @@ - - - - - - Admin Dashboard - - - - +
+
-
-

Admin Dashboard

-

Welcome to the admin panel. Here you can manage the content of the application.

- -
-
-
-
-
Challenges
-

Manage coding challenges.

- Go to Challenges -
-
-
-
-
-
-
Competitions
-

Manage coding competitions.

- Go to Competitions -
-
-
-
-
-
-
Users
-

Manage users.

- Go to Users -
-
-
-
+ +
+

+ Institution Dashboard +

+

+ Manage learning insights, coding challenges, and certifications from one place. +

- - + + +
+ + +
+

Learning Style Assessment

+

+ Understand how students learn through pattern-based assessments + without marks or exam pressure. +

+ +
+ + +
+

Coding Challenges

+

+ Structured coding tracks designed to build logical thinking + and real-world problem-solving skills. +

+ +
+ + +
+

Certificates

+

+ Generate professional participation certificates + recognizing student effort and demonstrated skills. +

+ +
+ +
+ +
+
+ + diff --git a/admin/login.php b/admin/login.php deleted file mode 100644 index 0a9b3bc..0000000 --- a/admin/login.php +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - Admin Login - - - -
-
-
-
-
Admin Login
-
- -
- -

- -
- -
-
- - -
-
- - -
- -
-
-
-
-
-
- - diff --git a/admin_dashboard.php b/admin_dashboard.php new file mode 100644 index 0000000..df7cdd7 --- /dev/null +++ b/admin_dashboard.php @@ -0,0 +1,196 @@ +connect_error) { + die("DB Connection failed"); +} + +/* GET VIEW FILTER */ +$view = $_GET['view'] ?? 'active'; + +/* COUNTS */ +$total = $conn->query("SELECT COUNT(*) c FROM institutions WHERE deleted_at IS NULL")->fetch_assoc()['c']; +$active = $conn->query("SELECT COUNT(*) c FROM institutions WHERE status='active' AND deleted_at IS NULL")->fetch_assoc()['c']; +$inactive = $conn->query("SELECT COUNT(*) c FROM institutions WHERE status='inactive' AND deleted_at IS NULL")->fetch_assoc()['c']; +$deleted = $conn->query("SELECT COUNT(*) c FROM institutions WHERE deleted_at IS NOT NULL")->fetch_assoc()['c']; + +/* FILTER QUERY */ +if($view == 'active'){ + $result = $conn->query("SELECT * FROM institutions WHERE status='active' AND deleted_at IS NULL"); +} +elseif($view == 'inactive'){ + $result = $conn->query("SELECT * FROM institutions WHERE status='inactive' AND deleted_at IS NULL"); +} +elseif($view == 'deleted'){ + $result = $conn->query("SELECT * FROM institutions WHERE deleted_at IS NOT NULL"); +} +else{ + $result = $conn->query("SELECT * FROM institutions WHERE deleted_at IS NULL"); +} +?> + + + + +Founder Dashboard | RS Learning Lab + + + + + + +
+ +

Founder Dashboard

+ +
+ + +
+

+

Total

+
+
+ + +
+

+

Active

+
+
+ + +
+

+

Inactive

+
+
+ + +
+

+

Deleted

+
+
+ +
+ + + + + + + + + + + + +fetch_assoc()): ?> + + + + + + + + + + + + + + + + +
NameContactPhoneEmailStatusAction
+ + + + + +Deleted + + + + + + + + + +
+ + + +
+ +β€” + +
+ +
+ + + + +close(); ?> \ No newline at end of file diff --git a/admin_login.php b/admin_login.php new file mode 100644 index 0000000..9bab386 --- /dev/null +++ b/admin_login.php @@ -0,0 +1,187 @@ + + + + + + +Admin Login | RS Learning Lab + + + + + + + +
+ +
+ + +

RS Learning Lab

+

Institution Admin Login

+
+ + +
+ + +
+
+ + +
+ +
+ + +
+ + +
+ +
+ Demo Credentials
+ Email: admin@rslearninglab.in
+ Password: admin123 +
+ + + +
+ + + diff --git a/api_scan_omr.php b/api_scan_omr.php new file mode 100644 index 0000000..f16bac6 --- /dev/null +++ b/api_scan_omr.php @@ -0,0 +1,71 @@ + date("Y-m-d H:i:s"), + 'roll_no' => $roll_no, + 'student_name' => $student_name, + 'answers' => $clean_answers +]; + +file_put_contents( + __DIR__ . "/omr_log.txt", + json_encode($logEntry) . PHP_EOL, + FILE_APPEND +); + +// πŸ”₯ FINAL CHANGE: RETURN instead of echo +return $clean_answers; \ No newline at end of file diff --git a/assets/css/admin.css b/assets/css/admin.css deleted file mode 100644 index a0708a7..0000000 --- a/assets/css/admin.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - background-color: #f8f9fa; -} - -.login-container { - max-width: 400px; - margin: 100px auto; - padding: 30px; - border: 1px solid #dee2e6; - border-radius: 5px; - background-color: #fff; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); -} diff --git a/assets/css/custom.css b/assets/css/custom.css deleted file mode 100644 index 9eeda0a..0000000 --- a/assets/css/custom.css +++ /dev/null @@ -1,74 +0,0 @@ -:root { - --primary-color: #20C997; - --secondary-color: #FD7E14; - --dark-color: #212529; - --light-color: #F8F9FA; -} - -body { - font-family: 'Poppins', sans-serif; - color: var(--dark-color); -} - -.btn-primary, .btn-primary:hover, .btn-primary:active, .btn-primary:visited { - background: var(--primary-color) !important; - border-color: var(--primary-color) !important; - transition: all 0.3s ease; -} - -.btn-primary:hover { - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(32, 201, 151, 0.2); -} - -.card { - border: none; - border-radius: 0.5rem; - box-shadow: 0 4px 25px rgba(0, 0, 0, 0.1); - transition: all 0.3s ease; -} - -.card:hover { - transform: translateY(-5px); - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15); -} - -.card-img-container { - position: relative; -} - -.card-img-overlay { - position: absolute; - bottom: 0; - left: 0; - right: 0; - background: linear-gradient(to top, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0)); - padding: 1.5rem; -} - -.card-img-overlay .card-title a { - color: white; - text-decoration: none; -} - -.card-img-overlay .badge { - position: absolute; - top: 1rem; - right: 1rem; -} - -.navbar-brand { - letter-spacing: -0.5px; -} - -.logo-img { - vertical-align: middle; -} - -h1, h2, h3, h4, h5, h6 { - font-weight: 700; -} - -.hero-section { - background: linear-gradient(45deg, var(--primary-color), #5be3c2); -} \ No newline at end of file diff --git a/assets/js/challenges.js b/assets/js/challenges.js deleted file mode 100644 index 30c06f1..0000000 --- a/assets/js/challenges.js +++ /dev/null @@ -1,56 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - const difficultyFilter = document.getElementById('difficulty-filter'); - const learningStyleFilter = document.getElementById('learning-style-filter'); - const sortBy = document.getElementById('sort-by'); - const challengeList = document.querySelector('.challenge-list'); - - if (difficultyFilter) { - function fetchChallenges() { - const difficulty = difficultyFilter.value; - const learningStyle = learningStyleFilter.value; - const sort = sortBy.value; - - const formData = new FormData(); - formData.append('difficulty', difficulty); - formData.append('learning_style', learningStyle); - formData.append('sort_by', sort); - - fetch('api/filter_challenges.php', { - method: 'POST', - body: formData - }) - .then(response => response.text()) - .then(data => { - challengeList.innerHTML = data; - }); - } - - difficultyFilter.addEventListener('change', fetchChallenges); - learningStyleFilter.addEventListener('change', fetchChallenges); - sortBy.addEventListener('change', fetchChallenges); - } - - const runCodeBtn = document.getElementById('run-code'); - if (runCodeBtn) { - runCodeBtn.addEventListener('click', function() { - const solution = document.getElementById('solution').value; - const language = document.getElementById('language').value; - const challengeId = document.querySelector('input[name="challenge_id"]').value; - - const formData = new FormData(); - formData.append('solution', solution); - formData.append('language', language); - formData.append('challenge_id', challengeId); - - fetch('api/run_code.php', { - method: 'POST', - body: formData - }) - .then(response => response.text()) - .then(data => { - const outputDiv = document.getElementById('output'); - outputDiv.innerHTML = data; - }); - }); - } -}); diff --git a/assets/pasted-20251129-085704-16fd66ae.png b/assets/pasted-20251129-085704-16fd66ae.png deleted file mode 100644 index 66c82d7..0000000 Binary files a/assets/pasted-20251129-085704-16fd66ae.png and /dev/null differ diff --git a/assets/pasted-20251129-090322-2aed21db.png b/assets/pasted-20251129-090322-2aed21db.png deleted file mode 100644 index d88cce1..0000000 Binary files a/assets/pasted-20251129-090322-2aed21db.png and /dev/null differ diff --git a/assets/pasted-20251129-092052-b4d88d1d.png b/assets/pasted-20251129-092052-b4d88d1d.png deleted file mode 100644 index a0f2458..0000000 Binary files a/assets/pasted-20251129-092052-b4d88d1d.png and /dev/null differ diff --git a/assets/pasted-20251129-095357-cfd84971.png b/assets/pasted-20251129-095357-cfd84971.png deleted file mode 100644 index baed5f4..0000000 Binary files a/assets/pasted-20251129-095357-cfd84971.png and /dev/null differ diff --git a/assets/pasted-20251129-143920-29d52aba.png b/assets/pasted-20251129-143920-29d52aba.png deleted file mode 100644 index ec84f35..0000000 Binary files a/assets/pasted-20251129-143920-29d52aba.png and /dev/null differ diff --git a/assets/pasted-20251129-153129-1e57bee7.png b/assets/pasted-20251129-153129-1e57bee7.png deleted file mode 100644 index 89288a4..0000000 Binary files a/assets/pasted-20251129-153129-1e57bee7.png and /dev/null differ diff --git a/auto_route_engine.php b/auto_route_engine.php new file mode 100644 index 0000000..dc76354 --- /dev/null +++ b/auto_route_engine.php @@ -0,0 +1,72 @@ +prepare(" + SELECT momentum_state + FROM learning_momentum + WHERE roll_no = :roll + LIMIT 1 +"); +$stmt->execute([':roll' => $roll_no]); +$data = $stmt->fetch(); + +$momentum_state = $data['momentum_state'] ?? "Building"; + +/* =============================== + ROUTING RULE ENGINE +================================ */ +switch ($momentum_state) { + + case "Applying": + $next_track = "final_track.php"; + $reason = "You are ready to apply what you’ve learned calmly."; + break; + + case "Stabilizing": + $next_track = "reinforcement_track.php"; + $reason = "Let’s stabilize your understanding before final application."; + break; + + case "Reset": + case "Building": + default: + $next_track = "practice_track.php"; + $reason = "Practice builds confidence. Let’s continue calmly."; + break; +} + +/* =============================== + AUTO REDIRECT +================================ */ +header("Location: $next_track?roll=" . urlencode($roll_no) . + "&name=" . urlencode($name) . + "&concept=" . urlencode($concept_id)); +exit; diff --git a/bulk_download.php b/bulk_download.php new file mode 100644 index 0000000..c80a4bd --- /dev/null +++ b/bulk_download.php @@ -0,0 +1,70 @@ + + + + + +Download Reports + + + + + + +
+ +

Learning Style Reports

+ +$name"; + +} + +?> + +
+ + + \ No newline at end of file diff --git a/certificate.html b/certificate.html new file mode 100644 index 0000000..2e29f34 --- /dev/null +++ b/certificate.html @@ -0,0 +1,48 @@ + + + + + + + +
+

+

CERTIFICATE OF PARTICIPATION

+
+ +

This is to certify that

+ +

STUDENT NAME

+ +

+ has successfully completed the coding challenge track + Python Foundations
+ conducted by RS Learning Lab +

+ + + + + diff --git a/certificate_verify.php b/certificate_verify.php new file mode 100644 index 0000000..83d6507 --- /dev/null +++ b/certificate_verify.php @@ -0,0 +1,137 @@ +prepare(" + SELECT + qa.roll_no, + SUM(qa.is_correct) AS correct_count, + COUNT(*) AS total_count + FROM question_attempts qa + WHERE qa.roll_no = ? + AND qa.track = 'final' + "); + $stmt->execute([$roll_no]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($row && $row['total_count'] > 0) { + $percent = round(($row['correct_count'] / $row['total_count']) * 100); + + if ($percent >= 60) { + $verified = true; + $data = [ + 'roll_no' => $roll_no, + 'concept' => $concept_id, + 'score' => $percent, + 'date' => date("d M Y") + ]; + } else { + $error = "Certificate not eligible (final not passed)."; + } + } else { + $error = "No final assessment found."; + } + } +} +?> + + + + +Certificate Verification + + + + +
+ +

Certificate Verification

+ +
+ + + +
+ + +
+ βœ… Certificate Verified

+ Roll No:
+ Concept:
+ Score: %
+ Status: VALID
+ Date: +
+ + + +
+ ❌ +
+ + +
+ + diff --git a/certificates/assets/certificate_bg.png b/certificates/assets/certificate_bg.png new file mode 100644 index 0000000..f09cc96 Binary files /dev/null and b/certificates/assets/certificate_bg.png differ diff --git a/certificates/assets/logo.png b/certificates/assets/logo.png new file mode 100644 index 0000000..1945bf1 Binary files /dev/null and b/certificates/assets/logo.png differ diff --git a/certificates/assets/signature.png b/certificates/assets/signature.png new file mode 100644 index 0000000..6b1a08d Binary files /dev/null and b/certificates/assets/signature.png differ diff --git a/certificates/certificate_template.php b/certificates/certificate_template.php new file mode 100644 index 0000000..72c7cef --- /dev/null +++ b/certificates/certificate_template.php @@ -0,0 +1,130 @@ + + + + + + + + + + + + +
+ +
+ +
CERTIFICATE OF PARTICIPATION
+
+ +
+

This is to certify that

+ +
+ +

has successfully participated in the

+ +
+ +

conducted by RS Learning Lab

+
+ + + + + + diff --git a/certificates/certificate_tmp.html b/certificates/certificate_tmp.html new file mode 100644 index 0000000..9426244 --- /dev/null +++ b/certificates/certificate_tmp.html @@ -0,0 +1,72 @@ + + + + + + + + +
+ + + +
Certificate ID: {{CERT_ID}}
+
Issue Date: {{ISSUE_DATE}}
+ +
{{NAME}}
+ +
+ + \ No newline at end of file diff --git a/certificates/generate_certificate_pdf.php b/certificates/generate_certificate_pdf.php new file mode 100644 index 0000000..88e2a29 --- /dev/null +++ b/certificates/generate_certificate_pdf.php @@ -0,0 +1,25 @@ +set('isRemoteEnabled', true); +$options->set('chroot', realpath(__DIR__)); + +$dompdf = new Dompdf($options); +$dompdf->loadHtml($html); +$dompdf->setPaper('A4', 'portrait'); +$dompdf->render(); +$dompdf->stream("certificate_$name.pdf", ["Attachment" => false]); \ No newline at end of file diff --git a/certificates/preview_certificate.php b/certificates/preview_certificate.php new file mode 100644 index 0000000..e940dca --- /dev/null +++ b/certificates/preview_certificate.php @@ -0,0 +1,53 @@ + + +
+
+ +
+

Certificate Preview

+

+ This is how the official certificate will appear for students. +

+
+ +
+ + +

Certificate of Participation

+ +

This is to certify that

+ +
Gokul
+ +

+ has actively participated in the RS Learning Lab Coding Challenge Program + and demonstrated consistent learning effort. +

+ +

Demonstrated Skills

+

Logical Thinking Β· Problem Solving Β· Coding Fundamentals

+ +
+ +
Gokula Krishnan
+
+ Founder – RS Learning Lab +
+
+ +
+ +
+ + Download Official Certificate (PDF) + +
+ +
+
+ + diff --git a/certificates/verify.php b/certificates/verify.php new file mode 100644 index 0000000..f819107 --- /dev/null +++ b/certificates/verify.php @@ -0,0 +1,59 @@ + + + + Certificate Verification | RS Learning Lab + + + + +
+

Certificate Verification

+

Enter Certificate ID

+ +
+ + +
+
+ + + diff --git a/certificates/verify_action.php b/certificates/verify_action.php new file mode 100644 index 0000000..ab4beca --- /dev/null +++ b/certificates/verify_action.php @@ -0,0 +1,31 @@ +prepare("SELECT * FROM certificates WHERE certificate_id = ?"); +$stmt->execute([$certificate_id]); + +$cert = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$cert) { + echo "Certificate Not Found"; + exit; +} +?> + +

βœ… Certificate Verified

+

Student:

+

Course:

+

Date:

diff --git a/challenge.php b/challenge.php deleted file mode 100644 index 19cd90d..0000000 --- a/challenge.php +++ /dev/null @@ -1,164 +0,0 @@ -prepare('SELECT * FROM challenges WHERE id = ? AND deleted_at IS NULL'); -$stmt->execute([$challenge_id]); -$challenge = $stmt->fetch(); - -if (!$challenge) { - header('Location: challenges.php'); - exit(); -} - -$user_id = $_SESSION['user_id'] ?? null; -$submissions = []; -if ($user_id) { - $stmt = $pdo->prepare('SELECT * FROM challenge_submissions WHERE user_id = ? AND challenge_id = ? ORDER BY submitted_at DESC'); - $stmt->execute([$user_id, $challenge_id]); - $submissions = $stmt->fetchAll(); -} - -?> - -
-
-
-
-
-
Submit Your Solution
-
- -
- - -
-
- -
- -
- - -
-
-
-
-
-
-

-

Difficulty: | Learning Style:

-
- -
-
-
-

Description

-

- -

Sample Cases

-
Input:\n" . htmlspecialchars($sample['input']) . "\n";
-                                    echo "Output:\n" . htmlspecialchars($sample['output']) . "\n\n";
-                                }
-                            ?>
- -
-
-
-
-

My Submissions

- - - - - - - - - - - 0) : ?> - - - - - - - - - - - - - - -
AttemptLanguageStatusDate
You have not made any submissions for this challenge yet.
-
-
-
-
-
-
- - - - diff --git a/challenges.php b/challenges.php index d94630d..43b7a21 100644 --- a/challenges.php +++ b/challenges.php @@ -1,114 +1,103 @@ -
-

Coding Challenges

-

Test your skills with our coding challenges.

+ + + + Coding Challenges | RS Learning Lab + + + - -
- -
+

πŸ† Coding Challenges

+

Base to advanced learning path.

+ + + +
+

Select Programming Language

+
+ + +
+
+ + + +
+

Track 1: Practice Track

+

Focus: Syntax, basics, confidence building.

+ + + Not Started

+ Start Practice + + Completed - - -
- -
- - - -
- -
- - -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- -
- query('SELECT * FROM challenges WHERE deleted_at IS NULL ORDER BY created_at DESC'); - $challenges = $stmt->fetchAll(); - - if (count($challenges) > 0) { - foreach ($challenges as $challenge) { - $image_data = pexels_get('https://api.pexels.com/v1/search?query=' . urlencode($challenge['title']) . '&per_page=1'); - if ($image_data && isset($image_data['photos'][0])) { - $image_url = $image_data['photos'][0]['src']['large']; - } else { - $image_url = 'https://via.placeholder.com/600x400'; // Default placeholder - } - - echo '
'; - echo '
'; - echo '
'; - echo '' . htmlspecialchars($challenge['title']) . ''; - echo '
'; - echo '
' . htmlspecialchars($challenge['title']) . '
'; - echo '' . htmlspecialchars($challenge['difficulty']) . ''; - echo '
'; - echo '
'; - echo '
'; - echo '

' . htmlspecialchars(substr($challenge['description'], 0, 100)) . '...

'; - echo '
'; - echo '' . htmlspecialchars($challenge['learning_style']) . ''; - echo '
'; - echo '
'; - echo '
'; - echo '
'; - } - } else { - echo '
No challenges available yet. Please check back later.
'; - } - ?> -
- + +
+

Track 2: Reinforcement Track

+

Focus: Logic strengthening.

+ + + Locked + + Start Reinforcement + + Completed
+ πŸ… Learning Badge Earned + +
+ + +
+

Track 3: Final Track

+

Focus: End-to-end problem solving.

+ + + Locked + + Start Final Track + + Completed
+ πŸ“œ Track Completion Certificate Unlocked + +
+ + +
+

Applied Thinking (Class 11–12 CS)

+ + + Start Applied Thinking + + Only for Class 11–12 CS + +
+ +← Back to Dashboard + + + diff --git a/change_password.php b/change_password.php new file mode 100644 index 0000000..6d5b5ba --- /dev/null +++ b/change_password.php @@ -0,0 +1,63 @@ +prepare(" + UPDATE institutions + SET password = ? + WHERE id = ? + "); + $stmt->execute([$hash, $_SESSION['institution_id']]); + + $success = "Password updated successfully"; + } +} +?> + + + + + Change Password | RS Learning Lab + + + +

Change Password

+ + +

+ + + +

+Go to Dashboard + + +
+

+

+ +
+ + + diff --git a/check_certificate_unlock.php b/check_certificate_unlock.php new file mode 100644 index 0000000..5541f9a --- /dev/null +++ b/check_certificate_unlock.php @@ -0,0 +1,40 @@ +prepare(" + SELECT COUNT(DISTINCT challenge_id) AS completed + FROM challenge_submissions + WHERE student_id = ? AND is_correct = 1 +"); +$stmt->bind_param("i", $student_id); +$stmt->execute(); +$result = $stmt->get_result()->fetch_assoc(); + +$completed = $result['completed']; + +// Rule: 5 challenges +if ($completed >= 5) { + + // Check already issued? + $check = $conn->prepare(" + SELECT id FROM certificates + WHERE student_id = ? AND certificate_type = 'Participation' + "); + $check->bind_param("i", $student_id); + $check->execute(); + $checkResult = $check->get_result(); + + if ($checkResult->num_rows == 0) { + // Issue certificate + $insert = $conn->prepare(" + INSERT INTO certificates (student_id, certificate_type, track_name) + VALUES (?, 'Participation', ?) + "); + $insert->bind_param("is", $student_id, $track); + $insert->execute(); + } +} diff --git a/check_professional_certificate.php b/check_professional_certificate.php new file mode 100644 index 0000000..a3a2d78 --- /dev/null +++ b/check_professional_certificate.php @@ -0,0 +1,24 @@ +"; +print_r($_SESSION); diff --git a/choose_assessment_mode.php b/choose_assessment_mode.php new file mode 100644 index 0000000..7b1c7cb --- /dev/null +++ b/choose_assessment_mode.php @@ -0,0 +1,103 @@ + + + + + + Choose Assessment Mode | RS Learning Lab + + + + + + + + + diff --git a/class_report.php b/class_report.php new file mode 100644 index 0000000..f40fc9c --- /dev/null +++ b/class_report.php @@ -0,0 +1,116 @@ +prepare(" +SELECT student_roll, student_name, primary_style, secondary_style, pdf_path +FROM learning_style_results +ORDER BY student_roll ASC +"); + +$stmt->execute(); +$students = $stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + + + +
+ +

Class Learning Style Report

+ +

+Teacher: | +Class: +

+ + + + + + + + + + + + + + + + + + + 0): ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Roll NoStudent NamePrimary StyleSecondary StyleReport
+ + + + +Download PDF + + + + +Not Generated + + + +
No assessment data available
+ + +
+ + + +
+ + + +← Back to Dashboard + + +
+ + + \ No newline at end of file diff --git a/class_summary.php b/class_summary.php new file mode 100644 index 0000000..95b870d --- /dev/null +++ b/class_summary.php @@ -0,0 +1,44 @@ +prepare(" + SELECT learning_style, COUNT(*) as count + FROM learning_styles + WHERE user_id = ? + GROUP BY learning_style +"); +$stmt->execute([$_SESSION['user_id']]); +$summary = $stmt->fetchAll(PDO::FETCH_ASSOC); +?> + + + +
+ +

Class Learning Style Summary

+

+ Overview of how students in your class prefer to learn. +

+ +
+ + +
+

+

students

+
+ + +
+ + ← Back to Dashboard +
+ + + diff --git a/code_arena/.env.example b/code_arena/.env.example new file mode 100644 index 0000000..7a550fe --- /dev/null +++ b/code_arena/.env.example @@ -0,0 +1,9 @@ +# GEMINI_API_KEY: Required for Gemini AI API calls. +# AI Studio automatically injects this at runtime from user secrets. +# Users configure this via the Secrets panel in the AI Studio UI. +GEMINI_API_KEY="MY_GEMINI_API_KEY" + +# APP_URL: The URL where this applet is hosted. +# AI Studio automatically injects this at runtime with the Cloud Run service URL. +# Used for self-referential links, OAuth callbacks, and API endpoints. +APP_URL="MY_APP_URL" diff --git a/code_arena/.gitignore b/code_arena/.gitignore new file mode 100644 index 0000000..5a86d2a --- /dev/null +++ b/code_arena/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +build/ +dist/ +coverage/ +.DS_Store +*.log +.env* +!.env.example diff --git a/code_arena/README.md b/code_arena/README.md new file mode 100644 index 0000000..01ae49d --- /dev/null +++ b/code_arena/README.md @@ -0,0 +1,20 @@ +
+GHBanner +
+ +# Run and deploy your AI Studio app + +This contains everything you need to run your app locally. + +View your app in AI Studio: https://ai.studio/apps/d9855fda-dc07-40f0-9cd7-f15e32e565e5 + +## Run Locally + +**Prerequisites:** Node.js + + +1. Install dependencies: + `npm install` +2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key +3. Run the app: + `npm run dev` diff --git a/code_arena/index.html b/code_arena/index.html new file mode 100644 index 0000000..1ece409 --- /dev/null +++ b/code_arena/index.html @@ -0,0 +1,14 @@ + + + + + + + RS LEARNING LAB + + +
+ + + + diff --git a/code_arena/metadata.json b/code_arena/metadata.json new file mode 100644 index 0000000..54a98d3 --- /dev/null +++ b/code_arena/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "RS Learning Lab - Coding Challenge System", + "description": "A gamified coding challenge platform with levels, tracks, mini-projects, and dynamic certificate generation.", + "requestFramePermissions": [] +} diff --git a/code_arena/package-lock.json b/code_arena/package-lock.json new file mode 100644 index 0000000..64eae73 --- /dev/null +++ b/code_arena/package-lock.json @@ -0,0 +1,4795 @@ +{ + "name": "react-example", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "react-example", + "version": "0.0.0", + "dependencies": { + "@google/genai": "^1.29.0", + "@tailwindcss/vite": "^4.1.14", + "@types/canvas-confetti": "^1.9.0", + "@vitejs/plugin-react": "^5.0.4", + "canvas-confetti": "^1.9.4", + "clsx": "^2.1.1", + "docx": "^9.6.1", + "dotenv": "^17.2.3", + "express": "^4.21.2", + "file-saver": "^2.0.5", + "framer-motion": "^12.38.0", + "html2canvas": "^1.4.1", + "jspdf": "^4.2.1", + "lucide-react": "^0.546.0", + "motion": "^12.23.24", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "tailwind-merge": "^3.5.0", + "vite": "^6.2.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^22.14.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "autoprefixer": "^10.4.21", + "tailwindcss": "^4.1.14", + "tsx": "^4.21.0", + "typescript": "~5.8.2", + "vite": "^6.2.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@google/genai": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.46.0.tgz", + "integrity": "sha512-ewPMN5JkKfgU5/kdco9ZhXBHDPhVqZpMQqIFQhwsHLf8kyZfx1cNpw1pHo1eV6PGEW7EhIBFi3aYZraFndAXqg==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "p-retry": "^4.6.2", + "protobufjs": "^7.5.4", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.25.2" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", + "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", + "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", + "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", + "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", + "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", + "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", + "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", + "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", + "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", + "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", + "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", + "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", + "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", + "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", + "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", + "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", + "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", + "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", + "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", + "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", + "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", + "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", + "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", + "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", + "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", + "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "tailwindcss": "4.2.2" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/canvas-confetti": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@types/canvas-confetti/-/canvas-confetti-1.9.0.tgz", + "integrity": "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==", + "license": "MIT" + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001774", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.11", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.11.tgz", + "integrity": "sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001781", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", + "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/canvas-confetti": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.4.tgz", + "integrity": "sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==", + "license": "ISC", + "funding": { + "type": "donate", + "url": "https://www.paypal.me/kirilvatev" + } + }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/docx": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/docx/-/docx-9.6.1.tgz", + "integrity": "sha512-ZJja9/KBUuFC109sCMzovoq2GR2wCG/AuxivjA+OHj/q0TEgJIm3S7yrlUxIy3B+bV8YDj/BiHfWyrRFmyWpDQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^25.2.3", + "hash.js": "^1.1.7", + "jszip": "^3.10.1", + "nanoid": "^5.1.3", + "xml": "^1.0.1", + "xml-js": "^1.6.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/docx/node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/docx/node_modules/nanoid": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", + "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/docx/node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/dompurify": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", + "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optional": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.328", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz", + "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==", + "license": "ISC" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "devOptional": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "license": "MIT", + "dependencies": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz", + "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.38.0", + "motion-utils": "^12.36.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", + "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/google-auth-library": { + "version": "10.6.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", + "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.1.4", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", + "license": "MIT" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jspdf": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.2.1.tgz", + "integrity": "sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "fast-png": "^6.2.0", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.3.1", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.546.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.546.0.tgz", + "integrity": "sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/motion": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.38.0.tgz", + "integrity": "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==", + "license": "MIT", + "dependencies": { + "framer-motion": "^12.38.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz", + "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.36.0" + } + }, + "node_modules/motion-utils": { + "version": "12.36.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz", + "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT", + "optional": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "optional": true + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, + "node_modules/rollup": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", + "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.0", + "@rollup/rollup-android-arm64": "4.60.0", + "@rollup/rollup-darwin-arm64": "4.60.0", + "@rollup/rollup-darwin-x64": "4.60.0", + "@rollup/rollup-freebsd-arm64": "4.60.0", + "@rollup/rollup-freebsd-x64": "4.60.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", + "@rollup/rollup-linux-arm-musleabihf": "4.60.0", + "@rollup/rollup-linux-arm64-gnu": "4.60.0", + "@rollup/rollup-linux-arm64-musl": "4.60.0", + "@rollup/rollup-linux-loong64-gnu": "4.60.0", + "@rollup/rollup-linux-loong64-musl": "4.60.0", + "@rollup/rollup-linux-ppc64-gnu": "4.60.0", + "@rollup/rollup-linux-ppc64-musl": "4.60.0", + "@rollup/rollup-linux-riscv64-gnu": "4.60.0", + "@rollup/rollup-linux-riscv64-musl": "4.60.0", + "@rollup/rollup-linux-s390x-gnu": "4.60.0", + "@rollup/rollup-linux-x64-gnu": "4.60.0", + "@rollup/rollup-linux-x64-musl": "4.60.0", + "@rollup/rollup-openbsd-x64": "4.60.0", + "@rollup/rollup-openharmony-arm64": "4.60.0", + "@rollup/rollup-win32-arm64-msvc": "4.60.0", + "@rollup/rollup-win32-ia32-msvc": "4.60.0", + "@rollup/rollup-win32-x64-gnu": "4.60.0", + "@rollup/rollup-win32-x64-msvc": "4.60.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/tailwind-merge": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "license": "MIT" + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + } + } +} diff --git a/code_arena/package.json b/code_arena/package.json new file mode 100644 index 0000000..35918b5 --- /dev/null +++ b/code_arena/package.json @@ -0,0 +1,45 @@ +{ + "name": "react-example", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "clean": "rm -rf dist", + "lint": "tsc --noEmit" + }, + "dependencies": { + "@google/genai": "^1.29.0", + "@tailwindcss/vite": "^4.1.14", + "@types/canvas-confetti": "^1.9.0", + "@vitejs/plugin-react": "^5.0.4", + "canvas-confetti": "^1.9.4", + "clsx": "^2.1.1", + "docx": "^9.6.1", + "dotenv": "^17.2.3", + "express": "^4.21.2", + "file-saver": "^2.0.5", + "framer-motion": "^12.38.0", + "html2canvas": "^1.4.1", + "jspdf": "^4.2.1", + "lucide-react": "^0.546.0", + "motion": "^12.23.24", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "tailwind-merge": "^3.5.0", + "vite": "^6.2.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^22.14.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "autoprefixer": "^10.4.21", + "tailwindcss": "^4.1.14", + "tsx": "^4.21.0", + "typescript": "~5.8.2", + "vite": "^6.2.0" + } +} diff --git a/code_arena/server.ts b/code_arena/server.ts new file mode 100644 index 0000000..23b9836 --- /dev/null +++ b/code_arena/server.ts @@ -0,0 +1,179 @@ +import express from "express"; +import path from "path"; +import { createServer as createViteServer } from "vite"; + +async function startServer() { + const app = express(); + const PORT = 3000; + + app.use(express.json()); + + // In-memory "database" + const db = { + users: [ + { id: "admin1", username: "admin", password: "password", role: "admin", name: "Administrator" }, + { id: "teacher1", username: "teacher1", password: "password", role: "teacher", name: "Gokula Krishnan", assignedGrade: "12" }, + { id: "teacher2", username: "teacher2", password: "password", role: "teacher", name: "Arun Kumar", assignedGrade: "10" }, + { id: "teacher3", username: "teacher3", password: "password", role: "teacher", name: "Meera Devi", assignedGrade: "11" }, + { id: "teacher4", username: "teacher4", password: "password", role: "teacher", name: "Senthil Nathan", assignedGrade: "9" } + ], + submissions: [] as any[], + students: [] as any[], + studentStats: {} as Record // rollNumber -> latest LEI stats + }; + + // Auth + app.post("/api/login", (req, res) => { + const { username, password } = req.body; + const user = db.users.find(u => u.username === username && u.password === password); + if (user) { + res.json({ id: user.id, username: user.username, role: user.role, name: user.name, assignedGrade: (user as any).assignedGrade }); + } else { + res.status(401).json({ success: false, message: "Invalid credentials" }); + } + }); + + // Submissions + app.post("/api/submissions", (req, res) => { + const submission = { + id: Date.now().toString(), + ...req.body, + status: "pending", + submittedAt: new Date().toISOString(), + documentationMarks: 0, + vivaScore: 0, + understandingLevel: 0, + explanationAbility: 0, + teacherRemarks: "", + lei: req.body.lei || 0, + leiCategory: req.body.leiCategory || 'Stalled Learner', + leiInsight: req.body.leiInsight || '', + retries: req.body.retries || 0, + streak: req.body.streak || 0, + completionRate: req.body.completionRate || 0 + }; + db.submissions.push(submission); + res.json({ success: true, submission }); + }); + + app.get("/api/submissions", (req, res) => { + const { grade } = req.query; + let filtered = db.submissions; + if (grade) { + filtered = filtered.filter(s => s.grade.includes(grade as string)); + } + res.json(filtered); + }); + + app.post("/api/student/lei", (req, res) => { + const { rollNumber, lei, leiCategory, leiInsight, studentName, grade } = req.body; + if (!rollNumber) return res.status(400).json({ success: false, message: "Roll number required" }); + + db.studentStats[rollNumber] = { + lei, + leiCategory, + leiInsight, + studentName, + grade, + updatedAt: new Date().toISOString() + }; + + res.json({ success: true }); + }); + + app.get("/api/students/lei", (req, res) => { + const { grade } = req.query; + let stats = Object.values(db.studentStats); + if (grade) { + stats = stats.filter(s => s.grade.includes(grade as string)); + } + res.json(stats); + }); + + app.post("/api/student/viva", (req, res) => { + const { rollNumber, session, vivaScore } = req.body; + if (!rollNumber) return res.status(400).json({ success: false, message: "Roll number required" }); + + if (db.studentStats[rollNumber]) { + if (!db.studentStats[rollNumber].vivaSessions) { + db.studentStats[rollNumber].vivaSessions = []; + } + db.studentStats[rollNumber].vivaSessions.push(session); + db.studentStats[rollNumber].vivaSummary = session.summary; + db.studentStats[rollNumber].vivaScore = vivaScore; + } + + res.json({ success: true }); + }); + + app.patch("/api/submissions/:id", (req, res) => { + const { id } = req.params; + const { + documentationMarks, vivaScore, teacherVivaMarks, vivaRequested, + teacherRemarks, status, marks, totalScore + } = req.body; + const index = db.submissions.findIndex(s => s.id === id); + if (index !== -1) { + if (documentationMarks !== undefined) db.submissions[index].documentationMarks = documentationMarks; + if (vivaScore !== undefined) db.submissions[index].vivaScore = vivaScore; + if (teacherVivaMarks !== undefined) db.submissions[index].teacherVivaMarks = teacherVivaMarks; + if (vivaRequested !== undefined) db.submissions[index].vivaRequested = vivaRequested; + if (teacherRemarks !== undefined) db.submissions[index].teacherRemarks = teacherRemarks; + if (marks !== undefined) db.submissions[index].marks = marks; + if (totalScore !== undefined) db.submissions[index].totalScore = totalScore; + db.submissions[index].status = status || "reviewed"; + + res.json({ success: true, submission: db.submissions[index] }); + } else { + res.status(404).json({ success: false, message: "Submission not found" }); + } + }); + + // Leaderboard + app.get("/api/leaderboard", (req, res) => { + const { grade } = req.query; + let filtered = db.submissions; + if (grade) { + filtered = filtered.filter(s => s.grade.includes(grade as string)); + } + const leaderboard = filtered + .sort((a, b) => (b.lei || 0) - (a.lei || 0)) + .slice(0, 10) + .map(s => { + let label = "Active Learner"; + if (s.lei > 100) label = "Top Contributor"; + else if (s.lei > 50) label = "Most Consistent"; + else if (s.lei > 20) label = "Rising Star"; + + return { + name: s.studentName, + lei: s.lei || 0, + grade: s.grade, + rollNumber: s.rollNumber, + label: label + }; + }); + res.json(leaderboard); + }); + + // Vite middleware for development + if (process.env.NODE_ENV !== "production") { + const vite = await createViteServer({ + server: { middlewareMode: true }, + appType: "spa", + }); + app.use(vite.middlewares); + } else { + const distPath = path.join(process.cwd(), 'dist'); + app.use(express.static(distPath)); + app.get('*', (req, res) => { + res.sendFile(path.join(distPath, 'index.html')); + }); + } + + app.listen(PORT, "0.0.0.0", () => { + console.log(`Server running on http://localhost:${PORT}`); + }); +} + +startServer(); diff --git a/code_arena/src/App.tsx b/code_arena/src/App.tsx new file mode 100644 index 0000000..bcdb97d --- /dev/null +++ b/code_arena/src/App.tsx @@ -0,0 +1,2563 @@ +import React from 'react'; +import { ErrorAnalyzerView } from './components/ErrorAnalyzerView'; +import { VivaSimulator } from './components/VivaSimulator'; +import { calculateLEI } from './lib/lei'; +import { analyzeError, type ErrorAnalysis } from './lib/errorAnalyzer'; +import { motion, AnimatePresence } from 'framer-motion'; +import { generateProjectDoc } from './lib/docGenerator'; +import { detectPlagiarism } from './lib/plagiarismDetector'; +import { analyzeDocumentation } from './lib/vivaSimulator'; +import logo from './assets/logo.png'; +import { + CheckCircle2, Trophy, ArrowRight, Star, BookOpen, Code2, Award, + Download, User, LogOut, Rocket, Flame, Target, Brain, Laptop, + Crown, Lock, Info, X, FileText, Upload, Play, LayoutDashboard, Users, Clock, AlertTriangle, MessageSquare +} from 'lucide-react'; +import { + LEVELS, TRACKS, QUESTION_BANK, PROJECTS, BADGES, + type LevelId, type TrackId, type Question, type UserProgress, type Project, type Badge, type User as UserType, type EffortLevel, type VivaSession +} from './types'; +import { cn } from './lib/utils'; +import confetti from 'canvas-confetti'; +//import { GoogleGenAI } from "@google/genai"; +import { generateCertificate } from './lib/certificate'; +import { TeacherDashboard } from './components/TeacherDashboard'; +import { Leaderboard } from './components/Leaderboard'; + +const ICON_MAP: Record = { + Rocket, Flame, Target, Brain, Trophy, Laptop, Crown, Star +}; + +const parseGrade = (input: string): number | null => { + const match = input.match(/\d+/); + return match ? parseInt(match[0], 10) : null; +}; + +export default function App() { + const [progress, setProgress] = React.useState(() => { + const saved = localStorage.getItem('rs_learning_progress'); + if (saved) { + const parsed = JSON.parse(saved); + // Migrate old data if necessary + return { + xp: parsed.xp || 0, + completedTracks: parsed.completedTracks || {}, + completedProjects: parsed.completedProjects || [], + badges: parsed.badges || [], + streak: parsed.streak || 0, + lastActivityDate: parsed.lastActivityDate || null, + questionsSolved: parsed.questionsSolved || 0, + name: parsed.name || '', + grade: parsed.grade || '', + rollNumber: parsed.rollNumber || '', + language: parsed.language || undefined, + timeSpent: parsed.timeSpent || 0, + totalAttempts: parsed.totalAttempts || 0, + hintsUsed: parsed.hintsUsed || 0, + retries: parsed.retries || 0, + effortLevel: parsed.effortLevel || 'Beginner', + lastLoginDate: parsed.lastLoginDate || new Date().toISOString(), + activityLog: parsed.activityLog || [], + answeredQuestionIds: parsed.answeredQuestionIds || [], + schoolName: parsed.schoolName || localStorage.getItem('rs_school_name') || '', + principalName: parsed.principalName || localStorage.getItem('rs_principal_name') || '', + assignedProject: parsed.assignedProject || undefined, + projectStatus: parsed.projectStatus || 'not-started' + }; + } + return { + xp: 0, + completedTracks: {}, + completedProjects: [], + badges: [], + streak: 0, + lastActivityDate: null, + questionsSolved: 0, + name: '', + grade: '', + rollNumber: '', + language: undefined, + timeSpent: 0, + totalAttempts: 0, + hintsUsed: 0, + effortLevel: 'Beginner', + lastLoginDate: new Date().toISOString(), + activityLog: [], + answeredQuestionIds: [], + schoolName: localStorage.getItem('rs_school_name') || '', + principalName: localStorage.getItem('rs_principal_name') || '', + projectStatus: 'not-started' + }; + }); + + const [view, setView] = React.useState<'onboarding' | 'dashboard' | 'challenge' | 'project' | 'project-intro' | 'badges' | 'coding' | 'submission' | 'project-submission' | 'teacher' | 'leaderboard' | 'login'>('onboarding'); + const [user, setUser] = React.useState(() => { + const saved = localStorage.getItem('rs_learning_user'); + return saved ? JSON.parse(saved) : null; + }); + const [loginForm, setLoginForm] = React.useState({ username: '', password: '' }); + const [loginError, setLoginError] = React.useState(''); + const [activeLevel, setActiveLevel] = React.useState(1); + const [activeTrack, setActiveTrack] = React.useState('basics'); + const [activeProject, setActiveProject] = React.useState(null); + const [newBadge, setNewBadge] = React.useState(null); + const [showLanguageSelector, setShowLanguageSelector] = React.useState(false); + const [isGeneratingProject, setIsGeneratingProject] = React.useState(false); + const [docContent, setDocContent] = React.useState({ + objective: '', + conceptsUsed: '', + howItWorks: '', + whatILearned: '' + }); + + // Challenge State + const [currentQuestionIndex, setCurrentQuestionIndex] = React.useState(0); + const [selectedOption, setSelectedOption] = React.useState(null); + const [isAnswered, setIsAnswered] = React.useState(false); + const [showHint, setShowHint] = React.useState(false); + const [wrongAttempts, setWrongAttempts] = React.useState(0); + const [questions, setQuestions] = React.useState([]); + + // Coding State + const [code, setCode] = React.useState(''); + const [evaluationResult, setEvaluationResult] = React.useState<{ success: boolean; message: string } | null>(null); + const [errorAnalysis, setErrorAnalysis] = React.useState(null); + const [currentHintLevel, setCurrentHintLevel] = React.useState(0); // 0: none, 1: small, 2: guidance, 3: full + const [isEvaluating, setIsEvaluating] = React.useState(false); + const [isSubmitting, setIsSubmitting] = React.useState(false); + + // Submission State + const [docText, setDocText] = React.useState(''); + const [trackStartTime, setTrackStartTime] = React.useState(null); + const [trackAttempts, setTrackAttempts] = React.useState(0); + const [trackHintsUsed, setTrackHintsUsed] = React.useState(0); + const [trackRetries, setTrackRetries] = React.useState(0); + const [showViva, setShowViva] = React.useState(false); + const [vivaRequestedSubmissions, setVivaRequestedSubmissions] = React.useState([]); + + React.useEffect(() => { + const checkVivaRequests = async () => { + if (!progress.rollNumber) return; + try { + const res = await fetch('/api/submissions'); + if (res.ok) { + const allSubmissions = await res.json(); + const requested = allSubmissions.filter((s: any) => s.rollNumber === progress.rollNumber && s.vivaRequested); + setVivaRequestedSubmissions(requested); + } + } catch (err) { + console.error("Failed to check viva requests:", err); + } + }; + + if (view === 'dashboard') { + checkVivaRequests(); + } + }, [view, progress.rollNumber]); + + React.useEffect(() => { + if (user?.role === 'teacher' && view === 'teacher') { + // Teacher panel logic + } else if (user?.role !== 'teacher' && view === 'teacher') { + setView('dashboard'); + } + }, [view, user]); + + const handleVivaComplete = async (session: VivaSession) => { + if (!progress.rollNumber) return; + + const correctCount = session.questions.filter(q => q.evaluation?.isCorrect).length; + const vivaScore = Math.min(5, Math.round((correctCount / session.questions.length) * 5)); + + try { + // Update student progress + await fetch('/api/student/viva', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + rollNumber: progress.rollNumber, + session, + vivaScore + }) + }); + + // Also update the latest submission with the viva score + const submissionsRes = await fetch('/api/submissions'); + if (submissionsRes.ok) { + const allSubmissions = await submissionsRes.json(); + const latestSub = allSubmissions + .filter((s: any) => s.rollNumber === progress.rollNumber && s.track === 'project') + .sort((a: any, b: any) => new Date(b.submittedAt).getTime() - new Date(a.submittedAt).getTime())[0]; + + if (latestSub) { + const updatedMarks = latestSub.marks ? { + ...latestSub.marks, + viva: { + ...latestSub.marks.viva, + aiScore: vivaScore, + total: vivaScore + (latestSub.marks.viva?.teacherScore || 0) + }, + total: (latestSub.marks.documentation?.total || 0) + + (latestSub.marks.code?.total || 0) + + vivaScore + (latestSub.marks.viva?.teacherScore || 0) + } : null; + + await fetch(`/api/submissions/${latestSub.id}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + vivaScore, + vivaSummary: session.summary, + marks: updatedMarks + }) + }); + } + } + + setProgress(prev => ({ + ...prev, + vivaSessions: [...(prev.vivaSessions || []), session], + vivaSummary: session.summary, + vivaScore + })); + + setShowViva(false); + confetti({ + particleCount: 150, + spread: 70, + origin: { y: 0.6 }, + colors: ['#0b8c9f', '#000000', '#ffffff'] + }); + } catch (error) { + console.error("Failed to save viva session:", error); + } + }; + + const refreshRecommendations = async () => { + if (!user || user.role === 'teacher' || !progress.rollNumber) return; + try { + const leiResult = await calculateLEI(progress); + + await fetch('/api/student/lei', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + rollNumber: progress.rollNumber, + studentName: progress.name, + grade: progress.grade, + lei: leiResult.score, + leiCategory: leiResult.category, + leiInsight: leiResult.insight + }) + }); + } catch (error) { + console.error("Failed to refresh stats:", error); + } + }; + + React.useEffect(() => { + if (user?.role !== 'teacher' && progress.rollNumber) { + const updateLEIOnServer = async () => { + const leiResult = await calculateLEI(progress); + + // Update LEI + await fetch('/api/student/lei', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + rollNumber: progress.rollNumber, + studentName: progress.name, + grade: progress.grade, + lei: leiResult.score, + leiCategory: leiResult.category, + leiInsight: leiResult.insight + }) + }); + }; + + const interval = setInterval(updateLEIOnServer, 300000); // Every 5 minutes + updateLEIOnServer(); // Initial update + + return () => clearInterval(interval); + } + }, [user, progress.rollNumber, progress.completedTracks, progress.totalAttempts, progress.timeSpent, progress.streak]); + + React.useEffect(() => { + localStorage.setItem('rs_learning_progress', JSON.stringify(progress)); + }, [progress]); + + React.useEffect(() => { + if (user) { + localStorage.setItem('rs_learning_user', JSON.stringify(user)); + } else { + localStorage.removeItem('rs_learning_user'); + } + }, [user]); + + // Streak Logic + React.useEffect(() => { + if (progress.name && view === 'dashboard') { + const today = new Date().toISOString().split('T')[0]; + const lastDate = progress.lastActivityDate ? progress.lastActivityDate.split('T')[0] : null; + + if (lastDate !== today) { + let newStreak = progress.streak; + if (lastDate) { + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + const yesterdayStr = yesterday.toISOString().split('T')[0]; + + if (lastDate === yesterdayStr) { + newStreak += 1; + } else { + newStreak = 1; + } + } else { + newStreak = 1; + } + + setProgress(prev => ({ + ...prev, + streak: newStreak, + lastActivityDate: new Date().toISOString() + })); + + // Check Consistency Badge + if (newStreak >= 3 && !progress.badges.includes('consistency')) { + unlockBadge('consistency'); + } + } + } + }, [view]); + + const unlockBadge = (badgeId: string) => { + if (progress.badges.includes(badgeId)) return; + const badge = BADGES.find(b => b.id === badgeId); + if (badge) { + setNewBadge(badge); + setProgress(prev => ({ + ...prev, + badges: [...prev.badges, badgeId], + xp: prev.xp + 100 // Bonus XP for badge + })); + confetti({ + particleCount: 150, + spread: 100, + origin: { y: 0.3 }, + colors: ['#0b8c9f', '#ff7a00', '#ffffff'] + }); + } + }; + + const startChallenge = (level: LevelId, track: TrackId) => { + if (!progress.language) { + setShowLanguageSelector(true); + return; + } + const bank = QUESTION_BANK[level][progress.language][track]; + + // Filter out already answered questions for this session + // But ensure we have at least some questions if all are answered (reset for new cycle) + let availableQuestions = bank.filter(q => !progress.answeredQuestionIds.includes(q.id)); + + if (availableQuestions.length === 0) { + // Reset answered questions for this specific track if all are done + availableQuestions = [...bank]; + setProgress(prev => ({ + ...prev, + answeredQuestionIds: prev.answeredQuestionIds.filter(id => !bank.some(bq => bq.id === id)) + })); + } + + // Shuffle and pick the required number of questions + const shuffled = [...availableQuestions].sort(() => Math.random() - 0.5); + setQuestions(shuffled); + setCurrentQuestionIndex(0); + setActiveLevel(level); + setActiveTrack(track); + setView('challenge'); + setIsAnswered(false); + setSelectedOption(null); + setShowHint(false); + setWrongAttempts(0); + + // Effort Tracking + setTrackStartTime(Date.now()); + setTrackAttempts(0); + setTrackHintsUsed(0); + }; + + const setLanguage = (lang: any) => { + setProgress(prev => ({ ...prev, language: lang })); + setShowLanguageSelector(false); + }; + + const handleAnswer = (optionIndex: number) => { + if (isAnswered) return; + + setTrackAttempts(prev => prev + 1); + setSelectedOption(optionIndex); + const isCorrect = optionIndex === questions[currentQuestionIndex].correctAnswer; + + if (isCorrect) { + setIsAnswered(true); + const xpGain = activeTrack === 'practice' ? 10 : activeTrack === 'reinforcement' ? 20 : 30; + + setProgress(prev => { + const newSolved = prev.questionsSolved + 1; + if (newSolved === 1 && !prev.badges.includes('starter')) { + setTimeout(() => unlockBadge('starter'), 500); + } + + // Update Effort Level + let newEffortLevel = prev.effortLevel; + if (prev.questionsSolved > 50) newEffortLevel = 'Advanced'; + else if (prev.questionsSolved > 20) newEffortLevel = 'Consistent'; + else if (prev.questionsSolved > 5) newEffortLevel = 'Active'; + + return { + ...prev, + xp: prev.xp + xpGain, + questionsSolved: newSolved, + totalAttempts: prev.totalAttempts + 1, + effortLevel: newEffortLevel as EffortLevel, + answeredQuestionIds: [...prev.answeredQuestionIds, questions[currentQuestionIndex].id] + }; + }); + + confetti({ + particleCount: 40, + spread: 50, + origin: { y: 0.8 }, + colors: ['#0b8c9f', '#ff7a00'] + }); + } else { + setWrongAttempts(prev => prev + 1); + } + }; + + const nextQuestion = () => { + if (currentQuestionIndex < questions.length - 1) { + setCurrentQuestionIndex(prev => prev + 1); + setIsAnswered(false); + setSelectedOption(null); + setShowHint(false); + setWrongAttempts(0); + } else { + // Track Completed + const track = TRACKS.find(t => t.id === activeTrack); + if (track?.codingChallenge) { + setCode(track.codingChallenge.starterCode); + setEvaluationResult(null); + setView('coding'); + return; + } + completeTrack(); + } + }; + + const completeTrack = () => { + const trackKey = `${activeLevel}-${progress.language}-${activeTrack}`; + const bonusXp = activeTrack === 'basics' ? 50 : activeTrack === 'practice' ? 100 : activeTrack === 'reinforcement' ? 200 : 300; + + const timeSpentOnTrack = trackStartTime ? Math.round((Date.now() - trackStartTime) / 60000) : 0; + + setProgress(prev => { + const newCompletedTracks = { ...prev.completedTracks, [trackKey]: true }; + + // Badge Logic + if (activeTrack === 'basics' && !prev.badges.includes('starter')) { + setTimeout(() => unlockBadge('starter'), 500); + } else if (activeTrack === 'practice' && !prev.badges.includes('practice_master')) { + setTimeout(() => unlockBadge('practice_master'), 500); + } else if (activeTrack === 'reinforcement' && !prev.badges.includes('logic_builder')) { + setTimeout(() => unlockBadge('logic_builder'), 500); + } else if (activeTrack === 'final' && !prev.badges.includes('final_challenger')) { + setTimeout(() => unlockBadge('final_challenger'), 500); + } + + // Code Champion Logic + const allTracksInLevel = TRACKS.every(t => newCompletedTracks[`${activeLevel}-${progress.language}-${t.id}`]); + if (allTracksInLevel && !prev.badges.includes(`level_${activeLevel}_complete`)) { + setTimeout(() => unlockBadge('code_champion'), 1000); + } + + return { + ...prev, + completedTracks: newCompletedTracks, + xp: prev.xp + bonusXp, + timeSpent: prev.timeSpent + timeSpentOnTrack, + totalAttempts: prev.totalAttempts + trackAttempts, + hintsUsed: prev.hintsUsed + trackHintsUsed, + retries: prev.retries + trackRetries, + activityLog: [...prev.activityLog, { date: new Date().toISOString(), action: `Completed ${activeTrack} track in Level ${activeLevel}` }] + }; + }); + + if (activeTrack === 'final') { + const project = PROJECTS.find(p => p.level === activeLevel); + if (project) { + setActiveProject(project); + setView('project-intro'); + } else { + setView('dashboard'); + } + } else { + setView('dashboard'); + } + + confetti({ + particleCount: 200, + spread: 120, + origin: { y: 0.6 }, + colors: ['#0b8c9f', '#ff7a00', '#ffffff', '#ffd700'] + }); + }; + + const evaluateCode = async () => { + setIsEvaluating(true); + setEvaluationResult(null); + setErrorAnalysis(null); + + const track = TRACKS.find(t => t.id === activeTrack); + if (!track?.codingChallenge) { + setIsEvaluating(false); + return; + } + + setTrackAttempts(prev => prev + 1); + + try { + // Basic auto-evaluation using Function constructor + const userFn = new Function(`return ${code}`)(); + + let allPassed = true; + let failedTest = null; + for (const test of track.codingChallenge.testCases) { + const result = userFn(...test.input); + if (JSON.stringify(result) !== JSON.stringify(test.expected)) { + allPassed = false; + failedTest = test; + break; + } + } + + if (allPassed) { + setEvaluationResult({ success: true, message: "All test cases passed! Great job." }); + setTimeout(() => setView('submission'), 1500); + } else { + setEvaluationResult({ success: false, message: "Some test cases failed. Analyzing your code..." }); + setTrackRetries(prev => prev + 1); + + // Call AI for error analysis + const analysis = await analyzeError( + code, + JSON.stringify(failedTest?.expected), + JSON.stringify(userFn(...(failedTest?.input || []))), + progress.language || 'JavaScript', + track.codingChallenge.description + ); + setErrorAnalysis(analysis); + } + } catch (error) { + setEvaluationResult({ success: false, message: "Error in code. Analyzing the issue..." }); + setTrackRetries(prev => prev + 1); + + try { + const analysis = await analyzeError( + code, + "Valid execution", + (error as Error).message, + progress.language || 'JavaScript', + track.codingChallenge.description + ); + setErrorAnalysis(analysis); + } catch (aiError) { + setEvaluationResult({ success: false, message: "Error in code: " + (error as Error).message }); + } + } finally { + setIsEvaluating(false); + } + }; + + const handleLogin = async (e: React.FormEvent) => { + e.preventDefault(); + setLoginError(''); + try { + const res = await fetch('/api/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(loginForm) + }); + if (res.ok) { + const userData = await res.json(); + setUser(userData); + setView(userData.role === 'teacher' ? 'teacher' : 'dashboard'); + } else { + setLoginError('Invalid username or password'); + } + } catch (error) { + setLoginError('Server error. Please try again.'); + } + }; + + const handleDocSubmit = async () => { + setIsSubmitting(true); + const timeSpentOnTrack = trackStartTime ? Math.round((Date.now() - trackStartTime) / 60000) : 0; + + try { + const completedTracksCount = Object.values(progress.completedTracks).filter(Boolean).length; + const completionRate = Math.round((completedTracksCount / 12) * 100); + + const updatedProgress = { + ...progress, + timeSpent: progress.timeSpent + timeSpentOnTrack, + totalAttempts: progress.totalAttempts + trackAttempts, + hintsUsed: progress.hintsUsed + trackHintsUsed, + retries: progress.retries + trackRetries, + }; + + const leiResult = await calculateLEI(updatedProgress); + + // Plagiarism Detection + let plagiarismReport = null; + try { + const submissionsRes = await fetch('/api/submissions'); + if (submissionsRes.ok) { + const allSubmissions = await submissionsRes.json(); + const relevantSubmissions = allSubmissions.filter((s: any) => + s.track === activeTrack && + s.level === activeLevel && + s.rollNumber !== progress.rollNumber && + s.code + ); + + const trackData = TRACKS.find(t => t.id === activeTrack); + const problemStatement = trackData?.codingChallenge?.description || trackData?.description || "Coding challenge"; + + plagiarismReport = await detectPlagiarism( + code, + relevantSubmissions, + problemStatement, + progress.language || 'JavaScript' + ); + } + } catch (err) { + console.error("Plagiarism check failed:", err); + } + + const submissionData = { + studentName: progress.name, + rollNumber: progress.rollNumber, + grade: progress.grade, + level: activeLevel, + track: activeTrack, + language: progress.language, + code: code, + aiFeedback: "Great effort! Your documentation shows a good understanding of the concepts. Keep exploring more complex logic.", + docText: docText, + status: 'pending', + timeSpent: timeSpentOnTrack, + attempts: trackAttempts, + hintsUsed: trackHintsUsed, + retries: trackRetries, + streak: progress.streak, + completionRate: completionRate, + lei: leiResult.score, + leiCategory: leiResult.category, + leiInsight: leiResult.insight, + vivaSummary: progress.vivaSummary, + plagiarismReport, + documentationMarks: 0, + vivaScore: 0 + }; + + const res = await fetch('/api/submissions', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(submissionData) + }); + + if (res.ok) { + setIsSubmitting(false); + setDocText(''); + completeTrack(); + } + } catch (error) { + console.error('Submission failed:', error); + setIsSubmitting(false); + } + }; + + const handleProjectSubmit = async (projectId: string) => { + setIsEvaluating(true); + setEvaluationResult(null); + setErrorAnalysis(null); + setCurrentHintLevel(0); + + const project = progress.assignedProject || PROJECTS.find(p => p.id === projectId); + if (!project) return; + + setTrackAttempts(prev => prev + 1); + + try { + // Extract function name from project input format (e.g., "calculate(10, 0, '/')") + const fnName = project.inputFormat.split('(')[0]; + + // Basic auto-evaluation using Function constructor + // We wrap the code and return the expected function by name + const userFn = new Function(` + ${code} + if (typeof ${fnName} !== 'undefined') return ${fnName}; + return null; + `)(); + + if (typeof userFn !== 'function') { + throw new Error(`Function '${fnName}' not found. Ensure you have defined 'function ${fnName}(...) { ... }' without using 'export' or 'import' statements.`); + } + + let allPassed = true; + let failedTest = null; + for (const test of project.testCases) { + const result = userFn(...test.input); + if (JSON.stringify(result) !== JSON.stringify(test.expected)) { + allPassed = false; + failedTest = { input: test.input, expected: test.expected, actual: result }; + break; + } + } + + if (allPassed) { + setEvaluationResult({ success: true, message: "Project Evaluation Successful! All test cases passed." }); + setProgress(prev => ({ ...prev, projectStatus: 'documentation' })); + + setTimeout(() => { + setView('project-submission'); + setEvaluationResult(null); + }, 2000); + } else { + setEvaluationResult({ success: false, message: "Some test cases failed. Analyzing your code..." }); + setTrackRetries(prev => prev + 1); + + // Call AI for error analysis + const analysis = await analyzeError( + code, + JSON.stringify(failedTest?.expected), + JSON.stringify(failedTest?.actual), + progress.language || 'JavaScript', + project.description + ); + setErrorAnalysis(analysis); + } + } catch (error) { + setEvaluationResult({ success: false, message: "Error in code. Analyzing the issue..." }); + setTrackRetries(prev => prev + 1); + + try { + const analysis = await analyzeError( + code, + "Valid execution", + (error as Error).message, + progress.language || 'JavaScript', + project.description + ); + setErrorAnalysis(analysis); + } catch (aiError) { + setEvaluationResult({ success: false, message: "Error in code: " + (error as Error).message }); + } + } finally { + setIsEvaluating(false); + } + }; + + const handleProjectDocSubmit = async () => { + if (!activeProject) return; + setIsSubmitting(true); + const timeSpentOnTrack = trackStartTime ? Math.round((Date.now() - trackStartTime) / 60000) : 0; + + try { + const docText = `Objective: ${docContent.objective}\nConcepts Used: ${docContent.conceptsUsed}\nHow It Works: ${docContent.howItWorks}\nWhat I Learned: ${docContent.whatILearned}`; + + const completedTracksCount = Object.values(progress.completedTracks).filter(Boolean).length; + const completionRate = Math.round((completedTracksCount / 12) * 100); + + const updatedProgress = { + ...progress, + timeSpent: progress.timeSpent + timeSpentOnTrack, + totalAttempts: progress.totalAttempts + trackAttempts, + hintsUsed: progress.hintsUsed + trackHintsUsed, + retries: progress.retries + trackRetries, + }; + + const leiResult = await calculateLEI(updatedProgress); + + // AI Documentation & Code Analysis + const analysis = await analyzeDocumentation({ code, docText }, progress.language || 'JavaScript'); + + // Plagiarism Detection + let plagiarismReport = null; + try { + const submissionsRes = await fetch('/api/submissions'); + if (submissionsRes.ok) { + const allSubmissions = await submissionsRes.json(); + const relevantSubmissions = allSubmissions.filter((s: any) => + s.track === 'project' && + s.level === activeLevel && + s.rollNumber !== progress.rollNumber && + s.code + ); + + plagiarismReport = await detectPlagiarism( + code, + relevantSubmissions, + activeProject.description, + progress.language || 'JavaScript' + ); + } + } catch (err) { + console.error("Plagiarism check failed:", err); + } + + const submissionData = { + studentName: progress.name, + rollNumber: progress.rollNumber, + grade: progress.grade, + schoolName: progress.schoolName, + principalName: progress.principalName, + level: activeLevel, + track: 'project', + language: progress.language, + code: code, + aiFeedback: analysis?.aiFeedback || "Excellent work on the project! Your code passed all test cases, and your documentation provides clear insights into your implementation.", + docText: docText, + status: 'pending', + timeSpent: timeSpentOnTrack, + attempts: trackAttempts, + hintsUsed: trackHintsUsed, + retries: trackRetries, + streak: progress.streak, + completionRate: completionRate, + lei: leiResult.score, + leiCategory: leiResult.category, + leiInsight: leiResult.insight, + vivaSummary: progress.vivaSummary, + plagiarismReport, + marks: analysis?.marks || null, + documentationMarks: analysis?.marks?.documentation?.total || 0, + vivaScore: 0, + teacherVivaMarks: 0 + }; + + const res = await fetch('/api/submissions', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(submissionData) + }); + + if (res.ok) { + setIsSubmitting(false); + setDocText(''); + setDocContent({ objective: '', conceptsUsed: '', howItWorks: '', whatILearned: '' }); + + setProgress(prev => { + const newProjects = [...new Set([...prev.completedProjects, activeProject.id])]; + return { + ...prev, + completedProjects: newProjects, + projectStatus: 'completed', + xp: prev.xp + 500 + }; + }); + + if (!progress.badges.includes('project_builder')) { + unlockBadge('project_builder'); + } + + confetti({ + particleCount: 150, + spread: 100, + origin: { y: 0.6 }, + colors: ['#0b8c9f', '#ff7a00'] + }); + + setView('dashboard'); + } + } catch (error) { + console.error('Project submission failed:', error); + setIsSubmitting(false); + } + }; + + const generateUniqueProject = async () => { + if (!progress.language) return; + setIsGeneratingProject(true); + + try { + // βœ… TEMP: Direct fallback (NO AI) + const fallback = PROJECTS + .filter(p => + p.level === activeLevel && + Number(progress.grade) >= (p.minClass || 0) + )[0]; + setActiveProject(fallback); + setCode(fallback.starterCode); + setProgress(prev => ({ + ...prev, + assignedProject: fallback, + projectStatus: 'coding' + })); + setView('project'); + + } catch (error) { + console.error('Project generation failed:', error); + } finally { + setIsGeneratingProject(false); + } +}; + + const downloadCert = (level: LevelId, track: string, projectName?: string) => { + const skills = level === 1 + ? ['Basic Syntax', 'Variables', 'Print Statements'] + : level === 2 + ? ['Loops', 'Conditions', 'Arrays', 'Logic Building'] + : ['Advanced Algorithms', 'Problem Solving', 'Project Development']; + + generateCertificate( + progress.name, + level, + track, + skills, + level === 3, + progress.grade, + progress.rollNumber, + projectName, + progress.effortLevel, + progress.schoolName, + progress.principalName + ); + }; + + if (view === 'onboarding') { + return ( +
+ +
+
+ Logo { + // Fallback if the URL is still not working + (e.target as HTMLImageElement).src = "https://api.dicebear.com/7.x/bottts/svg?seed=RS"; + }} + /> +
+
+

RS Learning Lab

+

"Learning coding, one step at a time."

+ +
+
+ + setProgress(prev => ({ ...prev, name: e.target.value }))} + placeholder="Enter full name" + className="w-full px-4 py-3 rounded-xl bg-dark-bg border border-dark-border text-white focus:ring-2 focus:ring-[#0b8c9f] focus:border-transparent outline-none transition-all" + /> +
+
+ + setProgress(prev => ({ ...prev, grade: e.target.value }))} + placeholder="e.g. Class 10" + className="w-full px-4 py-3 rounded-xl bg-dark-bg border border-dark-border text-white focus:ring-2 focus:ring-[#0b8c9f] focus:border-transparent outline-none transition-all" + /> +
+
+ + setProgress(prev => ({ ...prev, rollNumber: e.target.value }))} + placeholder="Enter roll number" + className="w-full px-4 py-3 rounded-xl bg-dark-bg border border-dark-border text-white focus:ring-2 focus:ring-[#0b8c9f] focus:border-transparent outline-none transition-all" + /> +
+
+ + setProgress(prev => ({ ...prev, schoolName: e.target.value }))} + placeholder="Enter school name" + className="w-full px-4 py-3 rounded-xl bg-dark-bg border border-dark-border text-white focus:ring-2 focus:ring-[#0b8c9f] focus:border-transparent outline-none transition-all" + /> +
+
+ + setProgress(prev => ({ ...prev, principalName: e.target.value }))} + placeholder="Enter principal name" + className="w-full px-4 py-3 rounded-xl bg-dark-bg border border-dark-border text-white focus:ring-2 focus:ring-[#0b8c9f] focus:border-transparent outline-none transition-all" + /> +
+
+

+ Data Policy: Collected for educational purposes only. +

+ + {!user && ( + + )} +
+
+
+

Powered by RS Learning Lab

+

Founder: Gokula Krishnan

+
+
+
+ ); + } + + return ( +
+ {/* Badge Notification */} + + {newBadge && ( + +
+
+
+ {React.createElement(ICON_MAP[newBadge.icon] || Star, { size: 32 })} +
+
+
New Badge Earned!
+

{newBadge.name}

+

{newBadge.description}

+
+ +
+ + )} + + + {/* Header */} +
+
+
setView('dashboard')}> +
+ Logo { + (e.target as HTMLImageElement).src = "https://api.dicebear.com/7.x/bottts/svg?seed=RS"; + }} + /> +
+ RS Learning Lab +
+ +
+ {user?.role === 'student' && ( + + )} + + {user?.role === 'teacher' && ( +
+ + +
+ )} + +
+ + {progress.streak} Day Streak +
+ +
+
+ + {progress.xp} XP +
+
+ +
+ + +
+
+
+ +
+ + {view === 'login' && ( + +
+
+
+ +
+
+

Teacher Login

+

Access the evaluation dashboard

+ +
+
+ + setLoginForm(prev => ({ ...prev, username: e.target.value }))} + className="w-full px-4 py-3 rounded-xl bg-dark-bg border border-dark-border text-white focus:ring-2 focus:ring-[#0b8c9f] outline-none" + /> +
+
+ + setLoginForm(prev => ({ ...prev, password: e.target.value }))} + className="w-full px-4 py-3 rounded-xl bg-dark-bg border border-dark-border text-white focus:ring-2 focus:ring-[#0b8c9f] outline-none" + /> +
+ {loginError &&

{loginError}

} + + +
+
+
+ )} + + {view === 'teacher' && user?.role === 'teacher' && ( + + { setUser(null); setView('onboarding'); }} /> + + )} + + {view === 'leaderboard' && ( + + +
+ +
+
+ )} + + {showLanguageSelector && ( +
+ +

Select Your Path πŸš€

+

Choose a programming language to begin your learning journey.

+ +
+ {['C', 'C++', 'Java', 'Python', 'JavaScript'].map((lang) => ( + + ))} +
+ + +
+
+ )} + + {view === 'dashboard' && ( + + {vivaRequestedSubmissions.length > 0 && ( + +
+
+ +
+
+

Viva Explanation Requested

+

Your teacher has requested a viva explanation for your recent submission. Practice explaining your code!

+
+
+ +
+ )} + + {/* Welcome & Effort Stats */} +
+
+

Welcome back, {progress.name}! πŸ‘‹

+

You're doing great. Keep up the momentum!

+
+
+ {progress.effortLevel} Learner +
+
+ {progress.timeSpent}m Spent +
+
+
+
+ {progress.streak > 0 && ( +
+
+ +
+
+
Current Streak
+
{progress.streak} Days πŸ”₯
+
+
+ )} +
+
+ +
+
+
Total XP
+
{progress.xp} XP
+
+
+
+
+ + {/* AI Viva Simulator Card - Only shown after documentation submission */} + {progress.projectStatus === 'completed' && ( +
+ +
+
+
+ +
+

AI Viva Simulator

+

+ Practice explaining your project like a real viva. Build confidence and master your technical communication! +

+
+ + +
+ )} + + {showViva && ( + setShowViva(false)} + /> + )} + + {/* Levels */} +
{ + const grade = parseGrade(progress.grade); + if (grade === 12 || (grade && grade >= 11)) return "md:grid-cols-1 max-w-md mx-auto"; + if (grade && grade >= 6 && grade <= 8) return "md:grid-cols-1 max-w-md mx-auto"; + if (grade === 10) return "md:grid-cols-1 max-w-md mx-auto"; + if (grade === 9) return "md:grid-cols-2 max-w-4xl mx-auto"; + return "md:grid-cols-3"; + })() + )}> + {LEVELS.filter(level => { + const grade = parseGrade(progress.grade); + if (!grade) return true; + if (grade === 12 || grade === 11) return level.id >= 3; + if (grade >= 6 && grade <= 8) return level.id === 1; + if (grade === 9) return level.id <= 2; + if (grade === 10) return level.id === 2; + return true; + }).map((level, index, filteredArray) => { + const isFirstVisible = index === 0; + const isUnlocked = isFirstVisible || progress.completedTracks[`${level.id - 1}-${progress.language}-final`]; + const isCompleted = progress.completedTracks[`${level.id}-${progress.language}-final`]; + + // Calculate Level Progress + const levelTracks = TRACKS.map(t => `${level.id}-${progress.language}-${t.id}`); + const completedCount = levelTracks.filter(t => progress.completedTracks[t]).length; + const levelProgress = (completedCount / levelTracks.length) * 100; + const isLevelFullyCompleted = completedCount === levelTracks.length; + + return ( +
+ {isCompleted && ( +
+ +
+ )} +
+ + {level.subtitle} + +

{level.title}

+

{level.description}

+ + {/* Progress Bar */} +
+
+ Progress + {Math.round(levelProgress)}% +
+
+ +
+
+
+ +
+ {TRACKS.map((track) => { + const trackKey = `${level.id}-${progress.language}-${track.id}`; + const isTrackCompleted = progress.completedTracks[trackKey]; + const trackIndex = TRACKS.findIndex(t => t.id === track.id); + const prevTrackKey = trackIndex > 0 ? `${level.id}-${progress.language}-${TRACKS[trackIndex - 1].id}` : null; + const isTrackUnlocked = isUnlocked && (!prevTrackKey || progress.completedTracks[prevTrackKey]); + + return ( + + ); + })} +
+ + {/* Documentation Guide & Mini Projects */} +
+ {/* Documentation Guide - Level 3 Only */} + {level.id === 3 && ( +
+
+
Documentation Guide
+ {!isLevelFullyCompleted && } +
+
+
+
+ +
+
+

Project Documentation Guide

+

Includes Front Page, Certificate, Acknowledgement

+
+
+ +
+
+ )} + +
+
+
Mini Projects
+ {!isLevelFullyCompleted && ( +
+ Complete all tracks to unlock +
+ )} +
+
+ {PROJECTS + .filter(p => + p.level === 3 + ) + .map(p => { + const isProjDone = progress.completedProjects.includes(p.id); + return ( + + ); + })} +
+
+ {isLevelFullyCompleted && progress.completedProjects.some(pid => PROJECTS.find(p => p.id === pid)?.level === level.id) && ( + + )} +
+
+ ); + })} +
+ + {/* Daily Goal & Recent Activity */} +
+
+
+

+ Daily Goal +

+
+ {Math.min(progress.questionsSolved % 5, 5)}/5 Solved +
+
+
+
+ +
+

+ Complete 5 questions today to earn a Consistency Bonus! +

+
+
+ Next Milestone +
+
+
+ +
+
+
Logic Builder
+
Complete Level 1
+
+
+
+
+
+ +
+
+

+ Recent Activity +

+ +
+
+ {progress.activityLog.length > 0 ? ( + progress.activityLog.slice(-4).reverse().map((log, idx) => ( +
+
+
+ +
+
+
{log.action}
+
+ {new Date(log.date).toLocaleDateString()} at {new Date(log.date).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} +
+
+
+
+ {progress.language} +
+
+ )) + ) : ( +
+
+ +
+

No recent activity. Start your first track!

+
+ )} +
+
+
+
+ )} + + {view === 'badges' && ( + +
+
+

Your Achievements πŸ…

+

You've unlocked {progress.badges.length} out of {BADGES.length} badges.

+
+ +
+ +
+ {BADGES.map((badge) => { + const isUnlocked = progress.badges.includes(badge.id); + const Icon = ICON_MAP[badge.icon] || Star; + + return ( + +
+ {isUnlocked ? : } +
+ +

{badge.name}

+

{badge.description}

+ +
+
Condition
+
{badge.condition}
+
+ + {!isUnlocked && ( +
+
+ LOCKED +
+
+ )} +
+ ); + })} +
+
+ )} + + {view === 'challenge' && ( + +
+ +
+
+ Question {currentQuestionIndex + 1} of {questions.length} +
+
+
+
+
+
+ +
+
+
+
+ {questions[currentQuestionIndex].type?.replace('-', ' ') || 'multiple choice'} +
+
+ {questions[currentQuestionIndex].concept} +
+
+

+ {questions[currentQuestionIndex].text} +

+
+ + {questions[currentQuestionIndex].codeSnippet && ( +
+
+ +
+
+                      {questions[currentQuestionIndex].type === 'fill-blanks' ? (
+                        questions[currentQuestionIndex].codeSnippet?.split('_____').map((part, i, arr) => (
+                          
+                            {part}
+                            {i < arr.length - 1 && (
+                              
+                                {isAnswered ? questions[currentQuestionIndex].options[questions[currentQuestionIndex].correctAnswer] : '____'}
+                              
+                            )}
+                          
+                        ))
+                      ) : (
+                        questions[currentQuestionIndex].codeSnippet
+                      )}
+                    
+
+ )} + +
+ {questions[currentQuestionIndex].options.map((option, idx) => { + const isCorrect = idx === questions[currentQuestionIndex].correctAnswer; + const isSelected = selectedOption === idx; + + return ( + + ); + })} +
+ + {/* Execution Visualizer for Basics */} + {activeTrack === 'basics' && questions[currentQuestionIndex].executionSteps && ( +
+
+
+ Execution Visualization +
+ +
+
+ {questions[currentQuestionIndex].executionSteps?.map((step, idx) => ( +
+
+ {step.line} +
+
+

{step.explanation}

+ {step.variables && ( +
+ {Object.entries(step.variables).map(([key, val]) => ( +
+ {key}: {val} +
+ ))} +
+ )} +
+
+ ))} +
+
+ )} + + + {isAnswered && ( + +
+
+ Great job! πŸ”₯ +
+
+ Concept: {questions[currentQuestionIndex].concept} +
+
+

+ {questions[currentQuestionIndex].explanation} +

+ +
+ )} + + {!isAnswered && ( +
+ {((activeTrack === 'practice') || (activeTrack === 'reinforcement' && wrongAttempts > 0) || (activeTrack === 'final' && wrongAttempts > 0)) ? ( + + ) :
} + + {showHint && ( + + πŸ’‘ {questions[currentQuestionIndex].hint} + + )} +
+ )} + +
+ + )} + + {view === 'coding' && ( + +
+
+
+

Coding Challenge

+

+ {TRACKS.find(t => t.id === activeTrack)?.codingChallenge?.title || + TRACKS.find(t => t.id === activeTrack)?.title || + 'Interactive Playground'} +

+
+
+ +
+
+ +
+
+
+

Problem Description

+

+ {TRACKS.find(t => t.id === activeTrack)?.codingChallenge?.description || + "Experiment with the code snippet below and see how it works!"} +

+
+
+
+
Input Format
+ + {TRACKS.find(t => t.id === activeTrack)?.codingChallenge?.inputFormat || "N/A"} + +
+
+
Expected Output
+ + {TRACKS.find(t => t.id === activeTrack)?.codingChallenge?.outputFormat || "N/A"} + +
+
+
+ +
+
+
+
+
+
+
+ solution.js +
+ + + + + + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/coding/submit.php b/coding/submit.php new file mode 100644 index 0000000..107f600 --- /dev/null +++ b/coding/submit.php @@ -0,0 +1,19 @@ +prepare( + "INSERT INTO coding_submissions + (student_name, class, track_id, challenge_no) + VALUES (?, ?, ?, ?)" +); +$stmt->bind_param("ssii", $student_name, $class, $track_id, $challenge_no); +$stmt->execute(); + +header("Location: track_challenges.php?track_id=$track_id"); +exit; diff --git a/coding/submit_project.php b/coding/submit_project.php new file mode 100644 index 0000000..95c0352 --- /dev/null +++ b/coding/submit_project.php @@ -0,0 +1,90 @@ + 120){ + $thinking_type = "🧠 Deep Thinker"; +} +else{ + $thinking_type = "🚢 Balanced Learner"; +} + +/* SAVE TO DB */ +$status = $passed ? 'approved' : 'rejected'; + +$stmt = $pdo->prepare(" + INSERT INTO project_submissions + (student_name, student_id, class, topic_id, score, status, thinking_type) + VALUES (?,?,?,?,?,?,?) +"); + +$stmt->execute([$name, $roll, $class, $topic_id, $score, $status, $thinking_type]); + +/* RESULT UI */ +if($passed){ + echo " +
+

βœ… Project Approved πŸŽ‰

+ +

$thinking_type

+ +

+ You solved this in ".round($time_taken)." seconds +

+ + + πŸ† View Leaderboard + +
+ "; +}else{ + echo " +
+

❌ Improve your logic

+ +

$thinking_type

+ +

+ Try again. You spent ".round($time_taken)." seconds +

+
+ "; +} +?> \ No newline at end of file diff --git a/coding/track_challenges.php b/coding/track_challenges.php new file mode 100644 index 0000000..d876018 --- /dev/null +++ b/coding/track_challenges.php @@ -0,0 +1,52 @@ + "Logic & Problem Solving", + 2 => "Python Foundations", + 3 => "Applied Thinking" +]; + +$trackTitle = $trackNames[$track_id] ?? "Coding Challenges"; +?> + +
+
+ + +
+

+ +

+

+ Complete the challenges in sequence to unlock participation + recognition and skill validation. +

+
+ + +
+ + +
+

Challenge

+

+ This challenge focuses on strengthening core concepts + and applying logical reasoning step by step. +

+
+ +
+
+ + +
+ +
+
+ + diff --git a/coding/tracks.php b/coding/tracks.php new file mode 100644 index 0000000..64b3e4e --- /dev/null +++ b/coding/tracks.php @@ -0,0 +1,71 @@ + + +
+
+ + +
+

+ Coding Challenge Tracks +

+

+ Professionally designed learning tracks to develop logical thinking, + programming fundamentals, and real-world problem-solving skills. +

+
+ + +
+ + +
+

Logic & Problem Solving

+

+ Strengthen reasoning skills using puzzles, patterns, + and step-by-step thinking exercises. +

+ +
+ + +
+

Python Foundations

+

+ Learn programming basics with beginner-friendly + challenges focused on logic and clarity. +

+ +
+ + +
+

Applied Thinking

+

+ Apply concepts to simple real-life problems + and scenario-based coding tasks. +

+ +
+ +
+ +
+
+ + diff --git a/competitions.php b/competitions.php index 82f894d..851dca3 100644 --- a/competitions.php +++ b/competitions.php @@ -1,46 +1,57 @@ query('SELECT * FROM competitions WHERE deleted_at IS NULL ORDER BY start_date DESC'); -$competitions = $stmt->fetchAll(); + +// fetch competitions - prepared statement, safe +try { + $stmt = $pdo->prepare("SELECT id, title, description, start_date, end_date, registration_link FROM competitions ORDER BY start_date DESC"); + $stmt->execute(); + $competitions = $stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (Exception $e) { + // If query fails, show friendly message (we enabled display_errors during debug) + die("Database query failed: " . htmlspecialchars($e->getMessage())); +} + +// include header (will not start a new session because header uses safe check) +include __DIR__ . '/includes/header.php'; ?> -
-

Coding Competitions

-

Join our coding competitions and win exciting prizes.

+

Competitions

-
- 0) : ?> - - -
-
- <?php echo htmlspecialchars($competition['title']); ?> -
-
-

...

-
- Starts: - Ends: -
-
-
-
- - -
No competitions available yet. Please check back later.
- -
-
+ +

No competitions right now. Check back later.

+ + + + + + + + + + + + + + + + + + + + +
TitleDescriptionStartEnd
+ - +"; diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..20e2971 --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "dompdf/dompdf": "^3.1" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..3dfb034 --- /dev/null +++ b/composer.lock @@ -0,0 +1,307 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "00da199c24f69d4e39903c3676bfe248", + "packages": [ + { + "name": "dompdf/dompdf", + "version": "v3.1.4", + "source": { + "type": "git", + "url": "https://github.com/dompdf/dompdf.git", + "reference": "db712c90c5b9868df3600e64e68da62e78a34623" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/db712c90c5b9868df3600e64e68da62e78a34623", + "reference": "db712c90c5b9868df3600e64e68da62e78a34623", + "shasum": "" + }, + "require": { + "dompdf/php-font-lib": "^1.0.0", + "dompdf/php-svg-lib": "^1.0.0", + "ext-dom": "*", + "ext-mbstring": "*", + "masterminds/html5": "^2.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ext-gd": "*", + "ext-json": "*", + "ext-zip": "*", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + }, + "type": "library", + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "The Dompdf Community", + "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" + } + ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v3.1.4" + }, + "time": "2025-10-29T12:43:30+00:00" + }, + { + "name": "dompdf/php-font-lib", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "The FontLib Community", + "homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/dompdf/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/1.0.1" + }, + "time": "2024-12-02T14:37:59+00:00" + }, + { + "name": "dompdf/php-svg-lib", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0", + "sabberworm/php-css-parser": "^8.4" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The SvgLib Community", + "homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/dompdf/php-svg-lib", + "support": { + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0" + }, + "time": "2024-04-29T13:26:35+00:00" + }, + { + "name": "masterminds/html5", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "fcf91eb64359852f00d921887b219479b4f21251" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" + }, + "time": "2025-07-25T09:04:22+00:00" + }, + { + "name": "sabberworm/php-css-parser", + "version": "v8.9.0", + "source": { + "type": "git", + "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9", + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + }, + "require-dev": { + "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41", + "rawr/cross-data-providers": "^2.0.0" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + }, + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Jake Hotson", + "email": "jake.github@qzdesign.co.uk" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "support": { + "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0" + }, + "time": "2025-07-11T13:20:48+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +} diff --git a/computer_quiz.php b/computer_quiz.php new file mode 100644 index 0000000..6e0ff49 --- /dev/null +++ b/computer_quiz.php @@ -0,0 +1,156 @@ +query( + "SELECT * FROM computer_quizzes + WHERE is_active = 1 + ORDER BY created_at DESC + LIMIT 1" +); + +if ($result && $result->num_rows > 0) { + $quiz = $result->fetch_assoc(); +} + +$error = ''; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + + if (!$quiz) { + $error = "Quiz not started yet."; + } else { + $name = trim($_POST['student_name']); + $class = trim($_POST['class']); + $roll = trim($_POST['roll_number']); + + if ($name === '' || $class === '' || $roll === '') { + $error = "All fields are required."; + } else { + + // Save attempt + $stmt = $conn->prepare( + "INSERT INTO computer_quiz_attempts + (quiz_id, student_name, class, roll_number) + VALUES (?, ?, ?, ?)" + ); + $stmt->bind_param( + "isss", + $quiz['id'], + $name, + $class, + $roll + ); + $stmt->execute(); + $attempt_id = $stmt->insert_id; + $stmt->close(); + + // Store attempt in session + $_SESSION['quiz_attempt_id'] = $attempt_id; + $_SESSION['quiz_id'] = $quiz['id']; + + header("Location: computer_quiz_questions.php"); + exit; + } + } +} +?> + + + + +Computer Quiz | RS Learning Lab + + + + + + + +
+ +

Learning Style Quiz

+ + +

Please enter your details to begin.

+ +

No active quiz at the moment.

+ + + +
+ + + +
+ + + + + + + + + + +
+ + +
+ + diff --git a/computer_quiz_questions.php b/computer_quiz_questions.php new file mode 100644 index 0000000..faeb921 --- /dev/null +++ b/computer_quiz_questions.php @@ -0,0 +1,147 @@ + "When learning something new, what helps you most?", + 2 => "If you don’t understand a topic, what do you do first?", + 3 => "You remember things best when you…", + 4 => "In exams, you usually prefer questions that are…", + 5 => "While studying, you are more comfortable with…", + 6 => "When solving problems, you usually…", + 7 => "You feel confident when learning involves…", + 8 => "During group study, you usually…", + 9 => "You understand faster when lessons include…", + 10 => "When instructions are given, you prefer them to be…", + 11 => "You feel bored quickly if learning is…", + 12 => "You prefer teachers who…", + 13 => "While revising, you usually…", + 14 => "You feel learning is effective when it is…", + 15 => "When facing a difficult question, you…", + 16 => "You like assignments that involve…", + 17 => "You stay focused when tasks are…", + 18 => "You learn better from mistakes when…", + 19 => "You feel confident if learning materials are…", + 20 => "Overall, you feel learning is best when it is…" +]; + +// Options (same pattern for all) +$options = [ + 'A' => 'Using diagrams, images or videos', + 'B' => 'Understanding logic and steps', + 'C' => 'Practicing with real examples', + 'D' => 'Thinking deeply and quietly' +]; + +$error = ''; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + + foreach ($questions as $qNo => $qText) { + if (!isset($_POST['q'][$qNo])) { + $error = "Please answer all questions."; + break; + } + } + + if ($error === '') { + + foreach ($_POST['q'] as $qNo => $answer) { + $stmt = $conn->prepare( + "INSERT INTO computer_quiz_answers + (attempt_id, question_no, selected_option) + VALUES (?, ?, ?)" + ); + $stmt->bind_param("iis", $attemptId, $qNo, $answer); + $stmt->execute(); + $stmt->close(); + } + + header("Location: generate_learning_pdf.php"); + exit; + } +} +?> + + + + +Learning Style Quiz + + + + + + + +
+ +

Learning Style Assessment

+ + +
+ + +
+ $text): ?> +
+

.

+ $label): ?> +
+ +
+ +
+ + + +
+ +
+ + diff --git a/config.php b/config.php new file mode 100644 index 0000000..7be4606 --- /dev/null +++ b/config.php @@ -0,0 +1,19 @@ +connect_error) { + die("DB Connection Failed: " . $conn->connect_error); +} + +// Optional: show errors while developing +mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); +?> diff --git a/config/db.php b/config/db.php new file mode 100644 index 0000000..742bd93 --- /dev/null +++ b/config/db.php @@ -0,0 +1,19 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC + ] + ); +} catch (PDOException $e) { + die("Database connection failed: " . $e->getMessage()); +} diff --git a/dashboard.php b/dashboard.php new file mode 100644 index 0000000..4329c54 --- /dev/null +++ b/dashboard.php @@ -0,0 +1,29 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC + ] + ); +} catch (PDOException $e) { + die("DB connection failed"); +} + +/* =============================== + INPUT +================================ */ +$roll_no = $_GET['roll'] ?? null; + +if (!$roll_no) { + die("Student not specified"); +} + +/* =============================== + FETCH LEARNING PROFILE +================================ */ +$stmt = $pdo->prepare(" + SELECT * + FROM learning_profiles + WHERE roll_no = :roll + LIMIT 1 +"); +$stmt->execute([':roll' => $roll_no]); +$profile = $stmt->fetch(); + +if (!$profile) { + die("Learning profile not found"); +} + +$style_scores = json_decode($profile['style_scores'], true) ?? []; +$learning_signals = json_decode($profile['learning_signals'], true) ?? []; + +/* =============================== + FETCH MOMENTUM +================================ */ +$stmt = $pdo->prepare(" + SELECT momentum_score, momentum_state, updated_at + FROM learning_momentum + WHERE roll_no = :roll + LIMIT 1 +"); +$stmt->execute([':roll' => $roll_no]); +$momentum = $stmt->fetch(); + +$momentum_score = $momentum['momentum_score'] ?? 50; +$momentum_state = $momentum['momentum_state'] ?? "Building"; +$momentum_updated = $momentum['updated_at'] ?? null; + +/* =============================== + MOMENTUM UI MAPPING +================================ */ +$momentum_color = [ + "Applying" => "#22c55e", + "Stabilizing" => "#38bdf8", + "Building" => "#facc15", + "Reset" => "#ef4444" +]; + +$momentum_text = [ + "Applying" => "Applying concepts confidently", + "Stabilizing" => "Understanding is settling steadily", + "Building" => "Learning habit is forming", + "Reset" => "Needs calm practice reinforcement" +]; + +$bar_color = $momentum_color[$momentum_state] ?? "#38bdf8"; +?> + + + + +Student Dashboard – RS Learning Lab + + + + + +
+ +

Student Learning Dashboard

+ +
+ + +
+

Student Name

+

+ +

Roll No

+

+ +

Class

+

+
+ + +
+

Learning Style

+ + +
+ + +
+

Learning Effectiveness Index

+ + +
+ + +
+

Learning Momentum

+ +

+
+ +

+ +
+
+
+
+ + + + + View Momentum Timeline + +
+ +
+ + +
+

Learning Signals

+
    + +
  • + +
+
+ + +
+

Learning Pattern Overview

+
+ $score): ?> +
+
+ signal units +
+ +
+
+ + +
+

Learning Passport

+ + + + View Learning Passport PDF + +
+ + + +
+ + + diff --git a/db/config.php b/db/config.php index 9364bb8..aca45c9 100644 --- a/db/config.php +++ b/db/config.php @@ -1,17 +1,20 @@ PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]); - } - return $pdo; +$host = 'localhost'; +$db_name = 'rs_lab'; // create this DB in phpMyAdmin +$db_user = 'root'; // default XAMPP user +$db_pass = ''; // default XAMPP password (empty) + +// Create connection +$conn = new mysqli($host, $db_user, $db_pass, $db_name); + +// Check connection +if ($conn->connect_error) { + die("Database connection failed: " . $conn->connect_error); } + +// Set charset +$conn->set_charset("utf8mb4"); +?> diff --git a/db/config.php.bak b/db/config.php.bak new file mode 100644 index 0000000..9364bb8 --- /dev/null +++ b/db/config.php.bak @@ -0,0 +1,17 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); + } + return $pdo; +} diff --git a/db/config.php.bak.txt b/db/config.php.bak.txt new file mode 100644 index 0000000..0808d7c --- /dev/null +++ b/db/config.php.bak.txt @@ -0,0 +1,43 @@ +connect_errno) { + // If you enabled display_errors (see below) you'll see this on the page. + error_log("MySQL connection failed: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error); + die("Database connection failed. Please check config.php settings."); +} + +// Set charset to UTF-8 +$mysqli->set_charset("utf8mb4"); + +// Optional: a PDO connection if any part of app expects PDO (safe to have both) +try { + $dsn = "mysql:host={$DB_HOST};dbname={$DB_NAME};port={$DB_PORT};charset=utf8mb4"; + $pdo = new PDO($dsn, $DB_USER, $DB_PASS, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); +} catch (PDOException $e) { + error_log("PDO connection failed: " . $e->getMessage()); + // don't reveal raw error to users in production + // but during debugging it's okay to show: + // die("PDO DB connection failed: " . $e->getMessage()); +} + +// If your app expects a single $db or $conn variable, create alias: +$db = $mysqli; // mysqli object +$conn = $mysqli; // some files may use $conn + +// End of config.php diff --git a/db/students.csv b/db/students.csv new file mode 100644 index 0000000..993a607 --- /dev/null +++ b/db/students.csv @@ -0,0 +1,6 @@ +roll_number,student_name +2173,Gokul Krishnan +2174,Arun Kumar +2175,Karthik Raja +2176,Manoj Kumar +2177,Suresh Babu diff --git a/db_config.php b/db_config.php new file mode 100644 index 0000000..c35e1ba --- /dev/null +++ b/db_config.php @@ -0,0 +1,21 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC + ] + ); +} catch (PDOException $e) { + die("DB connection failed"); +} diff --git a/db_save_learning_profile.php b/db_save_learning_profile.php new file mode 100644 index 0000000..5922bc2 --- /dev/null +++ b/db_save_learning_profile.php @@ -0,0 +1,137 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC + ] + ); +} catch (PDOException $e) { + http_response_code(500); + echo json_encode([ + "status" => "error", + "message" => "Database connection failed" + ]); + exit; +} + +// ------------------------------------ +// INPUT DATA (POST / SESSION / API) +// ------------------------------------ +$student_name = $_POST['name'] ?? null; +$roll_no = $_POST['roll'] ?? null; +$class = $_POST['class'] ?? null; + +$primary_style = $_POST['primary'] ?? null; +$secondary_style = $_POST['secondary'] ?? null; + +$lei_signal = $_POST['lei'] ?? "Baseline"; + +// Scores JSON (internal, no marks) +$scores_json = $_POST['scores'] ?? null; + +// Signals array β†’ JSON +$signals = $_POST['signals'] ?? []; +$signals_json = json_encode($signals); + +// ------------------------------------ +// BASIC VALIDATION +// ------------------------------------ +if ( + !$student_name || + !$roll_no || + !$primary_style || + !$scores_json +) { + http_response_code(400); + echo json_encode([ + "status" => "error", + "message" => "Missing required learning profile data" + ]); + exit; +} + +// ------------------------------------ +// INSERT OR UPDATE LOGIC +// ------------------------------------ +// One active learning profile per student +$sql = " +INSERT INTO learning_profiles +( + student_name, + roll_no, + class, + primary_style, + secondary_style, + lei_signal, + style_scores, + learning_signals, + created_at +) +VALUES +( + :student_name, + :roll_no, + :class, + :primary_style, + :secondary_style, + :lei_signal, + :style_scores, + :learning_signals, + NOW() +) +ON DUPLICATE KEY UPDATE + primary_style = VALUES(primary_style), + secondary_style = VALUES(secondary_style), + lei_signal = VALUES(lei_signal), + style_scores = VALUES(style_scores), + learning_signals = VALUES(learning_signals), + updated_at = NOW() +"; + +$stmt = $pdo->prepare($sql); + +// ------------------------------------ +// EXECUTE +// ------------------------------------ +try { + $stmt->execute([ + ':student_name' => $student_name, + ':roll_no' => $roll_no, + ':class' => $class, + ':primary_style' => $primary_style, + ':secondary_style' => $secondary_style, + ':lei_signal' => $lei_signal, + ':style_scores' => $scores_json, + ':learning_signals'=> $signals_json + ]); + + echo json_encode([ + "status" => "success", + "message" => "Learning profile saved successfully" + ]); + +} catch (PDOException $e) { + http_response_code(500); + echo json_encode([ + "status" => "error", + "message" => "Failed to save learning profile" + ]); +} diff --git a/deactivate_institution.php b/deactivate_institution.php new file mode 100644 index 0000000..d853fbd --- /dev/null +++ b/deactivate_institution.php @@ -0,0 +1,17 @@ +connect_error) { + die("DB Connection failed"); +} + +if (isset($_GET['id'])) { + $id = intval($_GET['id']); + + $sql = "UPDATE institutions SET status='inactive' WHERE id=$id"; + $conn->query($sql); +} + +$conn->close(); + +header("Location: admin_dashboard.php"); +exit(); diff --git a/delete_institution.php b/delete_institution.php new file mode 100644 index 0000000..a68fb40 --- /dev/null +++ b/delete_institution.php @@ -0,0 +1,9 @@ +query("UPDATE institutions SET deleted_at = NOW() WHERE id=$id"); + +header("Location: admin_dashboard.php"); +exit; \ No newline at end of file diff --git a/export_teacher_excel.php b/export_teacher_excel.php new file mode 100644 index 0000000..8ba1f62 --- /dev/null +++ b/export_teacher_excel.php @@ -0,0 +1,57 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC + ] + ); +} catch (PDOException $e) { + die("DB connection failed"); +} + +$stmt = $pdo->prepare(" + SELECT + lp.roll_no, + lp.student_name, + lp.class, + lm.momentum_state, + lm.momentum_score, + lm.updated_at + FROM learning_profiles lp + LEFT JOIN learning_momentum lm + ON lp.roll_no = lm.roll_no + WHERE lp.class = :class + ORDER BY lp.roll_no ASC +"); +$stmt->execute([':class' => $class]); +$data = $stmt->fetchAll(); + +header("Content-Type: application/vnd.ms-excel"); +header("Content-Disposition: attachment; filename=Class_{$class}_Momentum_Report.xls"); + +echo "Roll No\tStudent Name\tClass\tMomentum State\tMomentum Score\tLast Activity\n"; + +foreach ($data as $row) { + echo + $row['roll_no'] . "\t" . + $row['student_name'] . "\t" . + $row['class'] . "\t" . + ($row['momentum_state'] ?? 'Building') . "\t" . + ($row['momentum_score'] ?? 50) . "\t" . + ($row['updated_at'] ?? '-') . "\n"; +} +exit; diff --git a/export_teacher_pdf.php b/export_teacher_pdf.php new file mode 100644 index 0000000..0f7ceb6 --- /dev/null +++ b/export_teacher_pdf.php @@ -0,0 +1,76 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC + ] + ); +} catch (PDOException $e) { + die("DB connection failed"); +} + +$stmt = $pdo->prepare(" + SELECT + lp.roll_no, + lp.student_name, + lm.momentum_state, + lm.momentum_score + FROM learning_profiles lp + LEFT JOIN learning_momentum lm + ON lp.roll_no = lm.roll_no + WHERE lp.class = :class + ORDER BY lp.roll_no ASC +"); +$stmt->execute([':class' => $class]); +$students = $stmt->fetchAll(); + +$html = " +

RS Learning Lab – Class Momentum Report

+

Class: {$class}

+ + + + + + + +"; + +foreach ($students as $s) { + $html .= " + + + + + + + "; +} + +$html .= "
Roll NoStudent NameMomentum StateMomentum Score
{$s['roll_no']}{$s['student_name']}".($s['momentum_state'] ?? 'Building')."".($s['momentum_score'] ?? 50)."
"; + +$tmp = __DIR__ . "/teacher_report.html"; +$pdf = __DIR__ . "/Class_{$class}_Momentum_Report.pdf"; + +file_put_contents($tmp, $html); + +shell_exec("wkhtmltopdf \"$tmp\" \"$pdf\""); + +header("Content-Type: application/pdf"); +header("Content-Disposition: inline; filename=Class_Momentum_Report.pdf"); +readfile($pdf); +exit; diff --git a/facilitated_assessment.php b/facilitated_assessment.php new file mode 100644 index 0000000..78d61fe --- /dev/null +++ b/facilitated_assessment.php @@ -0,0 +1,170 @@ +prepare("SELECT student_roll, pdf_path FROM learning_style_results"); +$stmt->execute(); +$results = $stmt->fetchAll(PDO::FETCH_ASSOC); + +$pdfMap = []; +foreach ($results as $row) { + $pdfMap[$row['student_roll']] = $row['pdf_path']; +} + +if (file_exists($csvFile)) { + if (($handle = fopen($csvFile, "r")) !== FALSE) { + $header = fgetcsv($handle); + + while (($row = fgetcsv($handle)) !== FALSE) { + $students[] = [ + "roll" => $row[0], + "name" => $row[1] + ]; + } + fclose($handle); + } +} +?> + + + + +Assessment Session | RS Learning Lab + + + + + + +
+

πŸ“‹ Assessment Session

+
+ Class Β· + Section Β· + Facilitator: +
+ + + + + + + + + + + + 0): ?> + + + + + + + + + + + + + + +
Roll NoStudent NameAction
+ + + + βœ… PDF Generated

+ + + View PDF + + + + + + Scan OMR + + + + +
+ No student list found. Please upload CSV to begin. +
+ +

+ + + Download All Reports + + +
+ + + \ No newline at end of file diff --git a/facilitated_complete.php b/facilitated_complete.php new file mode 100644 index 0000000..dd30706 --- /dev/null +++ b/facilitated_complete.php @@ -0,0 +1,110 @@ + + + + + + Session Completed | RS Learning Lab + + + + + + + + +
+ +
+

βœ… Facilitated Session Completed

+

+ The class session setup has been successfully completed. +

+ +
+

What happens next

+
    +
  • Teacher conducts the assessment in the classroom
  • +
  • Student responses are collected (manual / OMR)
  • +
  • Learning styles are analysed
  • +
  • Learning Passport is generated
  • +
+
+ + + Return to Dashboard + + +
+ Demo note: In the full version, this step leads to automated + response processing and learning style reports. +
+
+ +
+ + + diff --git a/facilitated_inputs.php b/facilitated_inputs.php new file mode 100644 index 0000000..094dc46 --- /dev/null +++ b/facilitated_inputs.php @@ -0,0 +1,128 @@ + + + + + + Facilitated Mode – Teacher Setup + + + + + +
+

πŸ‘¨β€πŸ« Facilitated Mode – Setup

+

+ Enter teacher details and upload the student list to begin the assessment session. +

+ +
+ +
+

Teacher & Class Details

+ + + + + + + + + +
+ +
+

Upload Student List (CSV)

+ + + + +

+ Expected columns: Roll No, Student Name +

+
+ + +
+
+ + + diff --git a/final_assessment_intro.php b/final_assessment_intro.php new file mode 100644 index 0000000..b6bd251 --- /dev/null +++ b/final_assessment_intro.php @@ -0,0 +1,46 @@ + + +
+ +

Final Assessment –

+ +

+ This is the final assessment for your learning journey. + Take your time and answer carefully. +

+ +
    +
  • This assessment is based on the concepts you practiced.
  • +
  • Only one attempt is allowed.
  • +
  • No hints or retries during this test.
  • +
  • Minimum 60% score required to earn certificate.
  • +
+ + + β–Ά Begin Test + + +
+ + diff --git a/final_assessment_result.php b/final_assessment_result.php new file mode 100644 index 0000000..d9a470b --- /dev/null +++ b/final_assessment_result.php @@ -0,0 +1,141 @@ += 60; + +/* Badge trigger */ +if ($passed) { + $_SESSION['award_badge'] = true; +} + +/* πŸ”₯ IMPORTANT FIX */ +$topic_id = $_GET['topic_id'] ?? 1; + +/* πŸ”₯ STRONG CLASS CHECK */ +$isEligible = true; + +?> + + + +Final Assessment Result | RS Learning Lab + + + + +
+

🏁 Final Assessment Result

+ +
+Your Score: % +
+ + +

πŸŽ‰ Excellent work, ! +You have successfully completed the final assessment.

+ + +πŸŽ“ Download Certificate + + + +

+ + +πŸš€ Start Mini Project + + + + + +

πŸ’ͺ Keep going! You are improving. +Revise the concepts and try again.

+ + +πŸ” Retry Final Assessment + + + +
+ + +β¬… Back to Dashboard + + + +
+ + + \ No newline at end of file diff --git a/final_assessment_test.php b/final_assessment_test.php new file mode 100644 index 0000000..04f0bb2 --- /dev/null +++ b/final_assessment_test.php @@ -0,0 +1,168 @@ + "Which of the following variable names will cause an error in Python?", + "options" => ["count", "_value", "2value", "value2"], + "answer" => "2value" + ], + [ + "question" => "What will be the output of: print(type(5))?", + "options" => ["int", "", "integer", "number"], + "answer" => "" + ], + [ + "question" => "Which statement correctly converts user input into integer?", + "options" => [ + "x = input()", + "x = int(input())", + "x = input(int)", + "x = readInt()" + ], + "answer" => "x = int(input())" + ], + [ + "question" => "Which condition will execute the block only if x is greater than 10?", + "options" => [ + "if x = 10", + "if x > 10", + "if x >= 10", + "if x != 10" + ], + "answer" => "if x > 10" + ], + [ + "question" => "How many times will the loop run? for i in range(1, 5):", + "options" => ["3", "4", "5", "Infinite"], + "answer" => "4" + ] +]; + +/* =============================== + FORM SUBMIT +=============================== */ +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + + $score = 0; + $total = 0; + + foreach ($questions as $index => $q) { + if (isset($_POST['q'.$index])) { + $total++; + if ($_POST['q'.$index] === $q['answer']) { + $score++; + } + } + } + + $percentage = ($score / $total) * 100; + + $_SESSION['final_attempted'] = true; + $_SESSION['final_score'] = $percentage; + + /* πŸ”₯ PASS topic_id TO RESULT PAGE */ + header("Location: /rs_lab/final_assessment_result.php?topic_id=$topic_id"); + exit; +} +?> + + + + +Final Assessment | RS Learning Lab + + + + +
+ +

🏁 Final Assessment

+ +
+ + $q): ?> +
+

+ + + + +
+ + + + +
+ +
+ + + \ No newline at end of file diff --git a/final_track.php b/final_track.php new file mode 100644 index 0000000..7d91cd6 --- /dev/null +++ b/final_track.php @@ -0,0 +1,89 @@ + + + + + +Final Assessment | RS Learning Lab + + + + +
+

🏁 Final Assessment

+
Last step of your learning journey
+ +
+ ()
+Class +

+This assessment evaluates your understanding and application skills.
+Complete it carefully to unlock your certificate. +
+ + +β–Ά Start Final Assessment + + + +
+ + + \ No newline at end of file diff --git a/generate_badge_pdf.php b/generate_badge_pdf.php new file mode 100644 index 0000000..be9a967 --- /dev/null +++ b/generate_badge_pdf.php @@ -0,0 +1,137 @@ + + + + + +Learning Badge | RS Learning Lab + + + + + +
+

RS Learning Lab

+

+ +
+

+

Roll No:

+

Class:

+
+ +
+ + +
+ + +
+ + + \ No newline at end of file diff --git a/generate_certificate.php b/generate_certificate.php index a4445af..ee28c87 100644 --- a/generate_certificate.php +++ b/generate_certificate.php @@ -1,97 +1,61 @@ prepare(' - SELECT - c.id, - c.certificate_type, - c.issued_at, - u.name as username, - com.title as competition_title - FROM certificates c - JOIN users u ON c.user_id = u.id - JOIN competitions com ON c.competition_id = com.id - WHERE c.id = ? AND c.user_id = ?'); -$stmt->execute([$certificate_id, $user_id]); -$certificate = $stmt->fetch(); +// πŸ” Check if certificate already exists +$check = $conn->prepare("SELECT certificate_id FROM certificates WHERE user_id = ?"); +$check->bind_param("i", $user_id); +$check->execute(); +$res = $check->get_result(); -if (!$certificate) { - die('Certificate not found or you do not have permission to view it.'); +if ($res->num_rows > 0) { + $row = $res->fetch_assoc(); + $_SESSION['certificate_id'] = $row['certificate_id']; + header("Location: generate_certificate_pdf.php"); + exit; } -// Create new PDF document -$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); +// πŸ†” Generate Certificate ID +$year = date("Y"); +$certificate_id = "RSL-PY-$year-" . str_pad(rand(1,999999), 6, "0", STR_PAD_LEFT); -// Set document information -$pdf->SetCreator(PDF_CREATOR); -$pdf->SetAuthor('RS Learning Lab'); -$pdf->SetTitle('Certificate of Achievement'); -$pdf->setPrintHeader(false); -$pdf->setPrintFooter(false); +// πŸ’Ύ Insert record +$stmt = $conn->prepare(" + INSERT INTO certificates + (certificate_id, user_id, student_name, language, track_name, issue_date) + VALUES (?, ?, ?, ?, ?, ?) +"); -$pdf->SetMargins(PDF_MARGIN_LEFT, 10, PDF_MARGIN_RIGHT); +$stmt->bind_param( + "sissss", + $certificate_id, + $user_id, + $student_name, + $language, + $track, + $issue_date +); -// Add a page -$pdf->AddPage(); +$stmt->execute(); -// Add logo and title -$pdf->SetFont('helvetica', 'B', 20); -$pdf->Cell(0, 15, 'RS Learning Lab', 0, false, 'C', 0, '', 0, false, 'M', 'M'); -$pdf->Image('assets/pasted-20251129-095357-cfd84971.png', 10, 10, 30, 0, 'PNG', '', 'T', false, 300, '', false, false, 0, false, false, false); -$pdf->Ln(20); +// Store for PDF +$_SESSION['certificate_id'] = $certificate_id; - -// Set background image -// NOTE: You might need to adjust the path to the image. -// $pdf->Image('assets/images/certificate_background.png', 0, 0, 210, 297, '', '', '', false, 300, '', false, false, 0); - -// Set font -$pdf->SetFont('helvetica', 'B', 20); - -// Title -$pdf->Cell(0, 15, 'Certificate of Achievement', 0, 1, 'C'); -$pdf->Ln(10); - -$pdf->SetFont('helvetica', '', 12); -$pdf->Cell(0, 10, 'This is to certify that', 0, 1, 'C'); -$pdf->Ln(5); - -$pdf->SetFont('helvetica', 'B', 16); -$pdf->Cell(0, 10, strtoupper($certificate['username']), 0, 1, 'C'); -$pdf->Ln(5); - -$pdf->SetFont('helvetica', '', 12); -$pdf->Cell(0, 10, 'has successfully completed the', 0, 1, 'C'); -$pdf->Ln(5); - -$pdf->SetFont('helvetica', 'B', 14); -$pdf->Cell(0, 10, $certificate['competition_title'], 0, 1, 'C'); -$pdf->Ln(5); - -if ($certificate['certificate_type'] === 'winner') { - $pdf->SetFont('helvetica', '', 12); - $pdf->Cell(0, 10, 'and is a winner of the competition.', 0, 1, 'C'); - $pdf->Ln(5); -} - -$pdf->SetFont('helvetica', '', 10); -$pdf->Cell(0, 10, 'Issued on: ' . date('M d, Y', strtotime($certificate['issued_at'])), 0, 1, 'C'); - -// Close and output PDF document -$pdf->Output('certificate.pdf', 'I'); +// ➑️ Generate PDF +header("Location: generate_certificate_pdf.php"); +exit; diff --git a/generate_class_pdf.php b/generate_class_pdf.php new file mode 100644 index 0000000..d3be2ca --- /dev/null +++ b/generate_class_pdf.php @@ -0,0 +1,63 @@ +prepare(" + SELECT roll_number, student_name, learning_style + FROM learning_styles + WHERE user_id = ? + ORDER BY roll_number +"); +$stmt->execute([$_SESSION['user_id']]); +$students = $stmt->fetchAll(PDO::FETCH_ASSOC); + +// HTML for PDF +$html = ' + + +

RS Learning Lab – Class Learning Report

+ +

Teacher: '.htmlspecialchars($_SESSION['teacher_name']).'

+

Class: '.htmlspecialchars($_SESSION['class_name']).'

+

Date: '.date("d M Y").'

+ + + + + + + +'; + +foreach ($students as $s) { + $html .= ' + + + + + + '; +} + +$html .= '
Roll NoStudent NameLearning Style
'.htmlspecialchars($s['roll_number']).''.htmlspecialchars($s['student_name']).''.htmlspecialchars($s['learning_style']).'
'; + +// Generate PDF +$dompdf = new Dompdf(); +$dompdf->loadHtml($html); +$dompdf->setPaper('A4', 'portrait'); +$dompdf->render(); +$dompdf->stream("Class_Learning_Report.pdf", ["Attachment" => true]); diff --git a/generate_learning_pdf.php b/generate_learning_pdf.php new file mode 100644 index 0000000..56ae128 --- /dev/null +++ b/generate_learning_pdf.php @@ -0,0 +1,215 @@ + [ + 'desc' => 'Learns best using diagrams, visuals, charts, and videos.', + 'plan' => [ + 'Use diagrams, flowcharts, and mind maps', + 'Watch concept videos before reading', + 'Highlight important points with colours', + 'Revise using visual notes', + 'Prefer presentations over long text' + ], + 'do' => [ + 'Use images and visual aids', + 'Explain concepts using charts', + 'Allow visual note-taking' + ], + 'dont' => [ + 'Avoid text-only explanations', + 'Do not rush without visuals', + 'Avoid dull, long lectures' + ] + ], + + 'Practical' => [ + 'desc' => 'Learns best by doing, practicing, and hands-on activities.', + 'plan' => [ + 'Learn by solving problems', + 'Practice immediately after learning', + 'Use real-life examples', + 'Participate in labs and activities', + 'Learn from mistakes' + ], + 'do' => [ + 'Give hands-on tasks', + 'Encourage experimentation', + 'Allow trial-and-error learning' + ], + 'dont' => [ + 'Avoid theory-only teaching', + 'Do not restrict practice', + 'Avoid long passive sessions' + ] + ], + + 'Logical' => [ + 'desc' => 'Learns best through logic, structure, and step-by-step explanation.', + 'plan' => [ + 'Study in a structured order', + 'Understand logic behind concepts', + 'Use formulas and rules', + 'Break problems into steps', + 'Revise using concept maps' + ], + 'do' => [ + 'Explain step-by-step', + 'Provide reasoning', + 'Use structured material' + ], + 'dont' => [ + 'Avoid random explanations', + 'Do not skip steps', + 'Avoid unclear instructions' + ] + ], + + 'Reflective' => [ + 'desc' => 'Learns best by thinking deeply and reflecting quietly.', + 'plan' => [ + 'Study alone in quiet environment', + 'Think before answering', + 'Write personal notes', + 'Revise by recalling', + 'Maintain a learning journal' + ], + 'do' => [ + 'Give thinking time', + 'Encourage self-reflection', + 'Allow independent study' + ], + 'dont' => [ + 'Avoid forcing instant answers', + 'Avoid constant group pressure', + 'Avoid interruptions' + ] + ] +]; + +$data = $content[$style] ?? $content['Visual']; + +/* -------- HTML DESIGN -------- */ +$html = " + + + + + + +
+ +
+

RS Learning Lab

+

Learning Style Report

+

$student " . ($roll ? "| Roll No: $roll" : "") . "

+

Date: $date

+
+ +
+

Identified Learning Style

+
+ $style Learner +

{$data['desc']}

+
+
+ +
+

Personalized Daily Study Plan

+
+
    +
  • " . implode("
  • ", $data['plan']) . "
  • +
+
+
+ +
+

How This Student Learns Best

+
+ DO +
    +
  • " . implode("
  • ", $data['do']) . "
  • +
+
+ DON'T +
    +
  • " . implode("
  • ", $data['dont']) . "
  • +
+
+
+ + + +
+ + +"; + +/* -------- PDF GENERATE -------- */ +$dompdf = new Dompdf(); +$dompdf->loadHtml($html); +$dompdf->setPaper('A4', 'portrait'); +$dompdf->render(); +$dompdf->stream("Learning_Style_Report_$student.pdf", ["Attachment" => true]); diff --git a/generate_learning_style_pdf.php b/generate_learning_style_pdf.php new file mode 100644 index 0000000..56ae128 --- /dev/null +++ b/generate_learning_style_pdf.php @@ -0,0 +1,215 @@ + [ + 'desc' => 'Learns best using diagrams, visuals, charts, and videos.', + 'plan' => [ + 'Use diagrams, flowcharts, and mind maps', + 'Watch concept videos before reading', + 'Highlight important points with colours', + 'Revise using visual notes', + 'Prefer presentations over long text' + ], + 'do' => [ + 'Use images and visual aids', + 'Explain concepts using charts', + 'Allow visual note-taking' + ], + 'dont' => [ + 'Avoid text-only explanations', + 'Do not rush without visuals', + 'Avoid dull, long lectures' + ] + ], + + 'Practical' => [ + 'desc' => 'Learns best by doing, practicing, and hands-on activities.', + 'plan' => [ + 'Learn by solving problems', + 'Practice immediately after learning', + 'Use real-life examples', + 'Participate in labs and activities', + 'Learn from mistakes' + ], + 'do' => [ + 'Give hands-on tasks', + 'Encourage experimentation', + 'Allow trial-and-error learning' + ], + 'dont' => [ + 'Avoid theory-only teaching', + 'Do not restrict practice', + 'Avoid long passive sessions' + ] + ], + + 'Logical' => [ + 'desc' => 'Learns best through logic, structure, and step-by-step explanation.', + 'plan' => [ + 'Study in a structured order', + 'Understand logic behind concepts', + 'Use formulas and rules', + 'Break problems into steps', + 'Revise using concept maps' + ], + 'do' => [ + 'Explain step-by-step', + 'Provide reasoning', + 'Use structured material' + ], + 'dont' => [ + 'Avoid random explanations', + 'Do not skip steps', + 'Avoid unclear instructions' + ] + ], + + 'Reflective' => [ + 'desc' => 'Learns best by thinking deeply and reflecting quietly.', + 'plan' => [ + 'Study alone in quiet environment', + 'Think before answering', + 'Write personal notes', + 'Revise by recalling', + 'Maintain a learning journal' + ], + 'do' => [ + 'Give thinking time', + 'Encourage self-reflection', + 'Allow independent study' + ], + 'dont' => [ + 'Avoid forcing instant answers', + 'Avoid constant group pressure', + 'Avoid interruptions' + ] + ] +]; + +$data = $content[$style] ?? $content['Visual']; + +/* -------- HTML DESIGN -------- */ +$html = " + + + + + + +
+ +
+

RS Learning Lab

+

Learning Style Report

+

$student " . ($roll ? "| Roll No: $roll" : "") . "

+

Date: $date

+
+ +
+

Identified Learning Style

+
+ $style Learner +

{$data['desc']}

+
+
+ +
+

Personalized Daily Study Plan

+
+
    +
  • " . implode("
  • ", $data['plan']) . "
  • +
+
+
+ +
+

How This Student Learns Best

+
+ DO +
    +
  • " . implode("
  • ", $data['do']) . "
  • +
+
+ DON'T +
    +
  • " . implode("
  • ", $data['dont']) . "
  • +
+
+
+ + + +
+ + +"; + +/* -------- PDF GENERATE -------- */ +$dompdf = new Dompdf(); +$dompdf->loadHtml($html); +$dompdf->setPaper('A4', 'portrait'); +$dompdf->render(); +$dompdf->stream("Learning_Style_Report_$student.pdf", ["Attachment" => true]); diff --git a/generate_passport_pdf.php b/generate_passport_pdf.php new file mode 100644 index 0000000..7b0d749 --- /dev/null +++ b/generate_passport_pdf.php @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/includes/TCPDF-main/tools/tcpdf_addfont.php b/includes/TCPDF-main/tools/tcpdf_addfont.php old mode 100755 new mode 100644 diff --git a/includes/admin_layout_bottom.php b/includes/admin_layout_bottom.php new file mode 100644 index 0000000..606d6e2 --- /dev/null +++ b/includes/admin_layout_bottom.php @@ -0,0 +1,13 @@ +
+ + + + + + + + + + diff --git a/includes/admin_layout_top.php b/includes/admin_layout_top.php new file mode 100644 index 0000000..5c81a2d --- /dev/null +++ b/includes/admin_layout_top.php @@ -0,0 +1,134 @@ + + + + + + RS Learning Lab – Admin + + + + + + + + + + + + + + + + +
+
+

RS Learning Lab

+ Admin Dashboard +
+ +
+ + Logout +
+
+ + +
diff --git a/includes/auth_check.php b/includes/auth_check.php new file mode 100644 index 0000000..aadcedf --- /dev/null +++ b/includes/auth_check.php @@ -0,0 +1,9 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +} catch (PDOException $e) { + die("Database connection failed"); +} diff --git a/includes/footer.php b/includes/footer.php index 6637275..bedff53 100644 --- a/includes/footer.php +++ b/includes/footer.php @@ -1,16 +1,21 @@ -
- - - - - - - - - - - - \ No newline at end of file + + + \ No newline at end of file diff --git a/includes/header.php b/includes/header.php index cd8b012..37ae272 100644 --- a/includes/header.php +++ b/includes/header.php @@ -1,57 +1,10 @@ - - RS Learning Lab - - - - - - - + + + - - -
+ diff --git a/index.php b/index.php index b2f9e09..a8d0c23 100644 --- a/index.php +++ b/index.php @@ -1,47 +1,208 @@ - + + + + RS Learning Lab | Learning Behaviour Platform -$pdo = db(); -$stmt = $pdo->query('SELECT * FROM challenges ORDER BY RAND() LIMIT 3'); -$featured_challenges = $stmt->fetchAll(); -?> + -
-

Welcome to RS Learning Lab

-

Your platform for coding challenges, competitions, and certificates.

-
-
- -

Challenges

-
-
- -

Competitions

-
-
- -

Certificates

+ + + + + +
+ + RS Learning Lab Logo +

RS Learning Lab

+
+ +
+
+

Understand How Students Learn,
Not Just How They Score

+ +

+ RS Learning Lab is a learning behaviour platform for schools and colleges. + We focus on effort, practice, improvement, and application β€” not marks or ranks. +

+ +
- Browse Challenges -
-
-

Featured Challenges

-
- -
-
-
-
-

...

- View Challenge -
-
-
- +
+
+

🧠 Learning Style Assessment

+

Behaviour-based analysis. No exams. No right or wrong answers.

+
+ +
+

🏫 Institution Friendly

+

Teacher-led facilitated mode and student self-assessment built for real classrooms.

+
+ +
+

πŸŽ“ Real Learning Outcomes

+

Learning passports, practice tracks, applied challenges, and meaningful certificates.

+
-
+ - \ No newline at end of file + +
+

Get Started with RS Learning Lab

+ +
+
+

School Onboarding

+

Learning behaviour assessment for Classes 6–12.

+ + + Start School Registration β†’ + +
+
+
+ + + + \ No newline at end of file diff --git a/institution/add_teacher.php b/institution/add_teacher.php new file mode 100644 index 0000000..38dc2ac --- /dev/null +++ b/institution/add_teacher.php @@ -0,0 +1,143 @@ +prepare(" + INSERT INTO teachers + (institution_id, username, class_handled, password_hash, temp_password, must_change_password) + VALUES (?, ?, ?, ?, ?, 1) + "); + + try { + $stmt->execute([ + $_SESSION['institution_id'], + $username, + $class_handled, + $password_hash, + $tempPassword + ]); + + $success = "Teacher Created βœ…
+ Username: $username
+ Password: $tempPassword"; + + } catch (PDOException $e) { + $error = "Username exists or DB error"; + } + } +} +?> + + + + +Add Teacher + + + + + + + +
+ +

Add Teacher

+ + +

+ + + +

+ + +
+ + + + + + + +
+ +
+ + + \ No newline at end of file diff --git a/institution/change_password.php b/institution/change_password.php new file mode 100644 index 0000000..8b9b347 --- /dev/null +++ b/institution/change_password.php @@ -0,0 +1,87 @@ +prepare(" + UPDATE institutions + SET password_hash = ?, first_login = 0 + WHERE id = ? + "); + $stmt->execute([$hash, $_SESSION['institution_id']]); + + header("Location: dashboard.php"); + exit; + } +} +?> + + + + + Change Password + + + + +
+

Change Password

+ + +

+ + +
+ + + +
+
+ + diff --git a/institution/dashboard.php b/institution/dashboard.php new file mode 100644 index 0000000..714243c --- /dev/null +++ b/institution/dashboard.php @@ -0,0 +1,265 @@ + + + + + + +<?= htmlspecialchars($institutionName) ?> | Dashboard + + + + + + + + +
+ +
+ +
+

+

Dashboard

+
+ + + + + + + +
+ +
+ +
+

πŸ’» Coding Challenges

+

Practice β†’ Reinforcement β†’ Final Track.

+ Start Coding +
+ +
+

πŸ“œ Certificates

+

View certificates and achievements.

+ View Certificates +
+ +
+

πŸ€– DERIS AI

+

AI-based resume & skill analysis.

+ Launch +
+ +
+ +
+ + + +
+ +
+ + + + + \ No newline at end of file diff --git a/institution/login.php b/institution/login.php new file mode 100644 index 0000000..63eba3c --- /dev/null +++ b/institution/login.php @@ -0,0 +1,117 @@ +prepare(" + SELECT id, password_hash, temp_password + FROM institutions + WHERE username = ? + LIMIT 1 + "); + $stmt->execute([$username]); + $inst = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($inst && password_verify($password, $inst['password_hash'])) { + + $_SESSION['institution_id'] = $inst['id']; + + /* πŸ”₯ CLEAR TEMP PASSWORD AFTER FIRST LOGIN */ + if (!empty($inst['temp_password'])) { + $clear = $pdo->prepare(" + UPDATE institutions + SET temp_password = NULL + WHERE id = ? + "); + $clear->execute([$inst['id']]); + } + + header("Location: /rs_lab/institution/dashboard.php"); + exit; + + } else { + $error = "Invalid credentials"; + } + } +} +?> + + + + +Institution Login + + + + + +
+

Institution Login

+ + +

+ + +
+ + + +
+ +
+ + + \ No newline at end of file diff --git a/institution_detail.php b/institution_detail.php new file mode 100644 index 0000000..0c51689 --- /dev/null +++ b/institution_detail.php @@ -0,0 +1,116 @@ +connect_error) { + die("DB Connection failed"); +} + +if (!isset($_GET['id'])) { + die("Invalid request"); +} + +$id = intval($_GET['id']); + +/* GET INSTITUTION */ +$result = $conn->query("SELECT * FROM institutions WHERE id=$id"); +$inst = $result->fetch_assoc(); + +$conn->close(); + +/* πŸ”₯ FIX: TAKE FROM DB (NOT SESSION) */ +$temp_password = $inst['temp_password'] ?? 'Already changed'; +?> + + + + +Institution Details | RS Learning Lab + + + + + + +
+ +

+ +

Institution Login Credentials

+ +

Username:

+ + +

Temporary Password:

+ +

Copy & Share Message

+ + + + + +

+← Back + +
+ + + + + \ No newline at end of file diff --git a/learning_mode.php b/learning_mode.php new file mode 100644 index 0000000..bde77d3 --- /dev/null +++ b/learning_mode.php @@ -0,0 +1,44 @@ + + + + + + + + diff --git a/learning_passport.php b/learning_passport.php new file mode 100644 index 0000000..36995e0 --- /dev/null +++ b/learning_passport.php @@ -0,0 +1,182 @@ + + + + +Learning Passport + + + + + +
+

RS Learning Lab – Learning Passport

+

A growth-based learning profile (Not a marksheet)

+
+ +
+

Student Details

+
+ Name: {$student_name}
+ Roll No: {$roll_no}
+ Class: {$class} +
+
+ +
+

Learning Style Profile

+
+

Primary Learning Style: {$primary_style}

+

Secondary Learning Style: {$secondary_style}

+
+
+ +
+

Learning Signals

+
+
    "; + +foreach ($signals as $s) { + $html .= "
  • {$s}
  • "; +} + +$html .= " +
+
+
+ +
+

Learning Effectiveness Index (LEI)

+
+ LEI Signal: {$lei_signal} +

+ LEI reflects learning consistency and improvement patterns. + This is an internal growth signal, not a score or rank. +

+
+
+ +
+

Important Note

+
+

+ This Learning Passport focuses on how a student learns, + how they improve through practice, and how they apply concepts. +

+ This document is not an examination result, marksheet, or government certificate. +

+
+
+ + + + + +"; + +// ------------------------------- +// WRITE TEMP HTML +// ------------------------------- +file_put_contents($tmpHtml, $html); + +// ------------------------------- +// GENERATE PDF USING WKHTMLTOPDF +// ------------------------------- +//$wkhtml = "wkhtmltopdf"; +//$command = "$wkhtml \"$tmpHtml\" \"$outputPdf\""; +//shell_exec($command); +echo $html; +exit; + +// ------------------------------- +// SERVE PDF FOR DOWNLOAD +// ------------------------------- +if (file_exists($outputPdf)) { + header('Content-Type: application/pdf'); + header('Content-Disposition: inline; filename="Learning_Passport.pdf"'); + readfile($outputPdf); + exit; +} else { + echo "PDF generation failed."; +} diff --git a/learning_style.php b/learning_style.php new file mode 100644 index 0000000..4692681 --- /dev/null +++ b/learning_style.php @@ -0,0 +1,164 @@ + 0, + 'Practical' => 0, + 'Logical' => 0, + 'Reflective' => 0 + ]; + + foreach ($_POST as $key => $value) { + + $value = (int)$value; + + if (strpos($key, 'visual_') === 0) { + $scores['Visual'] += $value; + } + + if (strpos($key, 'practical_') === 0) { + $scores['Practical'] += $value; + } + + if (strpos($key, 'logical_') === 0) { + $scores['Logical'] += $value; + } + + if (strpos($key, 'reflective_') === 0) { + $scores['Reflective'] += $value; + } + } + + arsort($scores); + $result = array_key_first($scores); + + /* SAVE */ + $stmt = $pdo->prepare(" + INSERT INTO learning_styles + (user_id, student_name, roll_number, learning_style) + VALUES (?, ?, ?, ?) + "); + + $stmt->execute([ + $user_id, + $student_name, + $roll_number, + $result + ]); + + $saved = true; +} +?> + + + + + +
+ +

🧠 Learning Style Assessment

+ +

+Student: + +| Roll: + +

+ + + +
+ +

Assessment Completed βœ…

+ +
+ +
+ +β¬… Back + +
+ + +⬇ Download Report + + +
+ + + +
+ + + + + +
+ + + +
+ + + \ No newline at end of file diff --git a/lei/calculate_lei.php b/lei/calculate_lei.php new file mode 100644 index 0000000..f815793 --- /dev/null +++ b/lei/calculate_lei.php @@ -0,0 +1,94 @@ + "error", + "message" => "Missing LEI input values" + ]); + exit; +} + +// Safety clamp (0–100) +$practice_effort = max(0, min(100, $practice_effort)); +$reinforcement_gain = max(0, min(100, $reinforcement_gain)); +$final_consistency = max(0, min(100, $final_consistency)); + +// ------------------------------------ +// LEI FORMULA (RS LEARNING LAB LOGIC) +// ------------------------------------ +// Practice > Reinforcement > Consistency + +$weight_practice = 0.40; +$weight_reinforcement = 0.35; +$weight_consistency = 0.25; + +$lei_internal_score = + ($practice_effort * $weight_practice) + + ($reinforcement_gain * $weight_reinforcement) + + ($final_consistency * $weight_consistency); + +$lei_internal_score = round($lei_internal_score, 1); + +// ------------------------------------ +// MAP TO LEI SIGNAL (NO NUMBERS SHOWN) +// ------------------------------------ +if ($lei_internal_score >= 80) { + $lei_signal = "Strong"; + $lei_description = "Consistent effort with strong learning improvement patterns"; +} elseif ($lei_internal_score >= 60) { + $lei_signal = "Moderate"; + $lei_description = "Learning is progressing with scope for reinforcement"; +} else { + $lei_signal = "Emerging"; + $lei_description = "Early-stage learning signals; needs sustained practice"; +} + +// ------------------------------------ +// FINAL RESPONSE +// ------------------------------------ +$response = [ + "status" => "success", + "student_name" => $student_name, + "roll_no" => $roll_no, + + // INTERNAL (can be hidden from UI) + "lei_score_internal" => $lei_internal_score, + + // WHAT RS LEARNING LAB SHOWS + "lei_signal" => $lei_signal, + "lei_description" => $lei_description, + + // Transparency for research / audit + "inputs" => [ + "practice_effort" => $practice_effort, + "reinforcement_gain" => $reinforcement_gain, + "final_consistency" => $final_consistency + ] +]; + +echo json_encode($response); +exit; diff --git a/login.php b/login.php index f5500c3..4a4ee25 100644 --- a/login.php +++ b/login.php @@ -1,70 +1,57 @@ prepare('SELECT * FROM users WHERE email = ?'); - $stmt->execute([$email]); - $user = $stmt->fetch(); - - if ($user && password_verify($password, $user['password'])) { - $_SESSION['user_id'] = $user['id']; - $_SESSION['user_name'] = $user['username']; - header('Location: index.php'); - exit; - } else { - $errors[] = 'Invalid email or password'; - } - } +if ($email === '' || $password === '') { + die("Missing credentials"); } -?> -
-
-
-
Login
-
- -
- - -
- -

- -
- -
-
- - -
-
- - -
- -
-
-
-
-
+/* =============================== + FETCH INSTITUTION +=============================== */ +$stmt = $pdo->prepare("SELECT * FROM institutions WHERE email = ? LIMIT 1"); +$stmt->execute([$email]); +$inst = $stmt->fetch(PDO::FETCH_ASSOC); - +if (!$inst) { + die("Account not found"); +} + +/* =============================== + PASSWORD CHECK (demo) +=============================== */ +if (!empty($inst['password']) && $inst['password'] !== $password) { + die("Invalid password"); +} + +/* =============================== + SESSION SET +=============================== */ +$_SESSION['user_role'] = 'institution'; +$_SESSION['institution_id'] = $inst['id']; +$_SESSION['institution_name'] = $inst['institution_name']; +$_SESSION['institution_type'] = $inst['institution_type']; // school / college +$_SESSION['first_login'] = $inst['first_login'] ?? 0; + +/* =============================== + FORCE PASSWORD CHANGE +=============================== */ +if ($_SESSION['first_login'] == 1) { + header("Location: change_password.php"); + exit; +} + +/* =============================== + πŸ”‘ CENTRAL ROUTER +=============================== */ +header("Location: router.php"); +exit; \ No newline at end of file diff --git a/logout.php b/logout.php index 95db42c..98843fb 100644 --- a/logout.php +++ b/logout.php @@ -1,6 +1,11 @@ + + + + Facilitated Mode | RS Learning Lab + + + + + +
+ +

πŸ‘¨β€πŸ« Facilitated Mode

+

+ Conduct learning style assessments for a group of students in a guided classroom environment. +

+ +
+

How this works

+
    +
  • Teacher conducts the assessment
  • +
  • Responses are entered manually or via uploaded data
  • +
  • No student login required
  • +
  • Ideal for classrooms & computer labs
  • +
+
+ +
+

Next Step

+

+ Start a new class session and prepare the student list for assessment. +

+ + + Start Facilitated Session + +
+ +
+ + + diff --git a/manual_enter_answers.php b/manual_enter_answers.php new file mode 100644 index 0000000..bd236fd --- /dev/null +++ b/manual_enter_answers.php @@ -0,0 +1,48 @@ +query("SELECT * FROM manual_quizzes WHERE id = $quiz_id"); +if ($quiz->num_rows == 0) { + die("Quiz not found."); +} +$quiz = $quiz->fetch_assoc(); +?> + + + + Enter Answers - RS Learning Lab + + +

Enter Answers for Quiz:

+ +
+ + + Student Name:

+ Roll Number:

+ + + Q: + +
+ + + +
+ + + diff --git a/manual_mode_setup.php b/manual_mode_setup.php new file mode 100644 index 0000000..5e6ee43 --- /dev/null +++ b/manual_mode_setup.php @@ -0,0 +1,111 @@ +prepare(" + INSERT INTO quizzes (institution_id, quiz_name, class_section, subject, total_questions) + VALUES (?, ?, ?, ?, ?) + "); + $stmt->bind_param("isssi", $institution_id, $generated_quiz_name, $class_section, $subject_tag, $fixed_questions); + + if ($stmt->execute()) { + $success = "Learning-Style quiz created successfully!"; + $class_section = ""; + } else { + $errors[] = "Database error: " . $stmt->error; + } + + $stmt->close(); + } +} + +$q = $conn->prepare("SELECT id, quiz_name, class_section, subject, total_questions, created_at + FROM quizzes WHERE institution_id = ? ORDER BY created_at DESC"); +$q->bind_param("i", $institution_id); +$q->execute(); +$result = $q->get_result(); +$quizzes = $result->fetch_all(MYSQLI_ASSOC); +$q->close(); + +?> + + + + Create Learning-Style Quiz + + + + +

Create Manual Learning-Style Quiz

+ + +

+ + + + +

+ + + +
+ +
+ +

+ +
+ +

+ + +
+ +
+ +

Existing Learning-Style Quizzes

+ + + + + + + + + + + + + + + + +
ClassQuestionsCreatedAction
+ Enter Answers +
+ + + diff --git a/manual_result_profile.php b/manual_result_profile.php new file mode 100644 index 0000000..4ef3f09 --- /dev/null +++ b/manual_result_profile.php @@ -0,0 +1,241 @@ +prepare("SELECT ma.*, mq.quiz_name, mq.class_name, mq.total_questions + FROM manual_answers ma + JOIN manual_quizzes mq ON mq.id = ma.quiz_id + WHERE ma.id = ? LIMIT 1"); +$stmt->bind_param('i', $answerId); +$stmt->execute(); +$row = $stmt->get_result()->fetch_assoc(); +$stmt->close(); + +if (!$row) { + echo "Record not found."; + exit; +} + +// Prepare answers array and normalize +$studentAnswers = array_map('trim', explode(',', $row['answers'])); // may contain '' for blanks +$totalQuestions = (int)$row['total_questions']; +for ($i = count($studentAnswers); $i < $totalQuestions; $i++) $studentAnswers[] = ''; + +// METRICS (no key, no marks) +$attempted = 0; +$blanks = 0; +$optionCounts = ['A'=>0,'B'=>0,'C'=>0,'D'=>0]; +$streaksBlank = []; +$streaksSame = []; +$curBlank = 0; $curBlankStart = null; +$curSame = 0; $curSameStart = null; +$lastOption = null; + +for ($i = 0; $i < $totalQuestions; $i++) { + $s = isset($studentAnswers[$i]) ? strtoupper($studentAnswers[$i]) : ''; + if ($s === '') { + $blanks++; + if ($curBlank === 0) $curBlankStart = $i+1; + $curBlank++; + if ($curSame > 0) { + $streaksSame[] = ['start'=>$curSameStart,'length'=>$curSame,'option'=>$lastOption]; + $curSame = 0; $curSameStart = null; + } + } else { + $attempted++; + if (isset($optionCounts[$s])) $optionCounts[$s]++; + if ($s === $lastOption) { + if ($curSame === 0) $curSameStart = $i; // start index 0-based + $curSame++; + } else { + if ($curSame > 0) { + $streaksSame[] = ['start'=>$curSameStart+1,'length'=>$curSame,'option'=>$lastOption]; + } + $curSame = 1; + $curSameStart = $i; + } + if ($curBlank > 0) { + $streaksBlank[] = ['start'=>$curBlankStart,'length'=>$curBlank]; + $curBlank = 0; $curBlankStart = null; + } + $lastOption = $s; + } +} +if ($curBlank > 0) $streaksBlank[] = ['start'=>$curBlankStart,'length'=>$curBlank]; +if ($curSame > 0) $streaksSame[] = ['start'=>$curSameStart+1,'length'=>$curSame,'option'=>$lastOption]; + +// basic dispersion: if one option dominates >60% of attempts -> likely guessing or habit +$dominantOption = null; +foreach ($optionCounts as $opt => $count) { + if ($attempted > 0 && ($count / $attempted) > 0.6) { + $dominantOption = $opt; break; + } +} + +// Compose patterns (heuristics) +$patterns = []; + +if ($blanks >= max(3, round($totalQuestions * 0.15))) { + $patterns[] = "Leaves several questions blank β€” may be cautious, unsure, or facing time-management issues."; +} +if ($dominantOption) { + $patterns[] = "High repetition of option '{$dominantOption}' across attempts β€” possible guessing or strong bias for a particular letter."; +} +foreach ($streaksSame as $st) { + if ($st['length'] >= 4) { + $patterns[] = "Repeated same answer from Q{$st['start']} for {$st['length']} questions β€” may be guess pattern or misread instructions."; + } +} +foreach ($streaksBlank as $st) { + if ($st['length'] >= 3) { + $patterns[] = "Left consecutive blanks from Q{$st['start']} (length {$st['length']}) β€” suggests uncertainty or a topic block."; + } +} + +// attempt distribution patterns +$firstHalfAttempt = 0; $secondHalfAttempt = 0; +for ($i = 0; $i < $totalQuestions; $i++) { + if ($i < $totalQuestions/2) { + if ($studentAnswers[$i] !== '') $firstHalfAttempt++; + } else { + if ($studentAnswers[$i] !== '') $secondHalfAttempt++; + } +} +if ($firstHalfAttempt > $secondHalfAttempt + 2) { + $patterns[] = "Stronger start, fewer attempts later β€” may lose focus or face time pressure towards the end."; +} elseif ($secondHalfAttempt > $firstHalfAttempt + 2) { + $patterns[] = "Fewer attempts initially and stronger finish β€” may be cautious first then gain confidence."; +} +if (empty($patterns)) { + $patterns[] = "Consistent attempt behaviour detected; no strong negative signals from this single test."; +} + +// Pick a friendly learning style label +$learningStyle = ''; +if ($blanks >= round($totalQuestions * 0.25)) { + $learningStyle = 'Cautious & Reflective'; +} elseif ($dominantOption) { + $learningStyle = 'Guess-Prone / Pattern-based'; +} elseif ($firstHalfAttempt > $secondHalfAttempt + 2) { + $learningStyle = 'Strong Starter'; +} elseif ($secondHalfAttempt > $firstHalfAttempt + 2) { + $learningStyle = 'Strong Finisher'; +} else { + $learningStyle = 'Balanced Attempt Style'; +} + +// Weak hints: use blank & streak positions as "weak ranges" +$weakHints = []; +foreach ($streaksBlank as $s) { + if ($s['length'] >= 2) $weakHints[] = "Weak zone: Q{$s['start']} - Q" . ($s['start'] + $s['length'] - 1); +} +if (empty($weakHints) && !empty($streaksSame)) { + foreach ($streaksSame as $s) { + if ($s['length'] >= 4) $weakHints[] = "Review section near Q{$s['start']} - Q" . ($s['start'] + $s['length'] - 1); + } +} +if (empty($weakHints)) $weakHints[] = "No clear weak zone found in this single attempt."; + +// Render page +include __DIR__ . '/includes/header.php'; +?> + + + +
+
+
+
+
Result Profile β€”
+

+ Quiz: + · Class: +

+ + + + + +
Total Questions
Attempted
Left Blank
+ +
Learning Style
+

+ +
Silent Skill Insights
+
    + +
  • + +
+ +
Weak Hints
+ +
    + +
  • + +
+ + + + +
+
+
+ +
+
+
+
Question-wise view (Student choices)
+
This view shows student-selected options only (no correct key required).
+
+ + + + + + + + + + + + +
QStudent Choice
Blank'; ?>
+
+ +
+
+
+
+ + diff --git a/manual_set_key.php b/manual_set_key.php new file mode 100644 index 0000000..6af5bfd --- /dev/null +++ b/manual_set_key.php @@ -0,0 +1,205 @@ +prepare(" + SELECT id, quiz_name, class_name, subject, total_questions + FROM manual_quizzes + WHERE id = ? AND institution_id = ? + LIMIT 1 +"); +$stmt->bind_param('ii', $quizId, $institutionId); +$stmt->execute(); +$quiz = $stmt->get_result()->fetch_assoc(); +$stmt->close(); + +if (!$quiz) { + header('Location: manual_mode_setup.php'); + exit; +} + +$totalQuestions = (int)$quiz['total_questions']; + +// Check if key already exists +$existingKey = null; +$stmt = $conn->prepare(" + SELECT id, correct_answers + FROM manual_answer_keys + WHERE quiz_id = ? + LIMIT 1 +"); +$stmt->bind_param('i', $quizId); +$stmt->execute(); +$res = $stmt->get_result()->fetch_assoc(); +$stmt->close(); +if ($res) { + $existingKey = $res; +} + +// If existing, prefill array +$prefill = array_fill(1, $totalQuestions, ''); +if ($existingKey) { + $parts = explode(',', $existingKey['correct_answers']); + for ($i = 0; $i < $totalQuestions; $i++) { + $qNum = $i + 1; + if (isset($parts[$i])) { + $val = strtoupper(trim($parts[$i])); + $prefill[$qNum] = in_array($val, ['A','B','C','D'], true) ? $val : ''; + } + } +} + +// Handle POST – save/update key +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $answers = []; + for ($i = 1; $i <= $totalQuestions; $i++) { + $key = 'q' . $i; + $val = isset($_POST[$key]) ? strtoupper(trim($_POST[$key])) : ''; + if (!in_array($val, ['A','B','C','D'], true)) { + $errors[] = "Please set a valid option (A/B/C/D) for Question {$i}."; + } + $answers[] = $val; + } + + if (empty($errors)) { + $ansStr = implode(',', $answers); + + if ($existingKey) { + // update + $stmt = $conn->prepare(" + UPDATE manual_answer_keys + SET correct_answers = ? + WHERE id = ? + "); + $stmt->bind_param('si', $ansStr, $existingKey['id']); + $stmt->execute(); + $stmt->close(); + $success = "Answer key updated successfully."; + } else { + // insert + $stmt = $conn->prepare(" + INSERT INTO manual_answer_keys + (quiz_id, correct_answers) + VALUES (?, ?) + "); + $stmt->bind_param('is', $quizId, $ansStr); + $stmt->execute(); + $stmt->close(); + $success = "Answer key saved successfully."; + } + + // refresh prefill + for ($i = 0; $i < $totalQuestions; $i++) { + $qNum = $i + 1; + $prefill[$qNum] = $answers[$i]; + } + } +} + +include __DIR__ . '/includes/header.php'; +?> + + + +
+
+
+
+
Set Answer Key
+

+ Quiz:
+ Class: · + Subject: +

+ + +
+
    + +
  • + +
+
+ + + +
+ +
+ + +
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+
+
+ +
+
+
+
How this is used?
+

+ The answer key is used to evaluate each student and to power + learning-style and silent-skill detection. Patterns of correct and + incorrect answers are analysed to understand how the student thinks. +

+
    +
  • Score calculation (correct / wrong / unanswered)
  • +
  • Identifying consistently weak zones in the paper
  • +
  • Behaviour pattern (careful / careless / conceptual gaps)
  • +
+
+
+
+
+ + diff --git a/momentum_engine.php b/momentum_engine.php new file mode 100644 index 0000000..785f733 --- /dev/null +++ b/momentum_engine.php @@ -0,0 +1,148 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC + ] + ); +} catch (PDOException $e) { + die("DB connection failed"); +} + +/* ================================== + TABLE (RUN ONCE) +================================== +learning_momentum +- id +- roll_no +- momentum_score +- momentum_state +- updated_at +================================== */ + +/* ================================== + INPUT (FROM TRACKS) +================================== */ +$roll_no = $_POST['roll'] ?? null; +$action = $_POST['action'] ?? null; +/* +Allowed actions: +practice_retry +practice_complete +reinforcement_complete +final_success +final_fail +*/ + +if (!$roll_no || !$action) { + http_response_code(400); + echo json_encode(["status"=>"error","message"=>"Invalid momentum input"]); + exit; +} + +/* ================================== + FETCH CURRENT MOMENTUM +================================== */ +$stmt = $pdo->prepare(" + SELECT * FROM learning_momentum + WHERE roll_no = :roll + LIMIT 1 +"); +$stmt->execute([':roll' => $roll_no]); +$data = $stmt->fetch(); + +$momentum_score = $data['momentum_score'] ?? 50; // neutral baseline + +/* ================================== + MOMENTUM RULE ENGINE +================================== */ +switch ($action) { + + case "practice_retry": + $momentum_score += 2; + break; + + case "practice_complete": + $momentum_score += 5; + break; + + case "reinforcement_complete": + $momentum_score += 4; + break; + + case "final_success": + $momentum_score += 10; + break; + + case "final_fail": + $momentum_score -= 8; + break; +} + +// Clamp score +$momentum_score = max(0, min(100, $momentum_score)); + +/* ================================== + MAP SCORE β†’ STATE +================================== */ +if ($momentum_score >= 80) { + $momentum_state = "Applying"; +} elseif ($momentum_score >= 60) { + $momentum_state = "Stabilizing"; +} elseif ($momentum_score >= 40) { + $momentum_state = "Building"; +} else { + $momentum_state = "Reset"; +} + +/* ================================== + SAVE / UPDATE +================================== */ +if ($data) { + $stmt = $pdo->prepare(" + UPDATE learning_momentum + SET momentum_score = :score, + momentum_state = :state, + updated_at = NOW() + WHERE roll_no = :roll + "); +} else { + $stmt = $pdo->prepare(" + INSERT INTO learning_momentum + (roll_no, momentum_score, momentum_state, updated_at) + VALUES (:roll, :score, :state, NOW()) + "); +} + +$stmt->execute([ + ':roll' => $roll_no, + ':score' => $momentum_score, + ':state' => $momentum_state +]); + +/* ================================== + OUTPUT +================================== */ +echo json_encode([ + "status" => "success", + "roll_no" => $roll_no, + "momentum_score" => $momentum_score, + "momentum_state" => $momentum_state +]); +exit; diff --git a/momentum_timeline.php b/momentum_timeline.php new file mode 100644 index 0000000..b527ee0 --- /dev/null +++ b/momentum_timeline.php @@ -0,0 +1,197 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC + ] + ); +} catch (PDOException $e) { + die("DB connection failed"); +} + +/* =============================== + INPUT +================================ */ +$roll_no = $_GET['roll'] ?? $_SESSION['roll'] ?? null; +$name = $_GET['name'] ?? $_SESSION['name'] ?? "Student"; + +if (!$roll_no) { + die("Student not specified"); +} + +/* =============================== + TABLE ASSUMPTION (RUN ONCE) +================================ +learning_momentum_log +- id +- roll_no +- action +- momentum_score +- momentum_state +- created_at +================================ */ + +/* =============================== + FETCH MOMENTUM HISTORY +================================ */ +$stmt = $pdo->prepare(" + SELECT action, momentum_score, momentum_state, created_at + FROM learning_momentum_log + WHERE roll_no = :roll + ORDER BY created_at ASC +"); +$stmt->execute([':roll' => $roll_no]); +$timeline = $stmt->fetchAll(); + +if (!$timeline) { + $no_data = true; +} else { + $no_data = false; +} +?> + + + + +Momentum Timeline – RS Learning Lab + + + + + +
+

Learning Momentum Timeline

+

+ (Roll No: ) +

+ +

+This timeline shows how learning momentum evolved across practice, reinforcement, and application stages. +

+ + + +

No momentum data available yet. Start practicing to build your learning journey.

+ + + +
+ + +
+ + + + + +
+
+
+ +
+ Momentum Score: Β· + +
+
+ + +
+ + + + + +
+ + + diff --git a/omr_log.txt b/omr_log.txt new file mode 100644 index 0000000..030d087 --- /dev/null +++ b/omr_log.txt @@ -0,0 +1,16 @@ +{"timestamp":"2026-01-29 13:39:31","roll_no":"1","student_name":"Arjun K","answers":"B,B,B,B,B,B,B,B,B,B,C,B,C,C,B,B,B,B"} +{"timestamp":"2026-01-29 13:46:45","roll_no":"1","student_name":"Arjun K","answers":"B,B,B,B,B,B,B,B,B,B,C,B,C,C,B,B,B,B"} +{"timestamp":"2026-01-29 14:30:06","roll_no":"1","student_name":"Arjun K","answers":"B,B,B,B,B,B,B,B,B,B,C,B,C,C,B,B,B,B"} +{"timestamp":"2026-01-29 14:30:28","roll_no":"1","student_name":"Arjun K","answers":"B,B,B,B,B,B,B,B,B,B,C,B,C,C,B,B,B,B"} +{"timestamp":"2026-01-29 15:38:20","roll_no":"1","student_name":"Arjun K","answers":"B,B,B,B,B,B,B,B,B,B,C,B,C,C,B,B,B,B"} +{"timestamp":"2026-02-01 18:07:46","roll_no":"01","student_name":"Arjun K","answers":"B,B,B,B,B,B,B,B,B,B,C,B,C,C,B,B,B,B"} +{"timestamp":"2026-03-02 17:50:44","roll_no":"1","student_name":"Arjun K","answers":"C,,A,A,A,A,A,A,A,A,A,C,A,A,A,A,C,A,A,C"} +{"timestamp":"2026-03-03 05:45:51","roll_no":"1","student_name":"Arjun K","answers":"C,,A,A,A,A,A,A,A,A,A,C,A,A,A,A,C,A,A,C"} +{"timestamp":"2026-03-26 15:49:49","roll_no":"Mohammed Shinos S","student_name":"9944201264","answers":",,,,,,,,,,,,C,,,C,B,A"} +{"timestamp":"2026-03-26 15:52:36","roll_no":"Mohammed Shinos S","student_name":"9944201264","answers":"C,A,,A,A,C,A,,A,A,C,A,B,B,A,A,B,A"} +{"timestamp":"2026-03-26 16:27:02","roll_no":"Mohammed Shinos S","student_name":"9944201264","answers":"C,A,,A,A,C,A,,A,A,C,A,B,B,A,A,B,A"} +{"timestamp":"2026-03-26 16:30:24","roll_no":"Mohammed Shinos S","student_name":"9944201264","answers":"C,A,,A,A,C,A,,A,A,C,A,B,B,A,A,B,A"} +{"timestamp":"2026-03-26 16:33:20","roll_no":"Askar A","student_name":"9994159658","answers":"C,A,,A,A,C,A,,A,A,C,A,B,B,A,A,B,A"} +{"timestamp":"2026-03-26 16:52:20","roll_no":"Askar A","student_name":"9994159658","answers":"C,A,,A,A,C,A,,A,A,C,A,B,B,A,A,B,A"} +{"timestamp":"2026-03-27 09:34:51","roll_no":"Mohammed Shinos S","student_name":"9944201264","answers":"C,A,,A,A,C,A,,A,A,C,A,B,B,A,A,B,A"} +{"timestamp":"2026-04-15 15:55:29","roll_no":"Keerthivasan G","student_name":"9150432407","answers":"C,A,,A,A,C,A,,A,A,C,A,B,B,A,A,B,A"} diff --git a/omr_read_sheet.py b/omr_read_sheet.py new file mode 100644 index 0000000..8191716 --- /dev/null +++ b/omr_read_sheet.py @@ -0,0 +1,76 @@ +import sys +import cv2 +import numpy as np + +# Usage: python omr_read_sheet.py path_to_image +if len(sys.argv) < 2: + print("ERROR: No image path given") + sys.exit(1) + +image_path = sys.argv[1] + +# Read image +image = cv2.imread(image_path) +if image is None: + print("ERROR: Cannot read image") + sys.exit(1) + +# Convert to gray + blur + threshold (invert so marks become white) +gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) +blur = cv2.GaussianBlur(gray, (5, 5), 0) +_, thresh = cv2.threshold( + blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU +) + +# NOTE: +# We assume a simple grid layout. +# You will have to adjust these values for your printed template. + +NUM_QUESTIONS = 20 +NUM_OPTIONS = 4 # A,B,C,D + +# These should be tuned based on your sheet +# Whole answer area as rectangle (x_start, y_start, x_end, y_end) +# For first test, take a scan and open in any image viewer to note pixel coords +ANS_X_START = 100 +ANS_X_END = 1000 +ANS_Y_START = 200 +ANS_Y_END = 1400 + +height = ANS_Y_END - ANS_Y_START +width = ANS_X_END - ANS_X_START + +row_height = height / NUM_QUESTIONS +col_width = width / NUM_OPTIONS + +answers = [] + +for q in range(NUM_QUESTIONS): + row_top = int(ANS_Y_START + q * row_height) + row_bottom = int(ANS_Y_START + (q + 1) * row_height) + + bubble_scores = [] + for o in range(NUM_OPTIONS): + col_left = int(ANS_X_START + o * col_width) + col_right = int(ANS_X_START + (o + 1) * col_width) + + # Region of interest for this bubble + roi = thresh[row_top:row_bottom, col_left:col_right] + + # Score = number of white pixels (since inverted) + score = cv2.countNonZero(roi) + bubble_scores.append(score) + + # Decide which option is filled + max_score = max(bubble_scores) + max_index = bubble_scores.index(max_score) + + # threshold: if mark very light, treat as blank + if max_score < 200: # you can tune this number + # No proper mark detected + answers.append("") + else: + answers.append("ABCD"[max_index]) + +# Print as comma-separated string => PHP will read this +print(",".join(answers)) diff --git a/omr_test.php b/omr_test.php new file mode 100644 index 0000000..4f2074a --- /dev/null +++ b/omr_test.php @@ -0,0 +1,226 @@ +'Visual',2=>'Auditory',3=>'Kinesthetic',4=>'Reflective',5=>'Active', + 6=>'Visual',7=>'Auditory',8=>'Kinesthetic',9=>'Reflective',10=>'Active', + 11=>'Visual',12=>'Auditory',13=>'Kinesthetic',14=>'Reflective',15=>'Active', + 16=>'Visual',17=>'Auditory',18=>'Kinesthetic',19=>'Reflective',20=>'Active' + ]; + + $option_weight = ['A'=>1,'B'=>2,'C'=>3,'D'=>2]; + + $style_score = [ + 'Visual'=>0, + 'Auditory'=>0, + 'Kinesthetic'=>0, + 'Reflective'=>0, + 'Active'=>0 + ]; + + foreach ($answers as $i => $opt) { + $q = $i + 1; + if (!isset($question_map[$q])) continue; + $style_score[$question_map[$q]] += $option_weight[$opt] ?? 0; + } + + arsort($style_score); + $styles = array_keys($style_score); + + $primary = $styles[0]; + $secondary = $styles[1]; + + /* ------------------------------- + STUDENT CONTEXT + -------------------------------- */ + $student_roll = $_GET['roll'] ?? 'UNKNOWN'; + $student_name = $_GET['name'] ?? 'Student'; + + /* ------------------------------- + GENERATE PDF + -------------------------------- */ + $dompdf = new Dompdf(); + + ob_start(); + ?> +

RS Learning Lab

+

Learning Style Report

+ +

Name:

+

Roll No:

+ +

Primary Style:

+

Secondary Style:

+ +

Scores

+
    + $v): ?> +
  • :
  • + +
+ +

RS Learning Lab – Learning Behaviour Platform

+ + loadHtml($html); + $dompdf->setPaper('A4'); + $dompdf->render(); + + $pdfDir = __DIR__ . '/generated_pdfs/'; + if (!is_dir($pdfDir)) { + mkdir($pdfDir, 0777, true); + } + + $pdfFile = $pdfDir . $student_roll . '_learning_style.pdf'; + + file_put_contents($pdfFile, $dompdf->output()); + + // Web path for download + $pdfWebPath = "generated_pdfs/" . $student_roll . "_learning_style.pdf"; + + /* ------------------------------- + SAVE TO DATABASE + -------------------------------- */ + + // check if record already exists + $check = $pdo->prepare("SELECT id FROM learning_style_results WHERE student_roll=?"); + $check->execute([$student_roll]); + + if ($check->rowCount() > 0) { + + $stmt = $pdo->prepare(" + UPDATE learning_style_results + SET student_name=?, primary_style=?, secondary_style=?, scores_json=?, pdf_path=? + WHERE student_roll=? + "); + + $stmt->execute([ + $student_name, + $primary, + $secondary, + json_encode($style_score), + $pdfWebPath, + $student_roll + ]); + + } else { + + $stmt = $pdo->prepare(" + INSERT INTO learning_style_results + (student_roll, student_name, primary_style, secondary_style, scores_json, pdf_path) + VALUES (?,?,?,?,?,?) + "); + + $stmt->execute([ + $student_roll, + $student_name, + $primary, + $secondary, + json_encode($style_score), + $pdfWebPath + ]); + } + + $result = true; +} +?> + + + + +OMR Scan + + + + + + +
+ +

OMR Scan

+ +
+ + + +

+ + + +
+ + + +
+ +

βœ… Learning style report generated

+ + +Download PDF + + + + +
+ + + \ No newline at end of file diff --git a/passport_tmp.html b/passport_tmp.html new file mode 100644 index 0000000..f2eee56 --- /dev/null +++ b/passport_tmp.html @@ -0,0 +1,116 @@ + + + + + +Learning Passport + + + + + +
+

RS Learning Lab – Learning Passport

+

A growth-based learning profile (Not a marksheet)

+
+ +
+

Student Details

+
+ Name: Arjun
+ Roll No: 01
+ Class: 10-A +
+
+ +
+

Learning Style Profile

+
+

Primary Learning Style: Visual

+

Secondary Learning Style: Kinesthetic

+
+
+ +
+

Learning Signals

+
+
  • Consistent practice
  • Improving confidence
  • +
+
+
+ +
+

Learning Effectiveness Index (LEI)

+
+ LEI Signal: Moderate +

+ LEI reflects learning consistency and improvement patterns. + This is an internal growth signal, not a score or rank. +

+
+
+ +
+

Important Note

+
+

+ This Learning Passport focuses on how a student learns, + how they improve through practice, and how they apply concepts. +

+ This document is not an examination result, marksheet, or government certificate. +

+
+
+ + + + + diff --git a/php_test.php b/php_test.php new file mode 100644 index 0000000..61ace19 --- /dev/null +++ b/php_test.php @@ -0,0 +1,2 @@ +prepare(" + SELECT q.* + FROM practice_questions q + WHERE q.track_id = ? + AND q.id NOT IN ( + SELECT question_id FROM practice_attempts + WHERE student_name = ? AND is_correct = 1 + ) + ORDER BY q.id ASC + LIMIT 1 +"); +$stmt->execute([$track_id, $student_name]); +$question = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$question) { + header("Location: track_complete.php?track_id=".$track_id); + exit(); +} + +$message = ""; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + + $selected = $_POST['answer']; + + $stmt = $pdo->prepare(" + SELECT * FROM practice_attempts + WHERE student_name = ? AND question_id = ? + "); + $stmt->execute([$student_name, $question['id']]); + $attempt = $stmt->fetch(PDO::FETCH_ASSOC); + + $attempt_count = $attempt ? $attempt['attempt_count'] + 1 : 1; + + $is_correct = ($selected === $question['correct_option']) ? 1 : 0; + + if ($attempt) { + + // If already correct, don't downgrade it + if ($attempt['is_correct'] == 1) { + $is_correct = 1; + } + + $stmt = $pdo->prepare(" + UPDATE practice_attempts + SET attempt_count = ?, is_correct = ? + WHERE id = ? + "); + $stmt->execute([$attempt_count, $is_correct, $attempt['id']]); + +} + else { + $stmt = $pdo->prepare(" + INSERT INTO practice_attempts + (student_name, roll_number, track_id, question_id, attempt_count, is_correct) + VALUES (?, ?, ?, ?, ?, ?) + "); + $stmt->execute([$student_name, $roll_number, $track_id, $question['id'], $attempt_count, $is_correct]); + } + + if ($is_correct) { + $message = "
Well done! Let’s move to the next challenge.
"; + } else { + $message = ($attempt_count == 1) + ? "
{$question['hint_1']}
" + : "
{$question['hint_2']}
"; + } +} +?> + +
+

Python Basics – Practice Challenge

+ + + +
+

+ +
+ +
+ + + +
+
+
+ + + diff --git a/practice_completed.php b/practice_completed.php new file mode 100644 index 0000000..41bd205 --- /dev/null +++ b/practice_completed.php @@ -0,0 +1,67 @@ + + + + + +Practice Completed | RS Learning Lab + + + + + + + +
+ +

πŸŽ‰ Practice Completed

+ +

+Good work, ! +You have successfully completed the Practice Track. +

+ + +πŸš€ Proceed to Reinforcement + + +
+ + + \ No newline at end of file diff --git a/practice_finish.php b/practice_finish.php new file mode 100644 index 0000000..17e4b75 --- /dev/null +++ b/practice_finish.php @@ -0,0 +1,6 @@ + + +
+ +

Reinforcement Completed βœ…

+ +

+ You have completed the reinforcement test. + This stage helps you understand where to improve. +

+ +

+ Your Score: +

+ + +

Learning Suggestions

+
    + +
  • + : + +
  • + +
+ +

+ Excellent! You have a strong understanding of all concepts. +

+ + + + +
+ + diff --git a/practice_retry.php b/practice_retry.php new file mode 100644 index 0000000..25e1ce0 --- /dev/null +++ b/practice_retry.php @@ -0,0 +1,38 @@ + + +
+ +

Good Effort πŸ‘

+ +

+ You scored /. +

+ + + + + +
+ + diff --git a/practice_track.php b/practice_track.php new file mode 100644 index 0000000..d5b69c4 --- /dev/null +++ b/practice_track.php @@ -0,0 +1,144 @@ +prepare(" + SELECT * FROM questions + WHERE id IN (22, 23) + "); + $stmt->execute(); // βœ… FIXED + + $_SESSION['practice_questions'] = $stmt->fetchAll(PDO::FETCH_ASSOC); + $_SESSION['practice_index'] = 0; +} + +/* CURRENT QUESTION */ +$questions = $_SESSION['practice_questions']; +print_r($questions); +exit; + +if (!isset($_SESSION['practice_index'])) { + $_SESSION['practice_index'] = 0; +} + +$index = $_SESSION['practice_index']; + +if ($index >= count($questions)) { + header("Location: practice_completed.php"); + exit; +} + +$q = $questions[$index]; +$qno = $index + 1; +?> + + + + + + + + + +
+ +

🧠 Practice Track

+ +

+ | +

+ +

Q.

+ +
+ + + + + + + + + + + + + + +
+ +
+ + + \ No newline at end of file diff --git a/quiz.php b/quiz.php index dc28102..06e3726 100644 --- a/quiz.php +++ b/quiz.php @@ -1,215 +1,216 @@ 'When learning a new skill, you prefer to:', - 'options' => [ - 'v' => 'Watch a video demonstration.', - 'a' => 'Listen to an instructor explain it.', - 'r' => 'Read the instructions or a manual.', - 'k' => 'Jump in and try it yourself.', - ], - ], - [ - 'question' => 'What do you remember most from a movie?', - 'options' => [ - 'v' => 'The scenery and visuals.', - 'a' => 'The soundtrack and dialogue.', - 'r' => 'The plot details and story.', - 'k' => 'The feelings it gave you.', - ], - ], - [ - 'question' => 'When assembling furniture, you first:', - 'options' => [ - 'v' => 'Look at the diagrams.', - 'a' => 'Ask someone to read the steps to you.', - 'r' => 'Read the entire instruction booklet.', - 'k' => 'Start putting the pieces together.', - ], - ], - [ - 'question' => 'If you need directions to a place, you prefer:', - 'options' => [ - 'v' => 'A map.', - 'a' => 'Someone to tell you the directions.', - 'r' => 'Written turn-by-turn directions.', - 'k' => 'To follow someone or use GPS to guide you as you go.', - ], - ], - [ - 'question' => "You're trying to spell a difficult word. You:", - 'options' => [ - 'v' => 'Visualize the word in your mind.', - 'a' => 'Sound it out.', - 'r' => 'Write it down a few times.', - 'k' => 'Trace the letters with your finger.', - ], - ], - [ - 'question' => 'When you meet someone new, you remember them by:', - 'options' => [ - 'v' => 'Their face.', - 'a' => 'Their name (after hearing it).', - 'r' => 'Their name (after seeing it written).', - 'k' => 'How you felt during your interaction.', - ], - ], - [ - 'question' => "In a new city, you'd love to:", - 'options' => [ - 'v' => 'Go to a museum with great art.', - 'a' => 'Attend a concert or a musical.', - 'r' => 'Visit a library or historical archive.', - 'k' => 'Take a walking tour.', - ], - ], - [ - 'question' => 'What kind of phone app do you use most?', - 'options' => [ - 'v' => 'Photo editing or video streaming.', - 'a' => 'Podcasts or music streaming.', - 'r' => 'News or e-reader apps.', - 'k' => 'Games or fitness tracking apps.', - ], - ], - [ - 'question' => 'When you study for a test, you:', - 'options' => [ - 'v' => 'Create charts and diagrams.', - 'a' => 'Read your notes out loud.', - 'r' => 'Rewrite your notes.', - 'k' => 'Pace around while studying.', - ], - ], - [ - 'question' => 'You are most distracted by:', - 'options' => [ - 'v' => 'Visual clutter or untidiness.', - 'a' => 'Unexpected noises.', - 'r' => 'Poorly written text.', - 'k' => 'Sitting still for too long.', - ], - ], - [ - 'question' => 'How do you prefer to get news updates?', - 'options' => [ - 'v' => 'Watching the news on TV.', - 'a' => 'Listening to a news radio station.', - 'r' => 'Reading a newspaper or news website.', - 'k' => 'Discussing events with others.', - ], - ], - [ - 'question' => 'When you cook a new recipe, you:', - 'options' => [ - 'v' => 'Watch a cooking show.', - 'a' => 'Have someone tell you the steps.', - 'r' => 'Follow a written recipe.', - 'k' => 'Taste and experiment as you go.', - ], - ], - [ - 'question' => "What's your favorite part of a presentation?", - 'options' => [ - 'v' => 'The slides and images.', - 'a' => "The speaker's tone and stories.", - 'r' => 'The handouts and notes.', - 'k' => 'The interactive Q&A session.', - ], - ], - [ - 'question' => 'You learn a new software program by:', - 'options' => [ - 'v' => 'Watching tutorial videos.', - 'a' => 'Following along with audio instructions.', - 'r' => 'Reading the help documentation.', - 'k' => 'Clicking around and figuring it out.', - ], - ], - [ - 'question' => 'When choosing a book, you are drawn to:', - 'options' => [ - 'v' => 'The cover art.', - 'a' => 'An audiobook version.', - 'r' => 'The description on the back.', - 'k' => 'The feel of the book in your hands.', - ], - ], - [ - 'question' => 'How do you best give feedback to someone?', - 'options' => [ - 'v' => 'Show them what to do differently.', - 'a' => 'Talk it through with them.', - 'r' => 'Write them an email or note.', - 'k' => 'Role-play the scenario.', - ], - ], - [ - 'question' => 'When bored, you are most likely to:', - 'options' => [ - 'v' => 'Doodle or watch something.', - 'a' => 'Listen to music or a podcast.', - 'r' => 'Read an article or book.', - 'k' => 'Fidget or walk around.', - ], - ], - [ - 'question' => 'You have to memorize a phone number. You:', - 'options' => [ - 'v' => 'Picture the numbers on the keypad.', - 'a' => 'Repeat the number out loud.', - 'r' => 'Write the number down.', - 'k' => 'Press the numbers on a keypad.', - ], - ], - [ - 'question' => 'Your ideal learning environment has:', - 'options' => [ - 'v' => 'Lots of posters and colors.', - 'a' => 'Occasional background music.', - 'r' => 'A quiet space with reference books.', - 'k' => 'Space to move around.', - ], - ], - [ - 'question' => 'When recalling a happy memory, you think of:', - 'options' => [ - 'v' => 'The sights and what things looked like.', - 'a' => 'The sounds and conversations.', - 'r' => 'The details of what happened in order.', - 'k' => 'The emotions and physical sensations.', - ], - ], + +["q"=>"When learning something new, you prefer:", + "options"=>[ + ["Seeing diagrams or flowcharts","V"], + ["Listening to explanations","A"], + ["Trying it hands-on","K"] + ]], + +["q"=>"In class, you understand better when:", + "options"=>[ + ["Teacher uses diagrams","V"], + ["Teacher explains verbally","A"], + ["You solve problems yourself","K"] + ]], + +["q"=>"While studying, you mostly:", + "options"=>[ + ["Highlight and visualize notes","V"], + ["Read aloud or discuss","A"], + ["Practice problems","K"] + ]], + +["q"=>"You remember concepts better when:", + "options"=>[ + ["You see it written","V"], + ["You hear it explained","A"], + ["You apply it practically","K"] + ]], + +["q"=>"During revision, you prefer:", + "options"=>[ + ["Charts & mind maps","V"], + ["Listening to recordings","A"], + ["Solving sample questions","K"] + ]], + +["q"=>"In group study, you:", + "options"=>[ + ["Draw diagrams","V"], + ["Explain to others","A"], + ["Demonstrate solutions","K"] + ]], + +["q"=>"When confused, you:", + "options"=>[ + ["Look for visual examples","V"], + ["Ask someone to explain","A"], + ["Try it yourself","K"] + ]], + +["q"=>"Your favourite learning tool:", + "options"=>[ + ["Whiteboard / slides","V"], + ["Lectures / podcasts","A"], + ["Labs / workshops","K"] + ]], + +["q"=>"You enjoy classes when:", + "options"=>[ + ["Teacher uses visuals","V"], + ["Teacher tells stories","A"], + ["Class is interactive","K"] + ]], + +["q"=>"Before exams, you:", + "options"=>[ + ["Revise diagrams","V"], + ["Explain topics aloud","A"], + ["Practice repeatedly","K"] + ]], + +["q"=>"You learn coding best by:", + "options"=>[ + ["Flowcharts & logic","V"], + ["Listening to explanations","A"], + ["Writing and running code","K"] + ]], + +["q"=>"Math concepts are easier when:", + "options"=>[ + ["Steps are visual","V"], + ["Teacher explains clearly","A"], + ["You solve many problems","K"] + ]], + +["q"=>"You prefer teachers who:", + "options"=>[ + ["Use visual aids","V"], + ["Explain verbally","A"], + ["Give practice tasks","K"] + ]], + +["q"=>"You grasp ideas faster when:", + "options"=>[ + ["You see examples","V"], + ["You hear explanations","A"], + ["You experiment","K"] + ]], + +["q"=>"You revise better by:", + "options"=>[ + ["Making notes","V"], + ["Discussing","A"], + ["Practicing","K"] + ]], + +["q"=>"You understand science best when:", + "options"=>[ + ["You see diagrams","V"], + ["Teacher explains","A"], + ["You do experiments","K"] + ]], + +["q"=>"You remember lessons longer when:", + "options"=>[ + ["You visualize","V"], + ["You listen","A"], + ["You apply","K"] + ]], + +["q"=>"Learning becomes effective when:", + "options"=>[ + ["Concepts are visual","V"], + ["Concepts are discussed","A"], + ["Concepts are practiced","K"] + ]], + +["q"=>"Your study strength is:", + "options"=>[ + ["Observation","V"], + ["Listening","A"], + ["Doing","K"] + ]], + +["q"=>"You feel confident when:", + "options"=>[ + ["You understand visually","V"], + ["You can explain verbally","A"], + ["You can solve independently","K"] + ]] ]; ?> -
-

VARK Learning Style Quiz

-
-
- - -
- $q): ?> -
-

- $option): ?> -
- - -
- -
- + + + +Learning Style Quiz + + + + + +
+ +

🧠 Learning Style Assessment

+ + + + $q): ?> +
+

Q.

+ + + +
+ +
+ + + + + -
- -
-
- \ No newline at end of file + + \ No newline at end of file diff --git a/register.php b/register.php index 89b0fa8..973c887 100644 --- a/register.php +++ b/register.php @@ -1,87 +1,58 @@ prepare('SELECT id FROM users WHERE email = ?'); - $stmt->execute([$email]); - if ($stmt->fetch()) { - $errors[] = 'Email already in use'; - } else { - $hashed_password = password_hash($password, PASSWORD_DEFAULT); - $stmt = $pdo->prepare('INSERT INTO users (username, email, password) VALUES (?, ?, ?)'); - if ($stmt->execute([$username, $email, $hashed_password])) { - require_once 'mail/MailService.php'; - $subject = 'Welcome to RS Learning Lab!'; - $body = "Hello {$username},

Thank you for registering at RS Learning Lab. We are excited to have you on board!

Best regards,
The RS Learning Lab Team"; - $result = MailService::sendMail($email, $subject, $body, $body); - if ($result['success']) { - //The registration email was sent successfully. - } else { - error_log('Failed to send registration email: ' . $result['error']); - } - header('Location: login.php?registered=true'); - exit; - } else { - $errors[] = 'Failed to register. Please try again.'; - } - } - } - } - ?> - -
-
-
-
Register
-
- -
- {$error}

"; - } - ?> -
- -
-
- - -
-
- - -
-
- - -
- -
-
-
-
-
- \ No newline at end of file + $username = trim($_POST['username']); + $email = trim($_POST['email']); + $password = $_POST['password']; + + if ($username && $email && $password) { + + $hashed = password_hash($password, PASSWORD_DEFAULT); + + $stmt = $pdo->prepare( + "INSERT INTO users (username, email, password) VALUES (?, ?, ?)" + ); + + if ($stmt->execute([$username, $email, $hashed])) { + $_SESSION['success_message'] = + "Your institution registered successfully. Please login."; + header("Location: login.php"); + exit(); + } else { + $error = "Registration failed. Try again."; + } + } else { + $error = "All fields are required."; + } +} +?> + + + + + + + + diff --git a/register_college.php b/register_college.php new file mode 100644 index 0000000..c582dde --- /dev/null +++ b/register_college.php @@ -0,0 +1,245 @@ + + + + + + College Registration | RS Learning Lab + + + + + + + + +
+ RS Learning Lab Logo +

RS Learning Lab

+
+ +
+
+

College Registration

+

+ Register your college to implement learning style diagnostics, + practice-based challenges, and applied learning certifications. +

+ +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ ← Back to Home + +
+
+
+
+ +
+ Β© RS Learning Lab β€” Learning Behaviour Platform +
+ + + diff --git a/register_institution.php b/register_institution.php new file mode 100644 index 0000000..7e90e8c --- /dev/null +++ b/register_institution.php @@ -0,0 +1,144 @@ + + + + + + Register Institution | RS Learning Lab + + + + +
+

Registration

+ +
+ + + + + + + + + +
+
+ + + + prepare($sql); + + if ($stmt->execute([ + $institution_name, + $contact_person, + $institution_address, + $email, + $phone, + $username, + $tempPassword, + $hashedPassword, + $tempPassword // πŸ”₯ IMPORTANT + ])) { + ?> + + + + + Registration Successful + + + +
+

Registration Successful βœ…

+

We will share login credentials shortly.

+
+ + + + + + + + + School Registration | RS Learning Lab + + + + +
+

School Registration

+
+ Register your institution to begin a learning-first journey. +
+ +
+ + + + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + + + + +
+ + +
+ + + diff --git a/registration_success.php b/registration_success.php new file mode 100644 index 0000000..b85ab5e --- /dev/null +++ b/registration_success.php @@ -0,0 +1,103 @@ + + + + Registration Request Received | RS Learning Lab + + + + +
+
πŸŽ‰
+ +

Registration Request Received

+ +

+ Thank you for registering with RS Learning Lab. +

+ +

+ Our team will personally contact you to understand + your learning needs and set up a + dedicated learning space for your institution. +

+ +

+ You will receive further details shortly. +

+ + + + ← Back to Home +
+ + + diff --git a/reinforcement_completed.php b/reinforcement_completed.php new file mode 100644 index 0000000..72a647b --- /dev/null +++ b/reinforcement_completed.php @@ -0,0 +1,94 @@ + + + + +Reinforcement Completed + + + + + + +
+

πŸŽ‰ Reinforcement Completed

+

Excellent work,

+ + + + πŸŽ– Download Learning Badge + + + + + β–Ά Proceed to Final Track + +
+ + + \ No newline at end of file diff --git a/reinforcement_finish.php b/reinforcement_finish.php new file mode 100644 index 0000000..7051d23 --- /dev/null +++ b/reinforcement_finish.php @@ -0,0 +1,26 @@ +prepare(" + SELECT * + FROM questions + WHERE topic_id = ? + AND track_type = 'reinforcement' + ORDER BY RAND() + LIMIT 5 +"); +$stmt->execute([$topic_id]); +$questions = $stmt->fetchAll(PDO::FETCH_ASSOC); + +/* HARD FAIL */ +if (!$questions || count($questions) === 0) { + die("❌ Reinforcement questions not found"); +} +?> + + + + +Reinforcement Track + + + + +
+

🧠 Reinforcement Track

+ + $q): ?> +
+

Q.

+
+ + +
+ + + + + + + + +
+ +
+ + + \ No newline at end of file diff --git a/reports/index.php b/reports/index.php new file mode 100644 index 0000000..1b823f1 --- /dev/null +++ b/reports/index.php @@ -0,0 +1,2 @@ +Reports Dashboard"; diff --git a/reset_password_once.php b/reset_password_once.php new file mode 100644 index 0000000..79c6fda --- /dev/null +++ b/reset_password_once.php @@ -0,0 +1,14 @@ +prepare(" + UPDATE institutions + SET password = ? + WHERE username = 'inst_test' +"); +$stmt->execute([$hash]); + +echo "Password reset successful. New password: " . $newPassword; diff --git a/router.php b/router.php new file mode 100644 index 0000000..4e8f5ca --- /dev/null +++ b/router.php @@ -0,0 +1,24 @@ +prepare("SELECT id, total_questions FROM manual_quizzes WHERE id = ? LIMIT 1"); +$stmtQ->bind_param('i', $quiz_id); +$stmtQ->execute(); +$qres = $stmtQ->get_result(); +if ($qres->num_rows === 0) { + $stmtQ->close(); + die('Quiz not found.'); +} +$quizRow = $qres->fetch_assoc(); +$stmtQ->close(); +$totalQuestions = (int)$quizRow['total_questions']; + +// Normalize answers to totalQuestions length and uppercase +$normalized = []; +for ($i = 0; $i < $totalQuestions; $i++) { + $val = isset($answersArr[$i]) ? strtoupper(trim($answersArr[$i])) : ''; + // accept only A/B/C/D or blank + if (!in_array($val, ['', 'A', 'B', 'C', 'D'], true)) $val = ''; + $normalized[] = $val; +} + +// join as CSV string e.g. "A,B,,D,..." +$answersStr = implode(',', $normalized); + +// Save to DB (manual_answers table) +$createdBy = (int)$_SESSION['user_id']; + +$stmt = $conn->prepare(" + INSERT INTO manual_answers + (quiz_id, student_name, roll_no, answers, created_by_user_id) + VALUES (?, ?, ?, ?, ?) +"); +if (!$stmt) { + die('DB prepare failed: ' . $conn->error); +} +$stmt->bind_param('isssi', $quiz_id, $student_name, $roll_no, $answersStr, $createdBy); + +if ($stmt->execute()) { + $insertId = $stmt->insert_id; + $stmt->close(); + // Redirect to result/profile page for this answer + header('Location: manual_result_profile.php?answer_id=' . $insertId); + exit; +} else { + $err = $stmt->error; + $stmt->close(); + die('Failed to save answers: ' . htmlspecialchars($err)); +} diff --git a/school/coding_tracks.php b/school/coding_tracks.php new file mode 100644 index 0000000..a33a466 --- /dev/null +++ b/school/coding_tracks.php @@ -0,0 +1,83 @@ +r + + + +Coding Challenges + + + + +
+

πŸ’» Coding Challenges

+ +
+ + + + + + + + + + + + + + + +
+ + + + + +
+
+
+ + + diff --git a/school/dashboard.php b/school/dashboard.php new file mode 100644 index 0000000..a929415 --- /dev/null +++ b/school/dashboard.php @@ -0,0 +1,98 @@ + + + + + + School Dashboard | RS Learning Lab + + + + + + + + diff --git a/school/learning_assessment.php b/school/learning_assessment.php new file mode 100644 index 0000000..ae8ff47 --- /dev/null +++ b/school/learning_assessment.php @@ -0,0 +1,62 @@ + + + + + Learning Style Assessment | RS Learning Lab + + + + +
+

🧠 Learning Style Assessment

+

+ Choose how you want to conduct the assessment for students. + This focuses on learning behaviour, not marks. +

+ + + + + +

+ +
+ + + diff --git a/school/reports.php b/school/reports.php new file mode 100644 index 0000000..d33edbb --- /dev/null +++ b/school/reports.php @@ -0,0 +1,52 @@ + + + + + Reports & Insights | RS Learning Lab + + + + +
+

πŸ“Š Reports & Learning Insights

+

+ This section will show: +

+
    +
  • Class-wise learning styles
  • +
  • Learning Effectiveness Index (LEI)
  • +
  • Momentum signals (Active / Slow / Stalled)
  • +
  • Learning Passports
  • +
+ +

+ (Data-driven reports already connected. UI expansion next.) +

+ + +
+ + + diff --git a/school_registration.php b/school_registration.php new file mode 100644 index 0000000..d4b2758 --- /dev/null +++ b/school_registration.php @@ -0,0 +1,173 @@ + + + + + + +School Registration | RS Learning Lab + + + + + + +
+

School Registration

+
+ Register your institution with RS Learning Lab +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ ← Back to Home + +
+ +
+
+ +
+Β© RS Learning Lab β€’ Learning Behaviour Platform +
+ + + diff --git a/self_assessment_start.php b/self_assessment_start.php new file mode 100644 index 0000000..7a90257 --- /dev/null +++ b/self_assessment_start.php @@ -0,0 +1,95 @@ + + + + + Self Assessment | RS Learning Lab + + + + + +
+

πŸ’» Self-Assessment

+

+ Enter your details to begin the learning style assessment. +

+ +
+ +
+ + + + + + + +
+
+ + + ← Back to Assessment Mode + +
+ + + diff --git a/self_student_entry.php b/self_student_entry.php new file mode 100644 index 0000000..62b32b9 --- /dev/null +++ b/self_student_entry.php @@ -0,0 +1,32 @@ + + +
+ +

Self-Assessment

+

Enter your details to begin the learning style assessment.

+ +
+ +
+ + +
+ +
+ + +
+ + + +
+ + ← Back + +
+ + + diff --git a/session_check.php b/session_check.php new file mode 100644 index 0000000..466ee06 --- /dev/null +++ b/session_check.php @@ -0,0 +1,5 @@ +"; +print_r($_SESSION); +echo ""; diff --git a/slowing_learners.php b/slowing_learners.php new file mode 100644 index 0000000..aa2419e --- /dev/null +++ b/slowing_learners.php @@ -0,0 +1,116 @@ +connect_error) { + die("Connection failed: " . $conn->connect_error); +} + +/* + SLOWING LEARNERS: + - Last practice between 3 and 5 days ago +*/ + +$sql = " +SELECT + pa.roll_no, + MAX(pa.last_attempt_at) AS last_practice_date, + DATEDIFF(NOW(), MAX(pa.last_attempt_at)) AS days_gap +FROM practice_attempts pa +GROUP BY pa.roll_no +HAVING + days_gap BETWEEN 3 AND 5 +ORDER BY days_gap DESC +"; + +$result = $conn->query($sql); +?> + + + + + Slowing Learners + + + + +

🟑 Slowing Learners (3–5 Days Gap)

+
+ These students are active but learning momentum is slowing. +
+ + + + + + + + + num_rows > 0) { + while ($row = $result->fetch_assoc()) { + echo " + + + + "; + } + } else { + echo " + + "; + } + ?> + +
Roll NoLast Practice DateDays Since Last Practice
{$row['roll_no']}{$row['last_practice_date']}{$row['days_gap']} day(s)
No slowing learners πŸŽ‰
+ + + + +close(); ?> diff --git a/stalled_students.php b/stalled_students.php new file mode 100644 index 0000000..2e30142 --- /dev/null +++ b/stalled_students.php @@ -0,0 +1,97 @@ +connect_error) { + die("Connection failed: " . $conn->connect_error); +} + +$sql = " +SELECT + pa.roll_no, + MAX(pa.last_attempt_at) AS last_practice_date +FROM practice_attempts pa +GROUP BY pa.roll_no +HAVING + MAX(pa.last_attempt_at) < NOW() - INTERVAL 10 DAY +ORDER BY last_practice_date ASC +LIMIT 25 +"; + +$result = $conn->query($sql); +?> + + + + + Stalled Learners + + + + +

🚨 Stalled Learners (10+ Days No Practice)

+ + + + + + + + num_rows > 0) { + while ($row = $result->fetch_assoc()) { + echo " + + + "; + } + } else { + echo ""; + } + ?> + +
Roll NoLast Practice Date
{$row['roll_no']}{$row['last_practice_date']}
No stalled learners πŸŽ‰
+ + + + +close(); ?> diff --git a/student/final_assessment.php b/student/final_assessment.php new file mode 100644 index 0000000..462e9b9 --- /dev/null +++ b/student/final_assessment.php @@ -0,0 +1,72 @@ + + + + + Final Assessment | RS Learning Lab + + + + +
+

🏁 Final Assessment

+

Student: ()

+ +
+ +
+

1️⃣ What is the output of: print(2 + 3 * 4)?

+
+ +
+ +
+

2️⃣ Which keyword is used to define a function in Python?

+
+ +
+ + +
+
+ + + \ No newline at end of file diff --git a/student/leaderboard.php b/student/leaderboard.php new file mode 100644 index 0000000..1e8b24f --- /dev/null +++ b/student/leaderboard.php @@ -0,0 +1,69 @@ +query(" + SELECT student_name, class, MAX(score) as best_score + FROM project_submissions + WHERE status='approved' + GROUP BY student_id + ORDER BY best_score DESC + LIMIT 10 +"); + +$students = $stmt->fetchAll(); +?> + + + + +Leaderboard | RS Learning Lab + + + + +

πŸ† Leaderboard

+ + + + + + + + + + + + + + + + + + +
RankNameClassScore
+ + + \ No newline at end of file diff --git a/student/lei_dashboard.php b/student/lei_dashboard.php new file mode 100644 index 0000000..91f6435 --- /dev/null +++ b/student/lei_dashboard.php @@ -0,0 +1,135 @@ +prepare($sql); +$stmt->bind_param("i", $student_id); +$stmt->execute(); +$result = $stmt->get_result(); + +if ($result->num_rows == 0) { + die("LEI not calculated yet"); +} + +$data = $result->fetch_assoc(); + +$total_lei = $data['total_lei']; +$level = $data['lei_level']; + +$consistency = $data['consistency_score']; +$attempts = $data['attempt_score']; +$improvement = $data['improvement_score']; +$style = $data['style_match_score']; +?> + + + +My Learning Effort Index + + + + + +
+
/100
+
Learning Effort Level:
+ +
+
Consistency
+
+
+ +
+
Attempts
+
+
+ +
+
Improvement
+
+
+ +
+
Learning Style Match
+
+
+ +
+ = 80) { + echo "Excellent consistency! Keep going πŸš€"; + } elseif ($total_lei >= 60) { + echo "Good effort. Try learning more regularly to improve πŸ‘"; + } else { + echo "Start small. Regular practice will boost your LEI πŸ’‘"; + } + ?> +
+
+ + + diff --git a/student/login.php b/student/login.php new file mode 100644 index 0000000..454b652 --- /dev/null +++ b/student/login.php @@ -0,0 +1,88 @@ + + + + + Student Login | RS Learning Lab + + + + +
+

Student Login

+ + +

+ + +
+ + + +
+
+ + + \ No newline at end of file diff --git a/student_assessment.php b/student_assessment.php new file mode 100644 index 0000000..5101bae --- /dev/null +++ b/student_assessment.php @@ -0,0 +1,134 @@ + '01', 'name' => 'Arjun K'], + ['roll' => '02', 'name' => 'Meena S'], + ['roll' => '03', 'name' => 'Rahul M'], + ['roll' => '04', 'name' => 'Priya R'] +]; +?> + + + + + Student Assessment – RS Learning Lab + + + + + +
+

Facilitated Assessment Session

+

+ Session:
+ Facilitator: +

+ +

OMR-based Learning Behaviour Assessment

+ + + + + + + + + + + + + + + + + + +
Roll NoStudent NameAssessment Mode
+ + Scan OMR + +
+ +
+ Important:
+ β€’ This assessment does not evaluate right or wrong answers.
+ β€’ OMR is used only to capture learning preferences at scale.
+ β€’ Results focus on learning style, improvement signals, and consistency.
+ β€’ No marks, ranks, or exam pressure involved. +
+ +

+ Β© 2026 RS Learning Lab Β· Facilitated Learning Mode +

+
+ + + diff --git a/student_dashboard.php b/student_dashboard.php new file mode 100644 index 0000000..3fdd605 --- /dev/null +++ b/student_dashboard.php @@ -0,0 +1,164 @@ +prepare(" + SELECT institution_name + FROM institutions + WHERE id = ? + LIMIT 1 +"); +$institutionId = $_SESSION['institution_id'] ?? null; +$institution = $stmt->fetch(PDO::FETCH_ASSOC); + +// Fallback (safety) +$institutionName = $institution['institution_name'] ?? 'School'; +?> + + + + + School Dashboard | RS Learning Lab + + + + + + +
+

🏫 β€” School Dashboard

+

Manage learning style assessments, coding practice, and reports.

+
+ +
+ + +
+

🧠 Learning Style Assessment

+

+ Understand how students learn using facilitated and self-assessment + modes. No marks. No ranks. +

+ + +
+ + +
+

πŸ’» Coding Challenges

+

+ Practice-based coding tracks to improve confidence, logic, + and application skills. +

+ +
+ + +
+

πŸ“Š Reports & Insights

+

+ View class-wise learning patterns, momentum signals, + and learning passports. +

+ +
+ +
+ +
+ Β© RS Learning Lab β€” School Learning Behaviour Platform +
+ + + diff --git a/student_details.php b/student_details.php new file mode 100644 index 0000000..73a2271 --- /dev/null +++ b/student_details.php @@ -0,0 +1,80 @@ + + + + +
+ +

+ +

+ +

+ + Enter the student details before starting the assessment. + + Please enter your details to begin the assessment. + +

+ + +
+ + +
+ + + + + + + + +
+ + ← Back +
+ + + diff --git a/student_list.php b/student_list.php new file mode 100644 index 0000000..98b8c45 --- /dev/null +++ b/student_list.php @@ -0,0 +1,52 @@ + + + + +
+ +

+ Class: +

+ +

+ Teacher: +

+ + + + + + + + + + + + + + + + + + +
Roll NoStudent NameAction
+ + Start Assessment + +
+ + ← Back to Dashboard +
+ + + diff --git a/student_profile.php b/student_profile.php new file mode 100644 index 0000000..1384051 --- /dev/null +++ b/student_profile.php @@ -0,0 +1,176 @@ +connect_error) die("DB Connection failed"); + +/* ===== PRACTICE BEHAVIOUR ===== */ +$practice = $conn->query(" + SELECT + COUNT(*) AS attempts, + MAX(last_attempt_at) AS last_practice, + COUNT(DISTINCT DATE(last_attempt_at)) AS active_days + FROM practice_attempts + WHERE roll_no = '$roll_no' +")->fetch_assoc(); + +/* ===== SAFE DEFAULTS ===== */ +$attempts = (int)($practice['attempts'] ?? 0); +$active_days = (int)($practice['active_days'] ?? 0); + +if ($practice['last_practice']) { + $days_since_last = (new DateTime())->diff(new DateTime($practice['last_practice']))->days; +} else { + $days_since_last = 999; // never practiced +} + +/* ===== LEI CALCULATION ===== */ +$effort_score = min(40, $attempts * 2); +$consistency_score = min(40, $active_days * 3); +$recency_score = max(0, 20 - $days_since_last); + +$lei = $effort_score + $consistency_score + $recency_score; +$lei = min(100, $lei); + +/* ===== LEI BAND ===== */ +if ($lei >= 70) { + $lei_status = "Stable Learner"; + $lei_color = "green"; +} elseif ($lei >= 40) { + $lei_status = "Needs Reinforcement"; + $lei_color = "yellow"; +} else { + $lei_status = "Immediate Support"; + $lei_color = "red"; +} + +$conn->close(); +?> + + + + +Student Profile | RS Learning Lab + + + + + + + +
+ +

Student Learning Passport

+
Roll No:
+ +
+ + +
+
Learning Effectiveness Index
+
+ +
+
+
+ + +
+

Total Practice Attempts

+

+
+ +
+

Active Days (last period)

+

+
+ +
+

Days Since Last Practice

+

+
+ +
+ +
+ + + diff --git a/style.css b/style.css new file mode 100644 index 0000000..1c7e99b --- /dev/null +++ b/style.css @@ -0,0 +1,60 @@ +body { + margin: 0; + font-family: "Segoe UI", Arial, sans-serif; + background: linear-gradient(135deg, #050b18, #0b1630); + color: #ffffff; +} + +.container { + max-width: 1100px; + margin: auto; + padding: 30px; +} + +h1, h2, h3 { + margin: 0 0 10px; +} + +.card { + background: linear-gradient(145deg, #0d1b33, #060c1d); + border-radius: 16px; + padding: 25px; + margin-bottom: 25px; + box-shadow: 0 10px 30px rgba(0,0,0,0.4); +} + +.card p { + color: #cfd8ff; +} + +.btn { + display: inline-block; + padding: 10px 18px; + background: #1ea7ff; + color: #000; + border-radius: 10px; + text-decoration: none; + font-weight: bold; +} + +.btn:hover { + background: #4cc3ff; +} + +.status-complete { + color: #39d98a; + font-weight: bold; +} + +.status-locked { + color: #ff6b6b; + font-weight: bold; +} + +.badge { + background: #f4c430; + color: #000; + padding: 4px 10px; + border-radius: 8px; + font-size: 13px; +} diff --git a/teacher/change_password.php b/teacher/change_password.php new file mode 100644 index 0000000..d9b878c --- /dev/null +++ b/teacher/change_password.php @@ -0,0 +1,156 @@ +prepare(" + UPDATE teachers + SET password_hash = ?, must_change_password = 0 + WHERE id = ? + "); + + $stmt->execute([ + $password_hash, + $_SESSION['teacher_id'] + ]); + + header("Location: /rs_lab/teacher/dashboard.php"); + exit; + } +} +?> + + + + +Change Password + + + + + + + +
+
+

Change Password

+ + +

+ + +
+ + + +
+
+
+ + + + + + \ No newline at end of file diff --git a/teacher/dashboard.php b/teacher/dashboard.php new file mode 100644 index 0000000..356d9b4 --- /dev/null +++ b/teacher/dashboard.php @@ -0,0 +1,146 @@ +prepare(" + SELECT username, class_handled + FROM teachers + WHERE id = ? +"); +$stmt->execute([$_SESSION['teacher_id']]); +$teacher = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$teacher) { + die("Invalid teacher session"); +} +?> + + + + +Teacher Dashboard + + + + + + + + + + + \ No newline at end of file diff --git a/teacher/login.php b/teacher/login.php new file mode 100644 index 0000000..892d914 --- /dev/null +++ b/teacher/login.php @@ -0,0 +1,106 @@ +prepare(" + SELECT id, password_hash, must_change_password + FROM teachers + WHERE username = ? + LIMIT 1 + "); + $stmt->execute([$username]); + $teacher = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($teacher && password_verify($password, $teacher['password_hash'])) { + + $_SESSION['teacher_id'] = $teacher['id']; + + if ((int)$teacher['must_change_password'] === 1) { + header("Location: /rs_lab/teacher/change_password.php"); + } else { + header("Location: /rs_lab/teacher/dashboard.php"); + } + exit; + + } else { + $error = "Invalid login credentials"; + } + } +} +?> + + + + + Teacher Login | RS Learning Lab + + + + +
+

Teacher Login

+ + +

+ + + +
+ + + +
+
+ + + \ No newline at end of file diff --git a/teacher/project_reviews.php b/teacher/project_reviews.php new file mode 100644 index 0000000..6cee1cd --- /dev/null +++ b/teacher/project_reviews.php @@ -0,0 +1,76 @@ +prepare(" + SELECT ps.*, s.student_name + FROM project_submissions ps + JOIN students s ON ps.student_id = s.id + ORDER BY ps.submitted_at DESC +"); +$stmt->execute(); +$projects = $stmt->fetchAll(); +?> + + + + +Project Reviews + + + + + +
+ +

Student Projects πŸ§ πŸ’»

+ + + +
+ +

+ +

Submitted At:

+ +
+
+
+ +
+ + + +
+
+ +
+ + +
+ + + +
+ +
+ + + +
+ + + \ No newline at end of file diff --git a/teacher/reports.php b/teacher/reports.php new file mode 100644 index 0000000..ff30010 --- /dev/null +++ b/teacher/reports.php @@ -0,0 +1,131 @@ +prepare(" + SELECT username, class_handled + FROM teachers + WHERE id = ? +"); +$stmt->execute([$_SESSION['teacher_id']]); +$teacher = $stmt->fetch(PDO::FETCH_ASSOC); + +$class = $teacher['class_handled']; + +/* Total */ +$stmt = $pdo->prepare("SELECT COUNT(*) FROM students WHERE class = ?"); +$stmt->execute([$class]); +$total_students = $stmt->fetchColumn(); + +/* Active */ +$stmt = $pdo->prepare(" + SELECT COUNT(DISTINCT pa.roll_no) + FROM practice_attempts pa + JOIN students s ON s.roll_no = pa.roll_no + WHERE s.class = ? + AND pa.last_attempt_at >= NOW() - INTERVAL 7 DAY +"); +$stmt->execute([$class]); +$active = $stmt->fetchColumn(); + +/* Slowing */ +$stmt = $pdo->prepare(" + SELECT COUNT(DISTINCT pa.roll_no) + FROM practice_attempts pa + JOIN students s ON s.roll_no = pa.roll_no + WHERE s.class = ? + AND pa.last_attempt_at BETWEEN + NOW() - INTERVAL 10 DAY AND NOW() - INTERVAL 8 DAY +"); +$stmt->execute([$class]); +$slowing = $stmt->fetchColumn(); + +/* Stalled */ +$stmt = $pdo->prepare(" + SELECT COUNT(*) + FROM ( + SELECT pa.roll_no + FROM practice_attempts pa + JOIN students s ON s.roll_no = pa.roll_no + WHERE s.class = ? + GROUP BY pa.roll_no + HAVING MAX(pa.last_attempt_at) < NOW() - INTERVAL 10 DAY + ) t +"); +$stmt->execute([$class]); +$stalled = $stmt->fetchColumn(); +?> + + + + +Reports + + + + + + + +
+ +

Reports

+ +
+ +
+

Total Students

+

+
+ +
+

Active Learners

+

+
+ +
+

Slowing Learners

+

+
+ +
+

Stalled Learners

+

+
+ +
+ +
+ + + \ No newline at end of file diff --git a/teacher/save_review.php b/teacher/save_review.php new file mode 100644 index 0000000..d39ca79 --- /dev/null +++ b/teacher/save_review.php @@ -0,0 +1,16 @@ +prepare(" + UPDATE project_submissions + SET score=?, feedback=? + WHERE id=? +"); +$stmt->execute([$score, $feedback, $id]); + +header("Location: project_reviews.php"); +exit; \ No newline at end of file diff --git a/teacher_dashboard.php b/teacher_dashboard.php new file mode 100644 index 0000000..d03e3b4 --- /dev/null +++ b/teacher_dashboard.php @@ -0,0 +1,131 @@ +connect_error) die("DB Connection failed"); + +// Active learners (last 7 days) +$active = $conn->query(" + SELECT COUNT(DISTINCT roll_no) AS cnt + FROM practice_attempts + WHERE last_attempt_at >= NOW() - INTERVAL 7 DAY +")->fetch_assoc()['cnt'] ?? 0; + +// Slowing learners (8–10 days) +$slowing = $conn->query(" + SELECT COUNT(*) AS cnt FROM ( + SELECT roll_no, MAX(last_attempt_at) last_practice + FROM practice_attempts + GROUP BY roll_no + HAVING last_practice BETWEEN + NOW() - INTERVAL 10 DAY AND NOW() - INTERVAL 8 DAY + ) t +")->fetch_assoc()['cnt'] ?? 0; + +// Stalled learners (10+ days) +$stalled = $conn->query(" + SELECT COUNT(*) AS cnt FROM ( + SELECT roll_no, MAX(last_attempt_at) last_practice + FROM practice_attempts + GROUP BY roll_no + HAVING last_practice < NOW() - INTERVAL 10 DAY + ) t +")->fetch_assoc()['cnt'] ?? 0; + +$conn->close(); +?> + + + + +Teacher Dashboard | RS Learning Lab + + + + + +

Teacher Dashboard

+
+Monitor learning activity, momentum, and student engagement. +
+ +
+ +
+

🟒 Active Learners

+

Practiced in last 7 days

+
+
+ +
+

🟑 Slowing Learners

+

Practice gap forming

+
+
+ +
+

πŸ”΄ Stalled Learners

+

10+ days no practice

+
+
+ +
+ + + diff --git a/teacher_login.php b/teacher_login.php new file mode 100644 index 0000000..32c4823 --- /dev/null +++ b/teacher_login.php @@ -0,0 +1,191 @@ +connect_error){ + die("DB Connection Failed"); +} + +$error = ""; + +if($_SERVER['REQUEST_METHOD']=="POST"){ + +$username = trim($_POST['username'] ?? ''); +$password = trim($_POST['password'] ?? ''); + +if($username=="" || $password==""){ +$error = "Username and password required"; +} + +else{ + +$stmt = $conn->prepare(" +SELECT id,username,password,temp_password +FROM teachers +WHERE username=? +LIMIT 1 +"); + +$stmt->bind_param("s",$username); +$stmt->execute(); + +$res = $stmt->get_result(); + +if($res && $res->num_rows==1){ + +$teacher = $res->fetch_assoc(); + +$loginOk = false; + +// temp password +if (!empty($teacher['temp_password']) && $password === $teacher['temp_password']) { + $loginOk = true; +} + +// normal password (plain text) +if (!empty($teacher['password']) && $password === $teacher['password']) { + $loginOk = true; +} +// Case 2: hashed password +if (!empty($teacher['password']) && password_verify($password, $teacher['password'])) { + $loginOk = true; +} + +if ($loginOk) { + + session_regenerate_id(true); + + // clear institution session + unset($_SESSION['institution_id']); + unset($_SESSION['institution_type']); + unset($_SESSION['institution_name']); + + // set teacher session + $_SESSION['teacher_id'] = $teacher['id']; + $_SESSION['teacher_username'] = $teacher['username']; + $_SESSION['role'] = 'teacher'; + + // redirect + if ((int)$teacher['must_change_password'] === 1) { + header("Location: /rs_lab/teacher/change_password.php"); + } else { + header("Location: /rs_lab/teacher_dashboard.php"); + } + + exit; +} + +session_regenerate_id(true); + +/* clear institution session */ +unset($_SESSION['institution_id']); +unset($_SESSION['institution_name']); +unset($_SESSION['institution_type']); + +/* teacher session */ +$_SESSION['teacher_id'] = $teacher['id']; +$_SESSION['teacher_username'] = $teacher['username']; +$_SESSION['role'] = "teacher"; + +/* DEBUG CHECK */ +header("Location: /rs_lab/school/dashboard.php"); +exit; + +} + +} + +$error="Invalid login credentials"; + +} +?> + + + + +Teacher Login | RS Learning Lab + + + + + + + +
+ +

Teacher Login

+ +
+ + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + \ No newline at end of file diff --git a/teacher_session.php b/teacher_session.php new file mode 100644 index 0000000..a32d51c --- /dev/null +++ b/teacher_session.php @@ -0,0 +1,91 @@ + $roll, + 'name' => $name + ]; + } + } + fclose($handle); + } + + if (count($students) === 0) { + $error = "CSV file does not contain valid student data."; + } else { + // Save session + $_SESSION['teacher_name'] = $teacher_name; + $_SESSION['class_name'] = $class_name; + $_SESSION['students'] = $students; + + header("Location: student_list.php"); + exit(); + } + } +} +?> + + + +
+ +

Start Class Session

+

Enter class details and upload student list.

+ + +
+ + +
+ + + + + + + + + + + +
+ + ← Back to Dashboard +
+ + + diff --git a/teacher_students.php b/teacher_students.php new file mode 100644 index 0000000..e5197ec --- /dev/null +++ b/teacher_students.php @@ -0,0 +1,170 @@ +connect_error) die("DB Connection failed"); + +/* +STATUS LOGIC: +Active -> last practice <= 7 days +Slowing -> 8–10 days +Stalled -> >10 days OR never practiced +*/ + +$sql = " +SELECT + lp.roll_no, + MAX(pa.last_attempt_at) AS last_practice, + DATEDIFF(CURDATE(), MAX(pa.last_attempt_at)) AS days_gap +FROM learning_profiles lp +LEFT JOIN practice_attempts pa + ON pa.roll_no = lp.roll_no +GROUP BY lp.roll_no +ORDER BY days_gap DESC +"; + +$result = $conn->query($sql); +?> + + + + +Students | RS Learning Lab + + + + + + + +

Students

+
+Learning behaviour overview (focus on consistency, not scores) +
+ + + + + + + + + +fetch_assoc()): + + $days = $row['days_gap']; + + if ($row['last_practice'] === null) { + $status = "stalled"; + $statusLabel = "Stalled"; + } elseif ($days <= 7) { + $status = "active"; + $statusLabel = "Active"; + } elseif ($days <= 10) { + $status = "slowing"; + $statusLabel = "Slowing"; + } else { + $status = "stalled"; + $statusLabel = "Stalled"; + } +?> + + + + + + + + + + +
Roll NoLast PracticeDays Since Last PracticeStatus
+ + + + + + + + + + + +
+ + + + +close(); ?> diff --git a/test.html b/test.html new file mode 100644 index 0000000..2bf7d6c --- /dev/null +++ b/test.html @@ -0,0 +1,9 @@ + + + + Test + + + TEST PAGE OK + + diff --git a/test.php b/test.php index 31a6f58..299e5d7 100644 --- a/test.php +++ b/test.php @@ -1,21 +1,2 @@ "; - -echo "MAIL_TRANSPORT: " . getenv('MAIL_TRANSPORT') . "\n"; -echo "SMTP_HOST: " . getenv('SMTP_HOST') . "\n"; -echo "SMTP_PORT: " . getenv('SMTP_PORT') . "\n"; -echo "SMTP_SECURE: " . getenv('SMTP_SECURE') . "\n"; -echo "SMTP_USER: " . getenv('SMTP_USER') . "\n"; -echo "SMTP_PASS: " . getenv('SMTP_PASS') . "\n"; -echo "MAIL_FROM: " . getenv('MAIL_FROM') . "\n"; -echo "MAIL_FROM_NAME: " . getenv('MAIL_FROM_NAME') . "\n"; -echo "MAIL_REPLY_TO: " . getenv('MAIL_REPLY_TO') . "\n"; -echo "MAIL_TO: " . getenv('MAIL_TO') . "\n"; - -echo ""; - -phpinfo(); \ No newline at end of file +echo "RS LAB WORKING"; diff --git a/test_header.php b/test_header.php new file mode 100644 index 0000000..9303699 --- /dev/null +++ b/test_header.php @@ -0,0 +1,3 @@ + +

Header & Footer Test Successful

+ diff --git a/test_image_pdf.php b/test_image_pdf.php new file mode 100644 index 0000000..69fd6ed --- /dev/null +++ b/test_image_pdf.php @@ -0,0 +1,18 @@ +set('isRemoteEnabled', true); + +$dompdf = new Dompdf($options); + +$img = __DIR__ . '/assets/rs_logo.png'; + +$html = "

Image Test

"; + +$dompdf->loadHtml($html); +$dompdf->render(); +$dompdf->stream("image_test.pdf", ["Attachment"=>false]); diff --git a/test_password.php b/test_password.php new file mode 100644 index 0000000..f3805a6 --- /dev/null +++ b/test_password.php @@ -0,0 +1,5 @@ +query("SELECT status FROM institutions WHERE id=$id AND deleted_at IS NULL"); +$row = $res->fetch_assoc(); + +if($row){ + $newStatus = ($row['status'] == 'active') ? 'inactive' : 'active'; + $conn->query("UPDATE institutions SET status='$newStatus' WHERE id=$id"); +} + +header("Location: admin_dashboard.php?view=".$view); +exit; \ No newline at end of file diff --git a/track_complete.php b/track_complete.php new file mode 100644 index 0000000..4298ca9 --- /dev/null +++ b/track_complete.php @@ -0,0 +1,53 @@ + + + + +
+

πŸŽ‰ Track Completed!

+ +

Python Basics – Learning Badge

+ +

+ Well done, ! + You have successfully completed the practice challenges in this track. +

+ +

+ This badge recognises your learning effort and consistency. +

+ + + + Download Learning Badge + + + +
diff --git a/track_intro.php b/track_intro.php new file mode 100644 index 0000000..d2f122c --- /dev/null +++ b/track_intro.php @@ -0,0 +1,82 @@ +prepare("SELECT * FROM tracks WHERE id = ?"); +$stmt->execute([$track_id]); +$track = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$track || $track['status'] !== 'active') { + header("Location: tracks.php"); + exit(); +} +?> + +
+ +

+ +

+ +

+ +
+

πŸ“˜ About This Learning Track

+

+ This track is designed to help you understand the basics of Python programming step by step. + You do not need any prior coding knowledge to start. +

+
+ +
+

🧠 What You Will Learn

+
    +
  • How programming works
  • +
  • How Python prints output
  • +
  • Understanding variables and values
  • +
  • Basic logical thinking using code
  • +
  • Reading simple Python code
  • +
+
+ +
+

πŸ§ͺ How the Practice Challenges Work

+
    +
  • This track contains 15 practice challenges
  • +
  • Each challenge focuses on one simple concept
  • +
  • You will receive guidance if your first attempt is incorrect
  • +
  • Mistakes are part of the learning process
  • +
+
+ +
+

πŸŽ–οΈ Learning Badge

+

+ After completing all practice challenges in this track, + you will unlock the Python Basics – Learning Badge. +

+

+ This badge represents your learning effort and consistency. +

+
+ + + + ← Back to Tracks + +
+ + + diff --git a/tracks.php b/tracks.php new file mode 100644 index 0000000..64822b4 --- /dev/null +++ b/tracks.php @@ -0,0 +1,52 @@ + + + + +
+

Learning Tracks

+

Select a track to begin your learning journey.

+ + +
+

Python Basics

+

+ Learn Python from the fundamentals to problem-solving. + This track focuses on understanding concepts, not memorizing code. +

+ Start Practice Track +
+ + +
+

Java Programming

+

Coming Soon

+ Locked +
+ +
+

C Programming

+

Coming Soon

+ Locked +
+ + +
diff --git a/tracks/final.php b/tracks/final.php new file mode 100644 index 0000000..6818860 --- /dev/null +++ b/tracks/final.php @@ -0,0 +1,47 @@ + + + + + +Final Track + + + + + +

Final Assessment

+

One attempt Β· Timer based Β· Certificate after this

+

Time left: 60s

+ +
+

Which data structure uses FIFO?

+
+
+ +
+ +
+ + + diff --git a/tracks/practice.php b/tracks/practice.php new file mode 100644 index 0000000..8031a10 --- /dev/null +++ b/tracks/practice.php @@ -0,0 +1,240 @@ + + + + +Practice Not Started + + + +
+

Practice Session Not Started

+

Please start practice from your dashboard.

+Go to Dashboard +
+ + + 0) { + $sql = "SELECT * FROM practice_questions + WHERE language=? AND concept_id=? + AND id NOT IN ($placeholders) + ORDER BY RAND() LIMIT 1"; + $stmt = $pdo->prepare($sql); + $stmt->execute(array_merge([$lang,$concept], $_SESSION['used_questions'])); +} else { + $stmt = $pdo->prepare("SELECT * FROM practice_questions + WHERE language=? AND concept_id=? ORDER BY RAND() LIMIT 1"); + $stmt->execute([$lang,$concept]); +} + +$q = $stmt->fetch(PDO::FETCH_ASSOC); +/* ===== MOTIVATION LINES ===== */ +$motivation_lines = [ + "Good learning takes practice. You're doing great.", + "Every attempt sharpens your understanding.", + "No pressure. Explore, try, improve.", + "Consistency builds confidence.", + "Mistakes here are stepping stones." +]; +$motivation = $motivation_lines[array_rand($motivation_lines)]; + + +if (!$q) { + $_SESSION['practice_completed'] = true; +?> + + + +Practice Completed + + + +

Practice Completed πŸŽ‰

+Proceed to Reinforcement + + + + + + + +Practice Track + + + + +
+ +

Q.

+ +
+ + + + + + + + + + + + +
+ +
+ + + +
+ +
+Motivation: +
+
+Hint: +
+ +
+ + + diff --git a/tracks/practice_submit.php b/tracks/practice_submit.php new file mode 100644 index 0000000..8fcae0d --- /dev/null +++ b/tracks/practice_submit.php @@ -0,0 +1,67 @@ + + + + + + + + + + +
+ + + +

βœ… Correct! Super da πŸ”₯

+

You’re improving fast πŸš€ Keep going!

+ +Next Question β†’ + + + +

πŸ‘ Almost there!

+

Small mistake... but you're learning πŸ’‘

+ +

Hint:

+ +Try Again πŸ” + + + +
+ + + \ No newline at end of file diff --git a/tracks/reinforcement.php b/tracks/reinforcement.php new file mode 100644 index 0000000..2c4490e --- /dev/null +++ b/tracks/reinforcement.php @@ -0,0 +1,49 @@ += 2) { + $_SESSION['badge_earned'] = true; + header("Location: final.php"); + exit; + } + + $message = "Good try! πŸ’ͺ Think again. You have one more attempt."; +} +?> + + + + +Reinforcement Track + + + + +

Reinforcement Track

+

Strengthen logic. Badge unlocks here.

+ +$message

"; ?> + +
+

What is the output of: 5 + 3 * 2 ?

+
+
+
+ +
+ +
+ + + diff --git a/tracks/select_language.php b/tracks/select_language.php new file mode 100644 index 0000000..10286d9 --- /dev/null +++ b/tracks/select_language.php @@ -0,0 +1,30 @@ + + + + + + Select Programming Language + + + +

Select Programming Language

+ +
+ + +
+ + + diff --git a/uploads/1769690370_697b550293142.jpg b/uploads/1769690370_697b550293142.jpg new file mode 100644 index 0000000..a57641c Binary files /dev/null and b/uploads/1769690370_697b550293142.jpg differ diff --git a/uploads/1769690804_697b56b4bb742.jpg b/uploads/1769690804_697b56b4bb742.jpg new file mode 100644 index 0000000..a57641c Binary files /dev/null and b/uploads/1769690804_697b56b4bb742.jpg differ diff --git a/uploads/1769693405_697b60dd843b1.jpg b/uploads/1769693405_697b60dd843b1.jpg new file mode 100644 index 0000000..a57641c Binary files /dev/null and b/uploads/1769693405_697b60dd843b1.jpg differ diff --git a/uploads/1769693427_697b60f338bcc.jpg b/uploads/1769693427_697b60f338bcc.jpg new file mode 100644 index 0000000..a57641c Binary files /dev/null and b/uploads/1769693427_697b60f338bcc.jpg differ diff --git a/uploads/1769697498_697b70daed94e.jpg b/uploads/1769697498_697b70daed94e.jpg new file mode 100644 index 0000000..a57641c Binary files /dev/null and b/uploads/1769697498_697b70daed94e.jpg differ diff --git a/uploads/1769965663_697f885f588fa.jpg b/uploads/1769965663_697f885f588fa.jpg new file mode 100644 index 0000000..a57641c Binary files /dev/null and b/uploads/1769965663_697f885f588fa.jpg differ diff --git a/uploads/1772470239_69a5bfdfeca58.png b/uploads/1772470239_69a5bfdfeca58.png new file mode 100644 index 0000000..bf6c99a Binary files /dev/null and b/uploads/1772470239_69a5bfdfeca58.png differ diff --git a/uploads/1772513147_69a6677b3c941.png b/uploads/1772513147_69a6677b3c941.png new file mode 100644 index 0000000..bf6c99a Binary files /dev/null and b/uploads/1772513147_69a6677b3c941.png differ diff --git a/uploads/1774536586_69c5478a20b2d.jpeg b/uploads/1774536586_69c5478a20b2d.jpeg new file mode 100644 index 0000000..cba8099 Binary files /dev/null and b/uploads/1774536586_69c5478a20b2d.jpeg differ diff --git a/uploads/1774536755_69c548339104b.jpeg b/uploads/1774536755_69c548339104b.jpeg new file mode 100644 index 0000000..bc56019 Binary files /dev/null and b/uploads/1774536755_69c548339104b.jpeg differ diff --git a/uploads/1774538819_69c5504314867.jpeg b/uploads/1774538819_69c5504314867.jpeg new file mode 100644 index 0000000..bc56019 Binary files /dev/null and b/uploads/1774538819_69c5504314867.jpeg differ diff --git a/uploads/1774539023_69c5510fe71de.jpeg b/uploads/1774539023_69c5510fe71de.jpeg new file mode 100644 index 0000000..bc56019 Binary files /dev/null and b/uploads/1774539023_69c5510fe71de.jpeg differ diff --git a/uploads/1774539200_69c551c04d202.jpeg b/uploads/1774539200_69c551c04d202.jpeg new file mode 100644 index 0000000..bc56019 Binary files /dev/null and b/uploads/1774539200_69c551c04d202.jpeg differ diff --git a/uploads/1774540339_69c55633b6db4.jpeg b/uploads/1774540339_69c55633b6db4.jpeg new file mode 100644 index 0000000..bc56019 Binary files /dev/null and b/uploads/1774540339_69c55633b6db4.jpeg differ diff --git a/uploads/1774600488_69c641284b288.jpeg b/uploads/1774600488_69c641284b288.jpeg new file mode 100644 index 0000000..bc56019 Binary files /dev/null and b/uploads/1774600488_69c641284b288.jpeg differ diff --git a/uploads/1776261325_69df98cd783c4.jpeg b/uploads/1776261325_69df98cd783c4.jpeg new file mode 100644 index 0000000..bc56019 Binary files /dev/null and b/uploads/1776261325_69df98cd783c4.jpeg differ diff --git a/uploads/answer_sheets/test_1765029892_7229.jpg b/uploads/answer_sheets/test_1765029892_7229.jpg new file mode 100644 index 0000000..53c69bd Binary files /dev/null and b/uploads/answer_sheets/test_1765029892_7229.jpg differ diff --git a/uploads/answer_sheets/test_1765083769_8312.jpg b/uploads/answer_sheets/test_1765083769_8312.jpg new file mode 100644 index 0000000..53c69bd Binary files /dev/null and b/uploads/answer_sheets/test_1765083769_8312.jpg differ diff --git a/uploads/answer_sheets/test_1769688029_7041.jpg b/uploads/answer_sheets/test_1769688029_7041.jpg new file mode 100644 index 0000000..a57641c Binary files /dev/null and b/uploads/answer_sheets/test_1769688029_7041.jpg differ diff --git a/uploads/rs_learning_lab_student_list.csv b/uploads/rs_learning_lab_student_list.csv new file mode 100644 index 0000000..5f70053 --- /dev/null +++ b/uploads/rs_learning_lab_student_list.csv @@ -0,0 +1,11 @@ +Roll No,Student Name +1,Arjun K +2,Priya S +3,Rahul M +4,Divya R +5,Karthik P +6,Ananya V +7,Suresh T +8,Meena L +9,Vishal N +10,Sneha A diff --git a/uploads/student_list.csv b/uploads/student_list.csv new file mode 100644 index 0000000..55b88d4 --- /dev/null +++ b/uploads/student_list.csv @@ -0,0 +1,68 @@ +DERIS AI INTERVIEW REPORT + +Question 1 +Tell me about yourself + +Answer: +hi I am Gopalakrishnan I am currently pursuing Bhopal and Krishna + +Feedback: +⚠️ Your answer is too short. Try explaining in detail. + +Score: 5/20 + +----------------------------- + +Question 2 +What are your strengths? + +Answer: +I believe I am strong in + +Feedback: +⚠️ Your answer is too short. Try explaining in detail. + +Score: 5/20 + +----------------------------- + +Question 3 +Tell me about one project you worked on + +Answer: +I completed Terrestrial resume analysing tool + +Feedback: +⚠️ Your answer is too short. Try explaining in detail. + +Score: 5/20 + +----------------------------- + +Question 4 +Why should we hire you? + +Answer: +I believe I am a good fit for this role because I am a hard worker and patience in all the database that + +Feedback: +πŸ‘ Good answer. Add more examples for stronger impact. + +Score: 10/20 + +----------------------------- + +Question 5 +Where do you see yourself in 5 years? + +Answer: +myself in 5 years Dene Ke Baad Mein Tu Ka Tala + +Feedback: +⚠️ Your answer is too short. Try explaining in detail. + +Score: 5/20 + +----------------------------- + +FINAL SCORE: 30/100 \ No newline at end of file diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000..bd49829 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,22 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ + private $prefixesPsr0 = array(); + /** + * @var list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php new file mode 100644 index 0000000..2052022 --- /dev/null +++ b/vendor/composer/InstalledVersions.php @@ -0,0 +1,396 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool + */ + private static $installedIsLocalDir; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; + } + + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + $copiedLocalDir = false; + + if (self::$canGetVendors) { + $selfDir = self::getSelfDir(); + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; + } + } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array() && !$copiedLocalDir) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 0000000..f27399a --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..bdc51cd --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,11 @@ + $vendorDir . '/composer/InstalledVersions.php', + 'Dompdf\\Cpdf' => $vendorDir . '/dompdf/dompdf/lib/Cpdf.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..15a2ff3 --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/dompdf/php-svg-lib/src/Svg'), + 'Sabberworm\\CSS\\' => array($vendorDir . '/sabberworm/php-css-parser/src'), + 'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'), + 'FontLib\\' => array($vendorDir . '/dompdf/php-font-lib/src/FontLib'), + 'Dompdf\\' => array($vendorDir . '/dompdf/dompdf/src'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000..28880ee --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,38 @@ +register(true); + + return $loader; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 0000000..09ddf0b --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,66 @@ + + array ( + 'Svg\\' => 4, + 'Sabberworm\\CSS\\' => 15, + ), + 'M' => + array ( + 'Masterminds\\' => 12, + ), + 'F' => + array ( + 'FontLib\\' => 8, + ), + 'D' => + array ( + 'Dompdf\\' => 7, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Svg\\' => + array ( + 0 => __DIR__ . '/..' . '/dompdf/php-svg-lib/src/Svg', + ), + 'Sabberworm\\CSS\\' => + array ( + 0 => __DIR__ . '/..' . '/sabberworm/php-css-parser/src', + ), + 'Masterminds\\' => + array ( + 0 => __DIR__ . '/..' . '/masterminds/html5/src', + ), + 'FontLib\\' => + array ( + 0 => __DIR__ . '/..' . '/dompdf/php-font-lib/src/FontLib', + ), + 'Dompdf\\' => + array ( + 0 => __DIR__ . '/..' . '/dompdf/dompdf/src', + ), + ); + + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'Dompdf\\Cpdf' => __DIR__ . '/..' . '/dompdf/dompdf/lib/Cpdf.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInitac2d307f4ed514763a8119f3523066bb::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitac2d307f4ed514763a8119f3523066bb::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInitac2d307f4ed514763a8119f3523066bb::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000..9e86b9a --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,309 @@ +{ + "packages": [ + { + "name": "dompdf/dompdf", + "version": "v3.1.4", + "version_normalized": "3.1.4.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/dompdf.git", + "reference": "db712c90c5b9868df3600e64e68da62e78a34623" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/db712c90c5b9868df3600e64e68da62e78a34623", + "reference": "db712c90c5b9868df3600e64e68da62e78a34623", + "shasum": "" + }, + "require": { + "dompdf/php-font-lib": "^1.0.0", + "dompdf/php-svg-lib": "^1.0.0", + "ext-dom": "*", + "ext-mbstring": "*", + "masterminds/html5": "^2.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ext-gd": "*", + "ext-json": "*", + "ext-zip": "*", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + }, + "time": "2025-10-29T12:43:30+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "The Dompdf Community", + "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" + } + ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v3.1.4" + }, + "install-path": "../dompdf/dompdf" + }, + { + "name": "dompdf/php-font-lib", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6" + }, + "time": "2024-12-02T14:37:59+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "The FontLib Community", + "homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/dompdf/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/1.0.1" + }, + "install-path": "../dompdf/php-font-lib" + }, + { + "name": "dompdf/php-svg-lib", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0", + "sabberworm/php-css-parser": "^8.4" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + }, + "time": "2024-04-29T13:26:35+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The SvgLib Community", + "homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/dompdf/php-svg-lib", + "support": { + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0" + }, + "install-path": "../dompdf/php-svg-lib" + }, + { + "name": "masterminds/html5", + "version": "2.10.0", + "version_normalized": "2.10.0.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "fcf91eb64359852f00d921887b219479b4f21251" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "time": "2025-07-25T09:04:22+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "installation-source": "source", + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" + }, + "install-path": "../masterminds/html5" + }, + { + "name": "sabberworm/php-css-parser", + "version": "v8.9.0", + "version_normalized": "8.9.0.0", + "source": { + "type": "git", + "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9", + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + }, + "require-dev": { + "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41", + "rawr/cross-data-providers": "^2.0.0" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "time": "2025-07-11T13:20:48+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.0.x-dev" + } + }, + "installation-source": "source", + "autoload": { + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + }, + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Jake Hotson", + "email": "jake.github@qzdesign.co.uk" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "support": { + "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0" + }, + "install-path": "../sabberworm/php-css-parser" + } + ], + "dev": true, + "dev-package-names": [] +} diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php new file mode 100644 index 0000000..90ef4d8 --- /dev/null +++ b/vendor/composer/installed.php @@ -0,0 +1,68 @@ + array( + 'name' => '__root__', + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'reference' => 'a4371fe7ea524d60b4ea020bf1f89312ef7a6c93', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'reference' => 'a4371fe7ea524d60b4ea020bf1f89312ef7a6c93', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'dompdf/dompdf' => array( + 'pretty_version' => 'v3.1.4', + 'version' => '3.1.4.0', + 'reference' => 'db712c90c5b9868df3600e64e68da62e78a34623', + 'type' => 'library', + 'install_path' => __DIR__ . '/../dompdf/dompdf', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'dompdf/php-font-lib' => array( + 'pretty_version' => '1.0.1', + 'version' => '1.0.1.0', + 'reference' => '6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d', + 'type' => 'library', + 'install_path' => __DIR__ . '/../dompdf/php-font-lib', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'dompdf/php-svg-lib' => array( + 'pretty_version' => '1.0.0', + 'version' => '1.0.0.0', + 'reference' => 'eb045e518185298eb6ff8d80d0d0c6b17aecd9af', + 'type' => 'library', + 'install_path' => __DIR__ . '/../dompdf/php-svg-lib', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'masterminds/html5' => array( + 'pretty_version' => '2.10.0', + 'version' => '2.10.0.0', + 'reference' => 'fcf91eb64359852f00d921887b219479b4f21251', + 'type' => 'library', + 'install_path' => __DIR__ . '/../masterminds/html5', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'sabberworm/php-css-parser' => array( + 'pretty_version' => 'v8.9.0', + 'version' => '8.9.0.0', + 'reference' => 'd8e916507b88e389e26d4ab03c904a082aa66bb9', + 'type' => 'library', + 'install_path' => __DIR__ . '/../sabberworm/php-css-parser', + 'aliases' => array(), + 'dev_requirement' => false, + ), + ), +); diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php new file mode 100644 index 0000000..3a099e4 --- /dev/null +++ b/vendor/composer/platform_check.php @@ -0,0 +1,25 @@ += 70100)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + throw new \RuntimeException( + 'Composer detected issues in your platform: ' . implode(' ', $issues) + ); +} diff --git a/vendor/dompdf/dompdf b/vendor/dompdf/dompdf new file mode 160000 index 0000000..db712c9 --- /dev/null +++ b/vendor/dompdf/dompdf @@ -0,0 +1 @@ +Subproject commit db712c90c5b9868df3600e64e68da62e78a34623 diff --git a/vendor/dompdf/php-font-lib b/vendor/dompdf/php-font-lib new file mode 160000 index 0000000..6137b7d --- /dev/null +++ b/vendor/dompdf/php-font-lib @@ -0,0 +1 @@ +Subproject commit 6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d diff --git a/vendor/dompdf/php-svg-lib b/vendor/dompdf/php-svg-lib new file mode 160000 index 0000000..eb045e5 --- /dev/null +++ b/vendor/dompdf/php-svg-lib @@ -0,0 +1 @@ +Subproject commit eb045e518185298eb6ff8d80d0d0c6b17aecd9af diff --git a/vendor/masterminds/html5 b/vendor/masterminds/html5 new file mode 160000 index 0000000..fcf91eb --- /dev/null +++ b/vendor/masterminds/html5 @@ -0,0 +1 @@ +Subproject commit fcf91eb64359852f00d921887b219479b4f21251 diff --git a/vendor/sabberworm/php-css-parser b/vendor/sabberworm/php-css-parser new file mode 160000 index 0000000..d8e9165 --- /dev/null +++ b/vendor/sabberworm/php-css-parser @@ -0,0 +1 @@ +Subproject commit d8e916507b88e389e26d4ab03c904a082aa66bb9