@thanh01.pmt/interactive-quiz-kit 1.0.24 → 1.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/player.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var React28 = require('react');
3
+ var React9 = require('react');
4
4
  var ReactDOM = require('react-dom/client');
5
5
  var reactI18next = require('react-i18next');
6
6
  var zod = require('zod');
@@ -10,6 +10,7 @@ var RadioGroupPrimitive = require('@radix-ui/react-radio-group');
10
10
  var lucideReact = require('lucide-react');
11
11
  var clsx = require('clsx');
12
12
  var tailwindMerge = require('tailwind-merge');
13
+ var jsxRuntime = require('react/jsx-runtime');
13
14
  var LabelPrimitive = require('@radix-ui/react-label');
14
15
  var classVarianceAuthority = require('class-variance-authority');
15
16
  var ReactMarkdown = require('react-markdown');
@@ -49,7 +50,7 @@ function _interopNamespace(e) {
49
50
  return Object.freeze(n);
50
51
  }
51
52
 
52
- var React28__namespace = /*#__PURE__*/_interopNamespace(React28);
53
+ var React9__namespace = /*#__PURE__*/_interopNamespace(React9);
53
54
  var ReactDOM__default = /*#__PURE__*/_interopDefault(ReactDOM);
54
55
  var RadioGroupPrimitive__namespace = /*#__PURE__*/_interopNamespace(RadioGroupPrimitive);
55
56
  var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
@@ -66,37 +67,7 @@ var ProgressPrimitive__namespace = /*#__PURE__*/_interopNamespace(ProgressPrimit
66
67
  var AccordionPrimitive__namespace = /*#__PURE__*/_interopNamespace(AccordionPrimitive);
67
68
  var ScrollAreaPrimitive__namespace = /*#__PURE__*/_interopNamespace(ScrollAreaPrimitive);
68
69
 
69
- var __defProp = Object.defineProperty;
70
- var __defProps = Object.defineProperties;
71
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
72
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
73
- var __hasOwnProp = Object.prototype.hasOwnProperty;
74
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
75
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
76
- var __spreadValues = (a, b) => {
77
- for (var prop in b || (b = {}))
78
- if (__hasOwnProp.call(b, prop))
79
- __defNormalProp(a, prop, b[prop]);
80
- if (__getOwnPropSymbols)
81
- for (var prop of __getOwnPropSymbols(b)) {
82
- if (__propIsEnum.call(b, prop))
83
- __defNormalProp(a, prop, b[prop]);
84
- }
85
- return a;
86
- };
87
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
88
- var __objRest = (source, exclude) => {
89
- var target = {};
90
- for (var prop in source)
91
- if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
92
- target[prop] = source[prop];
93
- if (source != null && __getOwnPropSymbols)
94
- for (var prop of __getOwnPropSymbols(source)) {
95
- if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
96
- target[prop] = source[prop];
97
- }
98
- return target;
99
- };
70
+ // src/player.ts
100
71
 
101
72
  // src/services/SCORMService.ts
102
73
  var SCORM_TRUE = "true";
@@ -117,11 +88,12 @@ var SCORMService = class {
117
88
  this.isInitialized = false;
118
89
  this.isTerminated = false;
119
90
  this.studentName = null;
120
- this.settings = __spreadValues({
91
+ this.settings = {
121
92
  setCompletionOnFinish: true,
122
93
  setSuccessOnPass: true,
123
- autoCommit: true
124
- }, settings);
94
+ autoCommit: true,
95
+ ...settings
96
+ };
125
97
  if (typeof window !== "undefined") {
126
98
  this._findAPI();
127
99
  }
@@ -227,14 +199,13 @@ var SCORMService = class {
227
199
  }
228
200
  }
229
201
  getValue(element) {
230
- var _a;
231
202
  if (!this.hasAPI() || !this.isInitialized) return null;
232
203
  const value = this.scormVersionFound === "2004" ? this.scormAPI.GetValue(element) : this.scormAPI.LMSGetValue(element);
233
204
  const error = this.getLastError();
234
205
  if (error.code !== SCORM_NO_ERROR && error.code !== "403" && error.code !== "0") {
235
206
  console.warn(`SCORMService: GetValue for ${element} produced an error ${error.code}: ${error.message}. Returning raw value:`, value);
236
207
  }
237
- return (_a = value == null ? void 0 : value.toString()) != null ? _a : null;
208
+ return value?.toString() ?? null;
238
209
  }
239
210
  commit() {
240
211
  if (!this.hasAPI() || !this.isInitialized) {
@@ -306,7 +277,6 @@ var SCORMService = class {
306
277
  }
307
278
  }
308
279
  getLastError() {
309
- var _a, _b;
310
280
  if (!this.hasAPI()) return { code: "-1", message: "SCORM API not found." };
311
281
  const errorCode = this.scormVersionFound === "2004" ? this.scormAPI.GetLastError() : this.scormAPI.LMSGetLastError();
312
282
  if (errorCode === SCORM_NO_ERROR || errorCode === 0 || errorCode === "0") {
@@ -316,8 +286,8 @@ var SCORMService = class {
316
286
  const diagnostic = this.scormVersionFound === "2004" ? this.scormAPI.GetDiagnostic(errorCode.toString()) : this.scormAPI.LMSGetDiagnostic(errorCode.toString());
317
287
  return {
318
288
  code: errorCode.toString(),
319
- message: (_a = errorMessage == null ? void 0 : errorMessage.toString()) != null ? _a : "Unknown error.",
320
- diagnostic: (_b = diagnostic == null ? void 0 : diagnostic.toString()) != null ? _b : void 0
289
+ message: errorMessage?.toString() ?? "Unknown error.",
290
+ diagnostic: diagnostic?.toString() ?? void 0
321
291
  };
322
292
  }
323
293
  formatCMITime(totalSeconds) {
@@ -348,14 +318,13 @@ var SCORMService = class {
348
318
  // src/services/evaluators/multiple-choice-evaluator.ts
349
319
  var MultipleChoiceEvaluator = class {
350
320
  async evaluate(question, answer) {
351
- var _a;
352
- const points = (_a = question.points) != null ? _a : 0;
321
+ const points = question.points ?? 0;
353
322
  const correctAnswerId = question.correctAnswerId;
354
323
  const isCorrect = answer === correctAnswerId;
355
324
  const correctOption = question.options.find((opt) => opt.id === correctAnswerId);
356
325
  const correctAnswerDetail = {
357
326
  id: correctAnswerId,
358
- value: (correctOption == null ? void 0 : correctOption.text) || ""
327
+ value: correctOption?.text || ""
359
328
  };
360
329
  return Promise.resolve({
361
330
  isCorrect,
@@ -368,8 +337,7 @@ var MultipleChoiceEvaluator = class {
368
337
  // src/services/evaluators/multiple-response-evaluator.ts
369
338
  var MultipleResponseEvaluator = class {
370
339
  async evaluate(question, answer) {
371
- var _a;
372
- const points = (_a = question.points) != null ? _a : 0;
340
+ const points = question.points ?? 0;
373
341
  const correctAnswerIds = question.correctAnswerIds;
374
342
  let isCorrect = false;
375
343
  if (Array.isArray(answer)) {
@@ -378,10 +346,7 @@ var MultipleResponseEvaluator = class {
378
346
  isCorrect = userAnswerSet.size === correctAnswerSet.size && [...userAnswerSet].every((id) => correctAnswerSet.has(id));
379
347
  }
380
348
  const correctValues = correctAnswerIds.map(
381
- (id) => {
382
- var _a2;
383
- return ((_a2 = question.options.find((opt) => opt.id === id)) == null ? void 0 : _a2.text) || "";
384
- }
349
+ (id) => question.options.find((opt) => opt.id === id)?.text || ""
385
350
  );
386
351
  const correctAnswerDetail = {
387
352
  id: correctAnswerIds,
@@ -398,8 +363,7 @@ var MultipleResponseEvaluator = class {
398
363
  // src/services/evaluators/true-false-evaluator.ts
399
364
  var TrueFalseEvaluator = class {
400
365
  async evaluate(question, answer) {
401
- var _a;
402
- const points = (_a = question.points) != null ? _a : 0;
366
+ const points = question.points ?? 0;
403
367
  const correctAnswer = question.correctAnswer;
404
368
  let userAnswer = answer;
405
369
  if (typeof answer === "string") {
@@ -421,12 +385,11 @@ var TrueFalseEvaluator = class {
421
385
  // src/services/evaluators/short-answer-evaluator.ts
422
386
  var ShortAnswerEvaluator = class {
423
387
  async evaluate(question, answer) {
424
- var _a, _b;
425
- const points = (_a = question.points) != null ? _a : 0;
388
+ const points = question.points ?? 0;
426
389
  let isCorrect = false;
427
390
  if (typeof answer === "string") {
428
391
  const userAnswerTrimmed = answer.trim();
429
- const caseSensitive = (_b = question.isCaseSensitive) != null ? _b : false;
392
+ const caseSensitive = question.isCaseSensitive ?? false;
430
393
  isCorrect = question.acceptedAnswers.some(
431
394
  (accAns) => caseSensitive ? accAns.trim() === userAnswerTrimmed : accAns.trim().toLowerCase() === userAnswerTrimmed.toLowerCase()
432
395
  );
@@ -446,8 +409,7 @@ var ShortAnswerEvaluator = class {
446
409
  // src/services/evaluators/numeric-evaluator.ts
447
410
  var NumericEvaluator = class {
448
411
  async evaluate(question, answer) {
449
- var _a;
450
- const points = (_a = question.points) != null ? _a : 0;
412
+ const points = question.points ?? 0;
451
413
  let isCorrect = false;
452
414
  if (typeof answer === "string" || typeof answer === "number") {
453
415
  const userAnswerNum = parseFloat(String(answer));
@@ -470,17 +432,13 @@ var NumericEvaluator = class {
470
432
  // src/services/evaluators/sequence-evaluator.ts
471
433
  var SequenceEvaluator = class {
472
434
  async evaluate(question, answer) {
473
- var _a;
474
- const points = (_a = question.points) != null ? _a : 0;
435
+ const points = question.points ?? 0;
475
436
  let isCorrect = false;
476
437
  if (Array.isArray(answer) && answer.length === question.correctOrder.length) {
477
438
  isCorrect = answer.every((itemId, index) => itemId === question.correctOrder[index]);
478
439
  }
479
440
  const correctValues = question.correctOrder.map(
480
- (id) => {
481
- var _a2;
482
- return ((_a2 = question.items.find((item) => item.id === id)) == null ? void 0 : _a2.content) || "";
483
- }
441
+ (id) => question.items.find((item) => item.id === id)?.content || ""
484
442
  );
485
443
  const correctAnswerDetail = {
486
444
  id: question.correctOrder,
@@ -497,17 +455,15 @@ var SequenceEvaluator = class {
497
455
  // src/services/evaluators/matching-evaluator.ts
498
456
  var MatchingEvaluator = class {
499
457
  async evaluate(question, answer) {
500
- var _a;
501
- const points = (_a = question.points) != null ? _a : 0;
458
+ const points = question.points ?? 0;
502
459
  let isCorrect = false;
503
460
  if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
504
461
  const userAnswerMap = answer;
505
462
  isCorrect = question.correctAnswerMap.length === Object.keys(userAnswerMap).length && question.correctAnswerMap.every((map) => userAnswerMap[map.promptId] === map.optionId);
506
463
  }
507
464
  const correctMap = question.correctAnswerMap.reduce((acc, curr) => {
508
- var _a2, _b;
509
- const promptText = ((_a2 = question.prompts.find((p) => p.id === curr.promptId)) == null ? void 0 : _a2.content) || "";
510
- const optionText = ((_b = question.options.find((o) => o.id === curr.optionId)) == null ? void 0 : _b.content) || "";
465
+ const promptText = question.prompts.find((p) => p.id === curr.promptId)?.content || "";
466
+ const optionText = question.options.find((o) => o.id === curr.optionId)?.content || "";
511
467
  acc[promptText] = optionText;
512
468
  return acc;
513
469
  }, {});
@@ -526,16 +482,14 @@ var MatchingEvaluator = class {
526
482
  // src/services/evaluators/fill-in-the-blanks-evaluator.ts
527
483
  var FillInTheBlanksEvaluator = class {
528
484
  async evaluate(question, answer) {
529
- var _a;
530
- const points = (_a = question.points) != null ? _a : 0;
485
+ const points = question.points ?? 0;
531
486
  let isCorrect = false;
532
487
  if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
533
488
  const userAnswerMap = answer;
534
489
  isCorrect = question.answers.length > 0 && question.answers.every((correctAnsDef) => {
535
- var _a2, _b;
536
- const userValForBlank = (_a2 = userAnswerMap[correctAnsDef.blankId]) == null ? void 0 : _a2.trim();
490
+ const userValForBlank = userAnswerMap[correctAnsDef.blankId]?.trim();
537
491
  if (userValForBlank === void 0) return false;
538
- const caseSensitive = (_b = question.isCaseSensitive) != null ? _b : false;
492
+ const caseSensitive = question.isCaseSensitive ?? false;
539
493
  return correctAnsDef.acceptedValues.some(
540
494
  (accVal) => caseSensitive ? accVal.trim() === userValForBlank : accVal.trim().toLowerCase() === userValForBlank.toLowerCase()
541
495
  );
@@ -560,17 +514,15 @@ var FillInTheBlanksEvaluator = class {
560
514
  // src/services/evaluators/drag-and-drop-evaluator.ts
561
515
  var DragAndDropEvaluator = class {
562
516
  async evaluate(question, answer) {
563
- var _a;
564
- const points = (_a = question.points) != null ? _a : 0;
517
+ const points = question.points ?? 0;
565
518
  let isCorrect = false;
566
519
  if (typeof answer === "object" && answer !== null && !Array.isArray(answer)) {
567
520
  const userAnswerMap = answer;
568
521
  isCorrect = question.answerMap.length === Object.keys(userAnswerMap).length && question.answerMap.every((map) => userAnswerMap[map.draggableId] === map.dropZoneId);
569
522
  }
570
523
  const correctMap = question.answerMap.reduce((acc, curr) => {
571
- var _a2, _b;
572
- const draggableText = ((_a2 = question.draggableItems.find((d) => d.id === curr.draggableId)) == null ? void 0 : _a2.content) || "";
573
- const dropZoneText = ((_b = question.dropZones.find((z3) => z3.id === curr.dropZoneId)) == null ? void 0 : _b.label) || "";
524
+ const draggableText = question.draggableItems.find((d) => d.id === curr.draggableId)?.content || "";
525
+ const dropZoneText = question.dropZones.find((z3) => z3.id === curr.dropZoneId)?.label || "";
574
526
  acc[draggableText] = dropZoneText;
575
527
  return acc;
576
528
  }, {});
@@ -589,8 +541,7 @@ var DragAndDropEvaluator = class {
589
541
  // src/services/evaluators/hotspot-evaluator.ts
590
542
  var HotspotEvaluator = class {
591
543
  async evaluate(question, answer) {
592
- var _a;
593
- const points = (_a = question.points) != null ? _a : 0;
544
+ const points = question.points ?? 0;
594
545
  let isCorrect = false;
595
546
  if (Array.isArray(answer)) {
596
547
  const userAnswerSet = new Set(answer);
@@ -598,10 +549,7 @@ var HotspotEvaluator = class {
598
549
  isCorrect = userAnswerSet.size === correctAnswerSet.size && [...userAnswerSet].every((id) => correctAnswerSet.has(id));
599
550
  }
600
551
  const correctValues = question.correctHotspotIds.map(
601
- (id) => {
602
- var _a2;
603
- return ((_a2 = question.hotspots.find((h) => h.id === id)) == null ? void 0 : _a2.description) || id;
604
- }
552
+ (id) => question.hotspots.find((h) => h.id === id)?.description || id
605
553
  );
606
554
  const correctAnswerDetail = {
607
555
  id: question.correctHotspotIds,
@@ -618,11 +566,10 @@ var HotspotEvaluator = class {
618
566
  // src/services/evaluators/programming-evaluator.ts
619
567
  var ProgrammingEvaluator = class {
620
568
  async evaluate(question, answer) {
621
- var _a, _b;
622
- const points = (_a = question.points) != null ? _a : 0;
569
+ const points = question.points ?? 0;
623
570
  let isCorrect = false;
624
571
  if (typeof answer === "string" && typeof question.solutionGeneratedCode === "string") {
625
- if (typeof window !== "undefined" && ((_b = window.Blockly) == null ? void 0 : _b.JavaScript)) {
572
+ if (typeof window !== "undefined" && window.Blockly?.JavaScript) {
626
573
  const LocalBlockly = window.Blockly;
627
574
  let generatedUserCode = "";
628
575
  try {
@@ -696,10 +643,10 @@ var JsonRepairEngine = class {
696
643
  if (breakIndex !== -1) {
697
644
  const stringContent = afterUnterminated.substring(0, breakIndex);
698
645
  const remainder = afterUnterminated.substring(breakIndex);
699
- const escapedContent = stringContent.replace(new RegExp('(?<!\\\\)"', "g"), '\\"');
646
+ const escapedContent = stringContent.replace(/(?<!\\)"/g, '\\"');
700
647
  repaired = beforeUnterminated + escapedContent + '"' + remainder;
701
648
  } else {
702
- const escapedContent = afterUnterminated.replace(new RegExp('(?<!\\\\)"', "g"), '\\"');
649
+ const escapedContent = afterUnterminated.replace(/(?<!\\)"/g, '\\"');
703
650
  repaired = beforeUnterminated + escapedContent + '"';
704
651
  }
705
652
  }
@@ -775,7 +722,6 @@ var JsonRepairEngine = class {
775
722
  * Main repair function that attempts multiple strategies.
776
723
  */
777
724
  static repairJson(jsonStr) {
778
- var _a;
779
725
  let current = jsonStr.trim();
780
726
  const maxAttempts = 5;
781
727
  let lastError = "";
@@ -805,7 +751,7 @@ var JsonRepairEngine = class {
805
751
  }
806
752
  lastError = validation.error || "";
807
753
  lastPosition = validation.position;
808
- if ((_a = validation.error) == null ? void 0 : _a.includes("Unterminated string")) {
754
+ if (validation.error?.includes("Unterminated string")) {
809
755
  current = this.repairUnterminatedStrings(current);
810
756
  } else {
811
757
  current = this.applyCommonFixes(current);
@@ -1100,9 +1046,10 @@ var CodeEvaluationService = class {
1100
1046
  userCode,
1101
1047
  testCase
1102
1048
  }, this.apiKey);
1103
- return __spreadValues({
1104
- testCaseId: testCase.id
1105
- }, aiResult);
1049
+ return {
1050
+ testCaseId: testCase.id,
1051
+ ...aiResult
1052
+ };
1106
1053
  }
1107
1054
  /**
1108
1055
  * Evaluates user's code against all test cases for a given question.
@@ -1139,8 +1086,7 @@ var CodeEvaluationService = class {
1139
1086
  // src/services/evaluators/coding-evaluator.ts
1140
1087
  var CodingEvaluator = class {
1141
1088
  async evaluate(question, answer) {
1142
- var _a;
1143
- const points = (_a = question.points) != null ? _a : 0;
1089
+ const points = question.points ?? 0;
1144
1090
  if (typeof answer !== "string" || !answer.trim()) {
1145
1091
  return {
1146
1092
  isCorrect: false,
@@ -1197,17 +1143,16 @@ var QuizEngine = class {
1197
1143
  this.quizResultState = { scormStatus: "idle" };
1198
1144
  this.questionStartTime = null;
1199
1145
  this.questionTimings = /* @__PURE__ */ new Map();
1200
- var _a, _b, _c, _d, _e;
1201
1146
  this.config = options.config;
1202
1147
  this.callbacks = options.callbacks || {};
1203
- this.questions = ((_a = this.config.settings) == null ? void 0 : _a.shuffleQuestions) ? [...this.config.questions].sort(() => Math.random() - 0.5) : this.config.questions;
1148
+ this.questions = this.config.settings?.shuffleQuestions ? [...this.config.questions].sort(() => Math.random() - 0.5) : this.config.questions;
1204
1149
  this.overallStartTime = Date.now();
1205
1150
  this.evaluators = /* @__PURE__ */ new Map();
1206
1151
  this.registerEvaluators();
1207
- if (((_b = this.config.settings) == null ? void 0 : _b.timeLimitMinutes) && this.config.settings.timeLimitMinutes > 0) {
1152
+ if (this.config.settings?.timeLimitMinutes && this.config.settings.timeLimitMinutes > 0) {
1208
1153
  this.timeLeftInSeconds = this.config.settings.timeLimitMinutes * 60;
1209
1154
  }
1210
- if ((_c = this.config.settings) == null ? void 0 : _c.scorm) {
1155
+ if (this.config.settings?.scorm) {
1211
1156
  this.quizResultState.scormStatus = "initializing";
1212
1157
  this.scormService = new SCORMService(this.config.settings.scorm);
1213
1158
  if (this.scormService.hasAPI()) {
@@ -1240,7 +1185,7 @@ var QuizEngine = class {
1240
1185
  if (this.timeLeftInSeconds !== null) {
1241
1186
  this.startTimer();
1242
1187
  }
1243
- (_e = (_d = this.callbacks).onQuestionChange) == null ? void 0 : _e.call(_d, initialQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
1188
+ this.callbacks.onQuestionChange?.(initialQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
1244
1189
  }
1245
1190
  registerEvaluators() {
1246
1191
  this.evaluators.set("multiple_choice", new MultipleChoiceEvaluator());
@@ -1278,15 +1223,14 @@ var QuizEngine = class {
1278
1223
  }
1279
1224
  }
1280
1225
  handleTick() {
1281
- var _a, _b, _c, _d;
1282
1226
  if (this.timeLeftInSeconds === null) return;
1283
1227
  if (this.timeLeftInSeconds > 0) {
1284
1228
  this.timeLeftInSeconds--;
1285
- (_b = (_a = this.callbacks).onTimeTick) == null ? void 0 : _b.call(_a, this.timeLeftInSeconds);
1229
+ this.callbacks.onTimeTick?.(this.timeLeftInSeconds);
1286
1230
  }
1287
1231
  if (this.timeLeftInSeconds <= 0) {
1288
1232
  this.stopTimer();
1289
- (_d = (_c = this.callbacks).onQuizTimeUp) == null ? void 0 : _d.call(_c);
1233
+ this.callbacks.onQuizTimeUp?.();
1290
1234
  this.calculateResults();
1291
1235
  }
1292
1236
  }
@@ -1309,43 +1253,39 @@ var QuizEngine = class {
1309
1253
  return this.quizResultState.score !== void 0;
1310
1254
  }
1311
1255
  submitAnswer(questionId, answer) {
1312
- var _a, _b;
1313
1256
  this.userAnswers.set(questionId, answer);
1314
1257
  const question = this.questions.find((q) => q.id === questionId);
1315
- if (question) (_b = (_a = this.callbacks).onAnswerSubmit) == null ? void 0 : _b.call(_a, question, answer);
1258
+ if (question) this.callbacks.onAnswerSubmit?.(question, answer);
1316
1259
  }
1317
1260
  nextQuestion() {
1318
- var _a, _b;
1319
1261
  this._recordCurrentQuestionTime();
1320
1262
  if (this.currentQuestionIndex < this.questions.length - 1) {
1321
1263
  this.currentQuestionIndex++;
1322
1264
  const currentQ = this.getCurrentQuestion();
1323
1265
  this.questionStartTime = Date.now();
1324
- (_b = (_a = this.callbacks).onQuestionChange) == null ? void 0 : _b.call(_a, currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
1266
+ this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
1325
1267
  return currentQ;
1326
1268
  }
1327
1269
  return null;
1328
1270
  }
1329
1271
  previousQuestion() {
1330
- var _a, _b;
1331
1272
  this._recordCurrentQuestionTime();
1332
1273
  if (this.currentQuestionIndex > 0) {
1333
1274
  this.currentQuestionIndex--;
1334
1275
  const currentQ = this.getCurrentQuestion();
1335
1276
  this.questionStartTime = Date.now();
1336
- (_b = (_a = this.callbacks).onQuestionChange) == null ? void 0 : _b.call(_a, currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
1277
+ this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
1337
1278
  return currentQ;
1338
1279
  }
1339
1280
  return null;
1340
1281
  }
1341
1282
  goToQuestion(index) {
1342
- var _a, _b;
1343
1283
  if (index >= 0 && index < this.questions.length && index !== this.currentQuestionIndex) {
1344
1284
  this._recordCurrentQuestionTime();
1345
1285
  this.currentQuestionIndex = index;
1346
1286
  const currentQ = this.getCurrentQuestion();
1347
1287
  this.questionStartTime = Date.now();
1348
- (_b = (_a = this.callbacks).onQuestionChange) == null ? void 0 : _b.call(_a, currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
1288
+ this.callbacks.onQuestionChange?.(currentQ, this.getCurrentQuestionNumber(), this.getTotalQuestions());
1349
1289
  return currentQ;
1350
1290
  }
1351
1291
  return this.getCurrentQuestion();
@@ -1371,7 +1311,6 @@ var QuizEngine = class {
1371
1311
  }
1372
1312
  // (Tiếp theo từ Phần 1)
1373
1313
  async calculateResults() {
1374
- var _a, _b, _c, _d, _e;
1375
1314
  this.stopTimer();
1376
1315
  this._recordCurrentQuestionTime();
1377
1316
  let totalScore = 0;
@@ -1380,7 +1319,7 @@ var QuizEngine = class {
1380
1319
  let accumulatedTotalTimeSpent = 0;
1381
1320
  for (const question of this.questions) {
1382
1321
  const userAnswerRaw = this.userAnswers.get(question.id) || null;
1383
- maxScore += (_a = question.points) != null ? _a : 0;
1322
+ maxScore += question.points ?? 0;
1384
1323
  const evaluator = this.evaluators.get(question.questionType);
1385
1324
  if (!evaluator) {
1386
1325
  console.warn(`No evaluator found for question type: ${question.questionType}`);
@@ -1420,13 +1359,13 @@ var QuizEngine = class {
1420
1359
  }
1421
1360
  const percentage = maxScore > 0 ? parseFloat((totalScore / maxScore * 100).toFixed(2)) : 0;
1422
1361
  let passed = void 0;
1423
- if (((_b = this.config.settings) == null ? void 0 : _b.passingScorePercent) != null) {
1362
+ if (this.config.settings?.passingScorePercent != null) {
1424
1363
  passed = percentage >= this.config.settings.passingScorePercent;
1425
1364
  }
1426
1365
  const totalQuizTimeSpentSeconds = parseFloat(accumulatedTotalTimeSpent.toFixed(2));
1427
1366
  const averageTimePerQuestionSeconds = this.questions.length > 0 ? parseFloat((totalQuizTimeSpentSeconds / this.questions.length).toFixed(2)) : 0;
1428
1367
  const metadataPerformance = await this._calculateMetadataPerformance();
1429
- const finalResults = __spreadValues({
1368
+ const finalResults = {
1430
1369
  score: totalScore,
1431
1370
  maxScore,
1432
1371
  percentage,
@@ -1438,39 +1377,33 @@ var QuizEngine = class {
1438
1377
  scormError: this.quizResultState.scormError,
1439
1378
  studentName: this.quizResultState.studentName,
1440
1379
  totalTimeSpentSeconds: totalQuizTimeSpentSeconds,
1441
- averageTimePerQuestionSeconds
1442
- }, metadataPerformance);
1443
- this.quizResultState = __spreadValues(__spreadValues({}, this.quizResultState), finalResults);
1444
- if ((_c = this.config.settings) == null ? void 0 : _c.scorm) this._sendResultsToSCORM(finalResults);
1380
+ averageTimePerQuestionSeconds,
1381
+ ...metadataPerformance
1382
+ };
1383
+ this.quizResultState = { ...this.quizResultState, ...finalResults };
1384
+ if (this.config.settings?.scorm) this._sendResultsToSCORM(finalResults);
1445
1385
  await this._sendResultsToWebhook(finalResults);
1446
- (_e = (_d = this.callbacks).onQuizFinish) == null ? void 0 : _e.call(_d, finalResults);
1386
+ this.callbacks.onQuizFinish?.(finalResults);
1447
1387
  return finalResults;
1448
1388
  }
1449
1389
  formatUserAnswerDetail(question, userAnswerRaw) {
1450
- var _a, _b, _c, _d, _e;
1451
1390
  if (userAnswerRaw === null) return null;
1452
1391
  switch (question.questionType) {
1453
1392
  case "multiple_choice": {
1454
1393
  const q = question;
1455
1394
  const id = userAnswerRaw;
1456
- return { id, value: ((_a = q.options.find((opt) => opt.id === id)) == null ? void 0 : _a.text) || "" };
1395
+ return { id, value: q.options.find((opt) => opt.id === id)?.text || "" };
1457
1396
  }
1458
1397
  case "multiple_response": {
1459
1398
  const q = question;
1460
1399
  const ids = userAnswerRaw;
1461
- const values = ids.map((id) => {
1462
- var _a2;
1463
- return ((_a2 = q.options.find((opt) => opt.id === id)) == null ? void 0 : _a2.text) || "";
1464
- });
1400
+ const values = ids.map((id) => q.options.find((opt) => opt.id === id)?.text || "");
1465
1401
  return { id: ids, value: values };
1466
1402
  }
1467
1403
  case "sequence": {
1468
1404
  const q = question;
1469
1405
  const ids = userAnswerRaw;
1470
- const values = ids.map((id) => {
1471
- var _a2;
1472
- return ((_a2 = q.items.find((item) => item.id === id)) == null ? void 0 : _a2.content) || "";
1473
- });
1406
+ const values = ids.map((id) => q.items.find((item) => item.id === id)?.content || "");
1474
1407
  return { id: ids, value: values };
1475
1408
  }
1476
1409
  case "matching": {
@@ -1479,8 +1412,8 @@ var QuizEngine = class {
1479
1412
  const valueMap = {};
1480
1413
  for (const promptId in userAnswerMap) {
1481
1414
  const optionId = userAnswerMap[promptId];
1482
- const promptText = ((_b = q.prompts.find((p) => p.id === promptId)) == null ? void 0 : _b.content) || "";
1483
- const optionText = ((_c = q.options.find((o) => o.id === optionId)) == null ? void 0 : _c.content) || "";
1415
+ const promptText = q.prompts.find((p) => p.id === promptId)?.content || "";
1416
+ const optionText = q.options.find((o) => o.id === optionId)?.content || "";
1484
1417
  valueMap[promptText] = optionText;
1485
1418
  }
1486
1419
  return { id: null, value: valueMap };
@@ -1492,8 +1425,8 @@ var QuizEngine = class {
1492
1425
  const enrichedUserAnswerMap = {};
1493
1426
  for (const draggableId in userAnswerMapByIds) {
1494
1427
  const dropZoneId = userAnswerMapByIds[draggableId];
1495
- const draggableText = ((_d = q.draggableItems.find((d) => d.id === draggableId)) == null ? void 0 : _d.content) || `(ID: ${draggableId})`;
1496
- const dropZoneText = ((_e = q.dropZones.find((z3) => z3.id === dropZoneId)) == null ? void 0 : _e.label) || `(ID: ${dropZoneId})`;
1428
+ const draggableText = q.draggableItems.find((d) => d.id === draggableId)?.content || `(ID: ${draggableId})`;
1429
+ const dropZoneText = q.dropZones.find((z3) => z3.id === dropZoneId)?.label || `(ID: ${dropZoneId})`;
1497
1430
  enrichedUserAnswerMap[draggableText] = dropZoneText;
1498
1431
  }
1499
1432
  return { id: null, value: enrichedUserAnswerMap };
@@ -1505,7 +1438,6 @@ var QuizEngine = class {
1505
1438
  }
1506
1439
  }
1507
1440
  async _calculateMetadataPerformance() {
1508
- var _a;
1509
1441
  const loPerformanceMap = /* @__PURE__ */ new Map();
1510
1442
  const categoryPerformanceMap = /* @__PURE__ */ new Map();
1511
1443
  const topicPerformanceMap = /* @__PURE__ */ new Map();
@@ -1527,7 +1459,7 @@ var QuizEngine = class {
1527
1459
  const evaluator = this.evaluators.get(q.questionType);
1528
1460
  if (evaluator) {
1529
1461
  const { isCorrect } = await evaluator.evaluate(q, userAnswer);
1530
- const pointsForThisQuestion = (_a = q.points) != null ? _a : 0;
1462
+ const pointsForThisQuestion = q.points ?? 0;
1531
1463
  updateMap(loPerformanceMap, q.learningObjective, pointsForThisQuestion, isCorrect);
1532
1464
  updateMap(categoryPerformanceMap, q.category, pointsForThisQuestion, isCorrect);
1533
1465
  updateMap(topicPerformanceMap, q.topic, pointsForThisQuestion, isCorrect);
@@ -1554,8 +1486,7 @@ var QuizEngine = class {
1554
1486
  };
1555
1487
  }
1556
1488
  async _sendResultsToWebhook(results) {
1557
- var _a;
1558
- if (!((_a = this.config.settings) == null ? void 0 : _a.webhookUrl)) {
1489
+ if (!this.config.settings?.webhookUrl) {
1559
1490
  results.webhookStatus = "idle";
1560
1491
  return;
1561
1492
  }
@@ -1583,12 +1514,11 @@ var QuizEngine = class {
1583
1514
  }
1584
1515
  }
1585
1516
  _sendResultsToSCORM(results) {
1586
- var _a, _b, _c, _d, _e, _f, _g;
1587
1517
  if (!this.scormService || !this.scormService.hasAPI() || this.quizResultState.scormStatus === "no_api") {
1588
1518
  results.scormStatus = this.quizResultState.scormStatus || "idle";
1589
1519
  return;
1590
1520
  }
1591
- if (this.quizResultState.scormStatus === "error" && ((_a = this.quizResultState.scormError) == null ? void 0 : _a.includes("initialization failed"))) {
1521
+ if (this.quizResultState.scormStatus === "error" && this.quizResultState.scormError?.includes("initialization failed")) {
1592
1522
  results.scormStatus = "error";
1593
1523
  results.scormError = this.quizResultState.scormError;
1594
1524
  return;
@@ -1597,15 +1527,15 @@ var QuizEngine = class {
1597
1527
  try {
1598
1528
  this.scormService.setScore(results.score, results.maxScore, 0);
1599
1529
  let lessonStatusSetting = "completed";
1600
- if (((_b = this.config.settings) == null ? void 0 : _b.passingScorePercent) !== void 0 && ((_c = this.config.settings) == null ? void 0 : _c.passingScorePercent) !== null) {
1530
+ if (this.config.settings?.passingScorePercent !== void 0 && this.config.settings?.passingScorePercent !== null) {
1601
1531
  lessonStatusSetting = results.passed ? "passed" : "failed";
1602
- } else if ((_e = (_d = this.config.settings) == null ? void 0 : _d.scorm) == null ? void 0 : _e.setCompletionOnFinish) {
1532
+ } else if (this.config.settings?.scorm?.setCompletionOnFinish) {
1603
1533
  lessonStatusSetting = "completed";
1604
1534
  }
1605
1535
  this.scormService.setLessonStatus(lessonStatusSetting, results.passed);
1606
1536
  if (results.totalTimeSpentSeconds !== void 0 && this.scormService.formatCMITime) {
1607
1537
  const cmiTime = this.scormService.formatCMITime(results.totalTimeSpentSeconds);
1608
- const sessionTimeVar = ((_g = (_f = this.config.settings) == null ? void 0 : _f.scorm) == null ? void 0 : _g.sessionTimeVar) || (this.scormService.getSCORMVersion() === "2004" ? "cmi.session_time" : "cmi.core.session_time");
1538
+ const sessionTimeVar = this.config.settings?.scorm?.sessionTimeVar || (this.scormService.getSCORMVersion() === "2004" ? "cmi.session_time" : "cmi.core.session_time");
1609
1539
  if (sessionTimeVar) this.scormService.setValue(sessionTimeVar, cmiTime);
1610
1540
  }
1611
1541
  const commitResult = this.scormService.commit();
@@ -1624,114 +1554,96 @@ var QuizEngine = class {
1624
1554
  function cn(...inputs) {
1625
1555
  return tailwindMerge.twMerge(clsx.clsx(inputs));
1626
1556
  }
1627
-
1628
- // src/react-ui/components/elements/radio-group.tsx
1629
- var RadioGroup = React28__namespace.forwardRef((_a, ref) => {
1630
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
1631
- return /* @__PURE__ */ React28__namespace.createElement(
1557
+ var RadioGroup = React9__namespace.forwardRef(({ className, ...props }, ref) => {
1558
+ return /* @__PURE__ */ jsxRuntime.jsx(
1632
1559
  RadioGroupPrimitive__namespace.Root,
1633
- __spreadProps(__spreadValues({
1634
- className: cn("grid gap-2", className)
1635
- }, props), {
1560
+ {
1561
+ className: cn("grid gap-2", className),
1562
+ ...props,
1636
1563
  ref
1637
- })
1564
+ }
1638
1565
  );
1639
1566
  });
1640
1567
  RadioGroup.displayName = RadioGroupPrimitive__namespace.Root.displayName;
1641
- var RadioGroupItem = React28__namespace.forwardRef((_a, ref) => {
1642
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
1643
- return /* @__PURE__ */ React28__namespace.createElement(
1568
+ var RadioGroupItem = React9__namespace.forwardRef(({ className, ...props }, ref) => {
1569
+ return /* @__PURE__ */ jsxRuntime.jsx(
1644
1570
  RadioGroupPrimitive__namespace.Item,
1645
- __spreadValues({
1571
+ {
1646
1572
  ref,
1647
1573
  className: cn(
1648
1574
  "aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
1649
1575
  className
1650
- )
1651
- }, props),
1652
- /* @__PURE__ */ React28__namespace.createElement(RadioGroupPrimitive__namespace.Indicator, { className: "flex items-center justify-center" }, /* @__PURE__ */ React28__namespace.createElement(lucideReact.Circle, { className: "h-2.5 w-2.5 fill-current text-current" }))
1576
+ ),
1577
+ ...props,
1578
+ children: /* @__PURE__ */ jsxRuntime.jsx(RadioGroupPrimitive__namespace.Indicator, { className: "flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Circle, { className: "h-2.5 w-2.5 fill-current text-current" }) })
1579
+ }
1653
1580
  );
1654
1581
  });
1655
1582
  RadioGroupItem.displayName = RadioGroupPrimitive__namespace.Item.displayName;
1656
1583
  var labelVariants = classVarianceAuthority.cva(
1657
1584
  "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
1658
1585
  );
1659
- var Label = React28__namespace.forwardRef((_a, ref) => {
1660
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
1661
- return /* @__PURE__ */ React28__namespace.createElement(
1662
- LabelPrimitive__namespace.Root,
1663
- __spreadValues({
1664
- ref,
1665
- className: cn(labelVariants(), className)
1666
- }, props)
1667
- );
1668
- });
1586
+ var Label = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1587
+ LabelPrimitive__namespace.Root,
1588
+ {
1589
+ ref,
1590
+ className: cn(labelVariants(), className),
1591
+ ...props
1592
+ }
1593
+ ));
1669
1594
  Label.displayName = LabelPrimitive__namespace.Root.displayName;
1670
- var Card = React28__namespace.forwardRef((_a, ref) => {
1671
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
1672
- return /* @__PURE__ */ React28__namespace.createElement(
1673
- "div",
1674
- __spreadValues({
1675
- ref,
1676
- className: cn(
1677
- "rounded-lg border bg-card text-card-foreground shadow-sm",
1678
- className
1679
- )
1680
- }, props)
1681
- );
1682
- });
1595
+ var Card = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1596
+ "div",
1597
+ {
1598
+ ref,
1599
+ className: cn(
1600
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
1601
+ className
1602
+ ),
1603
+ ...props
1604
+ }
1605
+ ));
1683
1606
  Card.displayName = "Card";
1684
- var CardHeader = React28__namespace.forwardRef((_a, ref) => {
1685
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
1686
- return /* @__PURE__ */ React28__namespace.createElement(
1687
- "div",
1688
- __spreadValues({
1689
- ref,
1690
- className: cn("flex flex-col space-y-1.5 p-6", className)
1691
- }, props)
1692
- );
1693
- });
1607
+ var CardHeader = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1608
+ "div",
1609
+ {
1610
+ ref,
1611
+ className: cn("flex flex-col space-y-1.5 p-6", className),
1612
+ ...props
1613
+ }
1614
+ ));
1694
1615
  CardHeader.displayName = "CardHeader";
1695
- var CardTitle = React28__namespace.forwardRef((_a, ref) => {
1696
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
1697
- return /* @__PURE__ */ React28__namespace.createElement(
1698
- "div",
1699
- __spreadValues({
1700
- ref,
1701
- className: cn(
1702
- "text-2xl font-semibold leading-none tracking-tight",
1703
- className
1704
- )
1705
- }, props)
1706
- );
1707
- });
1616
+ var CardTitle = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1617
+ "div",
1618
+ {
1619
+ ref,
1620
+ className: cn(
1621
+ "text-2xl font-semibold leading-none tracking-tight",
1622
+ className
1623
+ ),
1624
+ ...props
1625
+ }
1626
+ ));
1708
1627
  CardTitle.displayName = "CardTitle";
1709
- var CardDescription = React28__namespace.forwardRef((_a, ref) => {
1710
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
1711
- return /* @__PURE__ */ React28__namespace.createElement(
1712
- "div",
1713
- __spreadValues({
1714
- ref,
1715
- className: cn("text-sm text-muted-foreground", className)
1716
- }, props)
1717
- );
1718
- });
1628
+ var CardDescription = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1629
+ "div",
1630
+ {
1631
+ ref,
1632
+ className: cn("text-sm text-muted-foreground", className),
1633
+ ...props
1634
+ }
1635
+ ));
1719
1636
  CardDescription.displayName = "CardDescription";
1720
- var CardContent = React28__namespace.forwardRef((_a, ref) => {
1721
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
1722
- return /* @__PURE__ */ React28__namespace.createElement("div", __spreadValues({ ref, className: cn("p-6 pt-0", className) }, props));
1723
- });
1637
+ var CardContent = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
1724
1638
  CardContent.displayName = "CardContent";
1725
- var CardFooter = React28__namespace.forwardRef((_a, ref) => {
1726
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
1727
- return /* @__PURE__ */ React28__namespace.createElement(
1728
- "div",
1729
- __spreadValues({
1730
- ref,
1731
- className: cn("flex items-center p-6 pt-0", className)
1732
- }, props)
1733
- );
1734
- });
1639
+ var CardFooter = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1640
+ "div",
1641
+ {
1642
+ ref,
1643
+ className: cn("flex items-center p-6 pt-0", className),
1644
+ ...props
1645
+ }
1646
+ ));
1735
1647
  CardFooter.displayName = "CardFooter";
1736
1648
  var MarkdownRenderer = ({
1737
1649
  content,
@@ -1775,20 +1687,19 @@ var MarkdownRenderer = ({
1775
1687
  const processedContent = processContentForVideos(content);
1776
1688
  return (
1777
1689
  // Using Tailwind Typography for beautiful default styling of markdown content
1778
- /* @__PURE__ */ React28__namespace.default.createElement("div", { className: `prose dark:prose-invert max-w-none ${className}` }, /* @__PURE__ */ React28__namespace.default.createElement(
1690
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `prose dark:prose-invert max-w-none ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx(
1779
1691
  ReactMarkdown__default.default,
1780
1692
  {
1781
1693
  remarkPlugins: [remarkGfm__default.default, remarkMath__default.default],
1782
1694
  rehypePlugins: [rehypeHighlight__default.default, rehypeKatex__default.default],
1783
1695
  components: {
1784
1696
  // Override the default image component to handle videos and responsive images
1785
- img: (_a) => {
1786
- var _b = _a, { node } = _b, props = __objRest(_b, ["node"]);
1697
+ img: ({ node, ...props }) => {
1787
1698
  const src = props.src || "";
1788
1699
  const { platform, id } = getVideoId(src);
1789
1700
  if (platform && id) {
1790
1701
  const videoSrc = platform === "youtube" ? `https://www.youtube.com/embed/${id}` : `https://player.vimeo.com/video/${id}`;
1791
- return /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "aspect-w-16 aspect-h-9 my-4" }, /* @__PURE__ */ React28__namespace.default.createElement(
1702
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-w-16 aspect-h-9 my-4", children: /* @__PURE__ */ jsxRuntime.jsx(
1792
1703
  "iframe",
1793
1704
  {
1794
1705
  src: videoSrc,
@@ -1797,13 +1708,14 @@ var MarkdownRenderer = ({
1797
1708
  allowFullScreen: true,
1798
1709
  className: "w-full h-full rounded-md"
1799
1710
  }
1800
- ));
1711
+ ) });
1801
1712
  }
1802
1713
  return (
1803
1714
  // eslint-disable-next-line @next/next/no-img-element
1804
- /* @__PURE__ */ React28__namespace.default.createElement(
1715
+ /* @__PURE__ */ jsxRuntime.jsx(
1805
1716
  "img",
1806
- __spreadProps(__spreadValues({}, props), {
1717
+ {
1718
+ ...props,
1807
1719
  style: {
1808
1720
  maxWidth: "100%",
1809
1721
  height: "auto",
@@ -1811,33 +1723,26 @@ var MarkdownRenderer = ({
1811
1723
  margin: "1rem 0"
1812
1724
  },
1813
1725
  alt: props.alt || ""
1814
- })
1726
+ }
1815
1727
  )
1816
1728
  );
1817
1729
  },
1818
1730
  // Override the default table to add responsive wrapper
1819
- table: (_c) => {
1820
- var _d = _c, { node } = _d, props = __objRest(_d, ["node"]);
1821
- return /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React28__namespace.default.createElement("table", __spreadProps(__spreadValues({}, props), { className: "my-4 w-full text-sm" })));
1822
- },
1731
+ table: ({ node, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("table", { ...props, className: "my-4 w-full text-sm" }) }),
1823
1732
  // Override default blockquote for better styling
1824
- blockquote: (_e) => {
1825
- var _f = _e, { node } = _f, props = __objRest(_f, ["node"]);
1826
- return /* @__PURE__ */ React28__namespace.default.createElement(
1827
- "blockquote",
1828
- __spreadProps(__spreadValues({}, props), {
1829
- className: "border-l-4 border-primary bg-muted/50 p-4 my-4 italic"
1830
- })
1831
- );
1832
- }
1833
- }
1834
- },
1835
- processedContent
1836
- ))
1733
+ blockquote: ({ node, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
1734
+ "blockquote",
1735
+ {
1736
+ ...props,
1737
+ className: "border-l-4 border-primary bg-muted/50 p-4 my-4 italic"
1738
+ }
1739
+ )
1740
+ },
1741
+ children: processedContent
1742
+ }
1743
+ ) })
1837
1744
  );
1838
1745
  };
1839
-
1840
- // src/react-ui/components/ui/MultipleChoiceQuestionUI.tsx
1841
1746
  var MultipleChoiceQuestionUI = ({
1842
1747
  question,
1843
1748
  onAnswerChange,
@@ -1848,40 +1753,58 @@ var MultipleChoiceQuestionUI = ({
1848
1753
  const handleSelection = (value) => {
1849
1754
  onAnswerChange(value);
1850
1755
  };
1851
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: prompt })), points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0" }, /* @__PURE__ */ React28__namespace.default.createElement(
1852
- RadioGroup,
1853
- {
1854
- value: userAnswer || void 0,
1855
- onValueChange: handleSelection,
1856
- className: "space-y-3",
1857
- "aria-labelledby": `question-prompt-${questionId}`
1858
- },
1859
- options.map((option) => {
1860
- const isSelected = userAnswer === option.id;
1861
- const isCorrect = option.id === correctAnswerId;
1862
- let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
1863
- if (showCorrectAnswer) {
1864
- if (isCorrect) {
1865
- itemClassName += " border-green-500 bg-green-500/10";
1866
- } else if (isSelected && !isCorrect) {
1867
- itemClassName += " border-destructive bg-destructive/10";
1868
- } else {
1869
- itemClassName += " border-muted";
1870
- }
1871
- } else {
1872
- itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
1873
- }
1874
- return /* @__PURE__ */ React28__namespace.default.createElement(
1875
- Label,
1756
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
1757
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
1758
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
1759
+ points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
1760
+ "Points: ",
1761
+ points
1762
+ ] })
1763
+ ] }),
1764
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
1765
+ /* @__PURE__ */ jsxRuntime.jsx(
1766
+ RadioGroup,
1876
1767
  {
1877
- key: option.id,
1878
- htmlFor: option.id,
1879
- className: itemClassName
1880
- },
1881
- /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React28__namespace.default.createElement(RadioGroupItem, { value: option.id, id: option.id, className: "mr-3" }), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "text-base flex-1" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: option.text })))
1882
- );
1883
- })
1884
- ), showCorrectAnswer && explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Explanation:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" }))));
1768
+ value: userAnswer || void 0,
1769
+ onValueChange: handleSelection,
1770
+ className: "space-y-3",
1771
+ "aria-labelledby": `question-prompt-${questionId}`,
1772
+ children: options.map((option) => {
1773
+ const isSelected = userAnswer === option.id;
1774
+ const isCorrect = option.id === correctAnswerId;
1775
+ let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
1776
+ if (showCorrectAnswer) {
1777
+ if (isCorrect) {
1778
+ itemClassName += " border-green-500 bg-green-500/10";
1779
+ } else if (isSelected && !isCorrect) {
1780
+ itemClassName += " border-destructive bg-destructive/10";
1781
+ } else {
1782
+ itemClassName += " border-muted";
1783
+ }
1784
+ } else {
1785
+ itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
1786
+ }
1787
+ return /* @__PURE__ */ jsxRuntime.jsx(
1788
+ Label,
1789
+ {
1790
+ htmlFor: option.id,
1791
+ className: itemClassName,
1792
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
1793
+ /* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: option.id, id: option.id, className: "mr-3" }),
1794
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: option.text }) })
1795
+ ] })
1796
+ },
1797
+ option.id
1798
+ );
1799
+ })
1800
+ }
1801
+ ),
1802
+ showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
1803
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
1804
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
1805
+ ] })
1806
+ ] })
1807
+ ] });
1885
1808
  };
1886
1809
  var TrueFalseQuestionUI = ({
1887
1810
  question,
@@ -1897,64 +1820,78 @@ var TrueFalseQuestionUI = ({
1897
1820
  const handleSelection = (value) => {
1898
1821
  onAnswerChange(value);
1899
1822
  };
1900
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: prompt })), points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0" }, /* @__PURE__ */ React28__namespace.default.createElement(
1901
- RadioGroup,
1902
- {
1903
- value: userAnswer || void 0,
1904
- onValueChange: handleSelection,
1905
- className: "space-y-3",
1906
- "aria-labelledby": `question-prompt-${questionId}`
1907
- },
1908
- options.map((option) => {
1909
- const isSelected = userAnswer === option.value;
1910
- const isOptionCorrect = option.value === "true" && correctAnswer === true || option.value === "false" && correctAnswer === false;
1911
- let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
1912
- if (showCorrectAnswer) {
1913
- if (isOptionCorrect) {
1914
- itemClassName += " border-green-500 bg-green-500/10";
1915
- } else if (isSelected && !isOptionCorrect) {
1916
- itemClassName += " border-destructive bg-destructive/10";
1917
- } else {
1918
- itemClassName += " border-muted";
1919
- }
1920
- } else {
1921
- itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
1922
- }
1923
- return /* @__PURE__ */ React28__namespace.default.createElement(
1924
- Label,
1823
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
1824
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
1825
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
1826
+ points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
1827
+ "Points: ",
1828
+ points
1829
+ ] })
1830
+ ] }),
1831
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
1832
+ /* @__PURE__ */ jsxRuntime.jsx(
1833
+ RadioGroup,
1925
1834
  {
1926
- key: option.id,
1927
- htmlFor: option.id,
1928
- className: itemClassName
1929
- },
1930
- /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React28__namespace.default.createElement(RadioGroupItem, { value: option.value, id: option.id, className: "mr-3" }), /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "text-base" }, option.label))
1931
- );
1932
- })
1933
- ), showCorrectAnswer && explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Explanation:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" }))));
1835
+ value: userAnswer || void 0,
1836
+ onValueChange: handleSelection,
1837
+ className: "space-y-3",
1838
+ "aria-labelledby": `question-prompt-${questionId}`,
1839
+ children: options.map((option) => {
1840
+ const isSelected = userAnswer === option.value;
1841
+ const isOptionCorrect = option.value === "true" && correctAnswer === true || option.value === "false" && correctAnswer === false;
1842
+ let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary";
1843
+ if (showCorrectAnswer) {
1844
+ if (isOptionCorrect) {
1845
+ itemClassName += " border-green-500 bg-green-500/10";
1846
+ } else if (isSelected && !isOptionCorrect) {
1847
+ itemClassName += " border-destructive bg-destructive/10";
1848
+ } else {
1849
+ itemClassName += " border-muted";
1850
+ }
1851
+ } else {
1852
+ itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
1853
+ }
1854
+ return /* @__PURE__ */ jsxRuntime.jsx(
1855
+ Label,
1856
+ {
1857
+ htmlFor: option.id,
1858
+ className: itemClassName,
1859
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
1860
+ /* @__PURE__ */ jsxRuntime.jsx(RadioGroupItem, { value: option.value, id: option.id, className: "mr-3" }),
1861
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base", children: option.label })
1862
+ ] })
1863
+ },
1864
+ option.id
1865
+ );
1866
+ })
1867
+ }
1868
+ ),
1869
+ showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
1870
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
1871
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
1872
+ ] })
1873
+ ] })
1874
+ ] });
1934
1875
  };
1935
- var Checkbox = React28__namespace.forwardRef((_a, ref) => {
1936
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
1937
- return /* @__PURE__ */ React28__namespace.createElement(
1938
- CheckboxPrimitive__namespace.Root,
1939
- __spreadValues({
1940
- ref,
1941
- className: cn(
1942
- "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
1943
- className
1944
- )
1945
- }, props),
1946
- /* @__PURE__ */ React28__namespace.createElement(
1876
+ var Checkbox = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1877
+ CheckboxPrimitive__namespace.Root,
1878
+ {
1879
+ ref,
1880
+ className: cn(
1881
+ "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
1882
+ className
1883
+ ),
1884
+ ...props,
1885
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1947
1886
  CheckboxPrimitive__namespace.Indicator,
1948
1887
  {
1949
- className: cn("flex items-center justify-center text-current")
1950
- },
1951
- /* @__PURE__ */ React28__namespace.createElement(lucideReact.Check, { className: "h-4 w-4" })
1888
+ className: cn("flex items-center justify-center text-current"),
1889
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" })
1890
+ }
1952
1891
  )
1953
- );
1954
- });
1892
+ }
1893
+ ));
1955
1894
  Checkbox.displayName = CheckboxPrimitive__namespace.Root.displayName;
1956
-
1957
- // src/react-ui/components/ui/MultipleResponseQuestionUI.tsx
1958
1895
  var MultipleResponseQuestionUI = ({
1959
1896
  question,
1960
1897
  onAnswerChange,
@@ -1976,59 +1913,74 @@ var MultipleResponseQuestionUI = ({
1976
1913
  }
1977
1914
  onAnswerChange(newAnswers.length > 0 ? newAnswers : null);
1978
1915
  };
1979
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: prompt })), points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "space-y-3", role: "group", "aria-labelledby": `question-prompt-${questionId}` }, options.map((option) => {
1980
- const isSelected = Array.isArray(userAnswer) && userAnswer.includes(option.id);
1981
- const isCorrectOption = correctAnswerIds.includes(option.id);
1982
- let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary flex items-center";
1983
- if (showCorrectAnswer) {
1984
- if (isCorrectOption) {
1985
- itemClassName += isSelected ? " border-green-500 bg-green-500/10" : " border-green-500";
1986
- } else {
1987
- itemClassName += isSelected ? " border-destructive bg-destructive/10" : " border-muted";
1988
- }
1989
- } else {
1990
- itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
1991
- }
1992
- return /* @__PURE__ */ React28__namespace.default.createElement(
1993
- Label,
1994
- {
1995
- key: option.id,
1996
- htmlFor: option.id,
1997
- className: itemClassName
1998
- },
1999
- /* @__PURE__ */ React28__namespace.default.createElement(
2000
- Checkbox,
2001
- {
2002
- id: option.id,
2003
- checked: isSelected,
2004
- onCheckedChange: (checked) => handleSelectionChange(option.id, !!checked),
2005
- className: "mr-3",
2006
- "aria-label": option.text.replace(/<[^>]*>?/gm, "")
1916
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
1917
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
1918
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
1919
+ points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
1920
+ "Points: ",
1921
+ points
1922
+ ] })
1923
+ ] }),
1924
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
1925
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", role: "group", "aria-labelledby": `question-prompt-${questionId}`, children: options.map((option) => {
1926
+ const isSelected = Array.isArray(userAnswer) && userAnswer.includes(option.id);
1927
+ const isCorrectOption = correctAnswerIds.includes(option.id);
1928
+ let itemClassName = "p-4 rounded-lg border-2 transition-all cursor-pointer hover:border-primary flex items-center";
1929
+ if (showCorrectAnswer) {
1930
+ if (isCorrectOption) {
1931
+ itemClassName += isSelected ? " border-green-500 bg-green-500/10" : " border-green-500";
1932
+ } else {
1933
+ itemClassName += isSelected ? " border-destructive bg-destructive/10" : " border-muted";
1934
+ }
1935
+ } else {
1936
+ itemClassName += isSelected ? " border-primary bg-primary/10" : " border-muted";
2007
1937
  }
2008
- ),
2009
- /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "text-base flex-1" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: option.text }))
2010
- );
2011
- })), showCorrectAnswer && explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Explanation:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" }))));
1938
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1939
+ Label,
1940
+ {
1941
+ htmlFor: option.id,
1942
+ className: itemClassName,
1943
+ children: [
1944
+ /* @__PURE__ */ jsxRuntime.jsx(
1945
+ Checkbox,
1946
+ {
1947
+ id: option.id,
1948
+ checked: isSelected,
1949
+ onCheckedChange: (checked) => handleSelectionChange(option.id, !!checked),
1950
+ className: "mr-3",
1951
+ "aria-label": option.text.replace(/<[^>]*>?/gm, "")
1952
+ }
1953
+ ),
1954
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: option.text }) })
1955
+ ]
1956
+ },
1957
+ option.id
1958
+ );
1959
+ }) }),
1960
+ showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
1961
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
1962
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
1963
+ ] })
1964
+ ] })
1965
+ ] });
2012
1966
  };
2013
- var Input = React28__namespace.forwardRef(
2014
- (_a, ref) => {
2015
- var _b = _a, { className, type } = _b, props = __objRest(_b, ["className", "type"]);
2016
- return /* @__PURE__ */ React28__namespace.createElement(
1967
+ var Input = React9__namespace.forwardRef(
1968
+ ({ className, type, ...props }, ref) => {
1969
+ return /* @__PURE__ */ jsxRuntime.jsx(
2017
1970
  "input",
2018
- __spreadValues({
1971
+ {
2019
1972
  type,
2020
1973
  className: cn(
2021
1974
  "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
2022
1975
  className
2023
1976
  ),
2024
- ref
2025
- }, props)
1977
+ ref,
1978
+ ...props
1979
+ }
2026
1980
  );
2027
1981
  }
2028
1982
  );
2029
1983
  Input.displayName = "Input";
2030
-
2031
- // src/react-ui/components/ui/ShortAnswerQuestionUI.tsx
2032
1984
  var ShortAnswerQuestionUI = ({
2033
1985
  question,
2034
1986
  onAnswerChange,
@@ -2047,20 +1999,44 @@ var ShortAnswerQuestionUI = ({
2047
1999
  (accAns) => isCaseSensitive ? accAns.trim() === userAnswerTrimmed : accAns.trim().toLowerCase() === userAnswerTrimmed.toLowerCase()
2048
2000
  );
2049
2001
  }
2050
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: prompt })), points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0" }, /* @__PURE__ */ React28__namespace.default.createElement(Label, { htmlFor: `short-answer-input-${questionId}`, className: "sr-only" }, "Your Answer"), /* @__PURE__ */ React28__namespace.default.createElement(
2051
- Input,
2052
- {
2053
- id: `short-answer-input-${questionId}`,
2054
- type: "text",
2055
- value: displayUserAnswer,
2056
- onChange: handleInputChange,
2057
- placeholder: "Type your answer here...",
2058
- "aria-describedby": explanation ? `explanation-${questionId}` : void 0,
2059
- className: `
2002
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
2003
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
2004
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
2005
+ points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
2006
+ "Points: ",
2007
+ points
2008
+ ] })
2009
+ ] }),
2010
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
2011
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `short-answer-input-${questionId}`, className: "sr-only", children: "Your Answer" }),
2012
+ /* @__PURE__ */ jsxRuntime.jsx(
2013
+ Input,
2014
+ {
2015
+ id: `short-answer-input-${questionId}`,
2016
+ type: "text",
2017
+ value: displayUserAnswer,
2018
+ onChange: handleInputChange,
2019
+ placeholder: "Type your answer here...",
2020
+ "aria-describedby": explanation ? `explanation-${questionId}` : void 0,
2021
+ className: `
2060
2022
  ${showCorrectAnswer && userAnswer ? isActuallyCorrect ? "border-green-500 focus-visible:ring-green-500" : "border-destructive focus-visible:ring-destructive" : "border-input"}
2061
2023
  `
2062
- }
2063
- ), showCorrectAnswer && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 space-y-2" }, userAnswer && !isActuallyCorrect && /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-destructive" }, "Your answer was marked incorrect."), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Accepted Answers:"), /* @__PURE__ */ React28__namespace.default.createElement("ul", { className: "list-disc list-inside text-sm text-accent-foreground/80" }, acceptedAnswers.map((ans, idx) => /* @__PURE__ */ React28__namespace.default.createElement("li", { key: idx }, ans))), isCaseSensitive && /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xs text-muted-foreground mt-1" }, "(Case-sensitive)")), explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { id: `explanation-${questionId}`, className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold" }, "Explanation:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })))));
2024
+ }
2025
+ ),
2026
+ showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
2027
+ userAnswer && !isActuallyCorrect && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: "Your answer was marked incorrect." }),
2028
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-accent/20 border border-accent rounded-md", children: [
2029
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Accepted Answers:" }),
2030
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside text-sm text-accent-foreground/80", children: acceptedAnswers.map((ans, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: ans }, idx)) }),
2031
+ isCaseSensitive && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-1", children: "(Case-sensitive)" })
2032
+ ] }),
2033
+ explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `explanation-${questionId}`, className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md", children: [
2034
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "Explanation:" }),
2035
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })
2036
+ ] })
2037
+ ] })
2038
+ ] })
2039
+ ] });
2064
2040
  };
2065
2041
  var NumericQuestionUI = ({
2066
2042
  question,
@@ -2083,22 +2059,48 @@ var NumericQuestionUI = ({
2083
2059
  isActuallyCorrect = tolerance !== void 0 && tolerance !== null ? Math.abs(userAnswerNum - correctAnswerValue) <= tolerance : userAnswerNum === correctAnswerValue;
2084
2060
  }
2085
2061
  }
2086
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: prompt })), points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0" }, /* @__PURE__ */ React28__namespace.default.createElement(Label, { htmlFor: `numeric-input-${questionId}`, className: "sr-only" }, "Your Answer"), /* @__PURE__ */ React28__namespace.default.createElement(
2087
- Input,
2088
- {
2089
- id: `numeric-input-${questionId}`,
2090
- type: "text",
2091
- inputMode: "numeric",
2092
- pattern: "[0-9]*\\.?[0-9]*",
2093
- value: displayUserAnswer,
2094
- onChange: handleInputChange,
2095
- placeholder: "Enter a number",
2096
- "aria-describedby": explanation ? `explanation-${questionId}` : void 0,
2097
- className: `
2062
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
2063
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
2064
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
2065
+ points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
2066
+ "Points: ",
2067
+ points
2068
+ ] })
2069
+ ] }),
2070
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
2071
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `numeric-input-${questionId}`, className: "sr-only", children: "Your Answer" }),
2072
+ /* @__PURE__ */ jsxRuntime.jsx(
2073
+ Input,
2074
+ {
2075
+ id: `numeric-input-${questionId}`,
2076
+ type: "text",
2077
+ inputMode: "numeric",
2078
+ pattern: "[0-9]*\\.?[0-9]*",
2079
+ value: displayUserAnswer,
2080
+ onChange: handleInputChange,
2081
+ placeholder: "Enter a number",
2082
+ "aria-describedby": explanation ? `explanation-${questionId}` : void 0,
2083
+ className: `
2098
2084
  ${showCorrectAnswer && userAnswer !== null && userAnswer !== "" ? isActuallyCorrect ? "border-green-500 focus-visible:ring-green-500" : "border-destructive focus-visible:ring-destructive" : "border-input"}
2099
2085
  `
2100
- }
2101
- ), showCorrectAnswer && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 space-y-2" }, userAnswer !== null && userAnswer !== "" && !isActuallyCorrect && /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-destructive" }, "Your answer was marked incorrect."), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Correct Answer:"), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-accent-foreground/80" }, correctAnswerValue, tolerance !== void 0 && tolerance !== null && tolerance > 0 && ` (Tolerance: \xB1${tolerance}, Accepted range: ${correctAnswerValue - tolerance} to ${correctAnswerValue + tolerance})`)), explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { id: `explanation-${questionId}`, className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold" }, "Explanation:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })))));
2086
+ }
2087
+ ),
2088
+ showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
2089
+ userAnswer !== null && userAnswer !== "" && !isActuallyCorrect && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: "Your answer was marked incorrect." }),
2090
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-accent/20 border border-accent rounded-md", children: [
2091
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Correct Answer:" }),
2092
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-accent-foreground/80", children: [
2093
+ correctAnswerValue,
2094
+ tolerance !== void 0 && tolerance !== null && tolerance > 0 && ` (Tolerance: \xB1${tolerance}, Accepted range: ${correctAnswerValue - tolerance} to ${correctAnswerValue + tolerance})`
2095
+ ] })
2096
+ ] }),
2097
+ explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `explanation-${questionId}`, className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md", children: [
2098
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "Explanation:" }),
2099
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })
2100
+ ] })
2101
+ ] })
2102
+ ] })
2103
+ ] });
2102
2104
  };
2103
2105
  var FillInTheBlanksQuestionUI = ({
2104
2106
  question,
@@ -2107,8 +2109,8 @@ var FillInTheBlanksQuestionUI = ({
2107
2109
  showCorrectAnswer = false
2108
2110
  }) => {
2109
2111
  const { prompt, segments, answers: correctAnswersMap, points, explanation, id: questionId, isCaseSensitive } = question;
2110
- const [userInputs, setUserInputs] = React28.useState({});
2111
- React28.useEffect(() => {
2112
+ const [userInputs, setUserInputs] = React9.useState({});
2113
+ React9.useEffect(() => {
2112
2114
  if (userAnswer && typeof userAnswer === "object" && !Array.isArray(userAnswer)) {
2113
2115
  setUserInputs(userAnswer);
2114
2116
  } else {
@@ -2122,15 +2124,14 @@ var FillInTheBlanksQuestionUI = ({
2122
2124
  }
2123
2125
  }, [segments, userAnswer]);
2124
2126
  const handleInputChange = (blankId, value) => {
2125
- const newInputs = __spreadProps(__spreadValues({}, userInputs), { [blankId]: value });
2127
+ const newInputs = { ...userInputs, [blankId]: value };
2126
2128
  setUserInputs(newInputs);
2127
2129
  const hasValue = Object.values(newInputs).some((val) => val.trim() !== "");
2128
2130
  onAnswerChange(hasValue ? newInputs : null);
2129
2131
  };
2130
2132
  const getCorrectnessForBlank = (blankId) => {
2131
- var _a;
2132
2133
  if (!showCorrectAnswer || !userInputs[blankId]) return null;
2133
- const userAnswerForBlank = (_a = userInputs[blankId]) == null ? void 0 : _a.trim();
2134
+ const userAnswerForBlank = userInputs[blankId]?.trim();
2134
2135
  const correctAnswerDef = correctAnswersMap.find((a) => a.blankId === blankId);
2135
2136
  if (!correctAnswerDef || !userAnswerForBlank) return false;
2136
2137
  const caseSensitive = isCaseSensitive === void 0 ? false : isCaseSensitive;
@@ -2138,49 +2139,78 @@ var FillInTheBlanksQuestionUI = ({
2138
2139
  (accVal) => caseSensitive ? accVal.trim() === userAnswerForBlank : accVal.trim().toLowerCase() === userAnswerForBlank.toLowerCase()
2139
2140
  );
2140
2141
  };
2141
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: prompt })), points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "text-base leading-relaxed flex flex-wrap items-center gap-x-1", "aria-labelledby": `question-prompt-${questionId}` }, segments.map((segment, index) => {
2142
- var _a;
2143
- if (segment.type === "text") {
2144
- return /* @__PURE__ */ React28__namespace.default.createElement(
2145
- MarkdownRenderer,
2146
- {
2147
- key: `text-${index}`,
2148
- content: segment.content || "",
2149
- className: "inline"
2142
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
2143
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
2144
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
2145
+ points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
2146
+ "Points: ",
2147
+ points
2148
+ ] })
2149
+ ] }),
2150
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
2151
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base leading-relaxed flex flex-wrap items-center gap-x-1", "aria-labelledby": `question-prompt-${questionId}`, children: segments.map((segment, index) => {
2152
+ if (segment.type === "text") {
2153
+ return /* @__PURE__ */ jsxRuntime.jsx(
2154
+ MarkdownRenderer,
2155
+ {
2156
+ content: segment.content || "",
2157
+ className: "inline"
2158
+ },
2159
+ `text-${index}`
2160
+ );
2150
2161
  }
2151
- );
2152
- }
2153
- if (segment.type === "blank" && segment.id) {
2154
- const blankId = segment.id;
2155
- const isCorrect = getCorrectnessForBlank(blankId);
2156
- let inputClassName = "inline-block w-auto min-w-[100px] max-w-[200px] h-8 mx-1 align-baseline text-base";
2157
- if (showCorrectAnswer && ((_a = userInputs[blankId]) == null ? void 0 : _a.trim())) {
2158
- inputClassName += isCorrect ? " border-green-500 focus-visible:ring-green-500" : " border-destructive focus-visible:ring-destructive";
2159
- } else {
2160
- inputClassName += " border-input";
2161
- }
2162
- return /* @__PURE__ */ React28__namespace.default.createElement(
2163
- Input,
2164
- {
2165
- key: blankId,
2166
- id: blankId,
2167
- type: "text",
2168
- value: userInputs[blankId] || "",
2169
- onChange: (e) => handleInputChange(blankId, e.target.value),
2170
- placeholder: "\u0110i\u1EC1n...",
2171
- className: inputClassName,
2172
- "aria-label": `Blank ${index + 1}`,
2173
- disabled: showCorrectAnswer
2162
+ if (segment.type === "blank" && segment.id) {
2163
+ const blankId = segment.id;
2164
+ const isCorrect = getCorrectnessForBlank(blankId);
2165
+ let inputClassName = "inline-block w-auto min-w-[100px] max-w-[200px] h-8 mx-1 align-baseline text-base";
2166
+ if (showCorrectAnswer && userInputs[blankId]?.trim()) {
2167
+ inputClassName += isCorrect ? " border-green-500 focus-visible:ring-green-500" : " border-destructive focus-visible:ring-destructive";
2168
+ } else {
2169
+ inputClassName += " border-input";
2170
+ }
2171
+ return /* @__PURE__ */ jsxRuntime.jsx(
2172
+ Input,
2173
+ {
2174
+ id: blankId,
2175
+ type: "text",
2176
+ value: userInputs[blankId] || "",
2177
+ onChange: (e) => handleInputChange(blankId, e.target.value),
2178
+ placeholder: "\u0110i\u1EC1n...",
2179
+ className: inputClassName,
2180
+ "aria-label": `Blank ${index + 1}`,
2181
+ disabled: showCorrectAnswer
2182
+ },
2183
+ blankId
2184
+ );
2174
2185
  }
2175
- );
2176
- }
2177
- return null;
2178
- })), showCorrectAnswer && explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Gi\u1EA3i th\xEDch chung:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })), showCorrectAnswer && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 space-y-3" }, correctAnswersMap.map((ansDef) => {
2179
- var _a;
2180
- const isBlankCorrect = getCorrectnessForBlank(ansDef.blankId);
2181
- const userAnswerDisplay = userInputs[ansDef.blankId] || "Ch\u01B0a tr\u1EA3 l\u1EDDi";
2182
- return /* @__PURE__ */ React28__namespace.default.createElement("div", { key: `feedback-${ansDef.blankId}`, className: `p-2 border rounded-md ${isBlankCorrect ? "border-green-500/50 bg-green-500/10" : "border-destructive/50 bg-destructive/10"}` }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "font-semibold" }, "\xD4 tr\u1ED1ng '", ((_a = segments.find((s) => s.id === ansDef.blankId && s.type === "blank")) == null ? void 0 : _a.id) || ansDef.blankId, "':"), ' B\u1EA1n \u0111\xE3 \u0111i\u1EC1n: "', userAnswerDisplay, '".'), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xs" }, "\u0110\xE1p \xE1n ch\u1EA5p nh\u1EADn: ", ansDef.acceptedValues.join(", ")));
2183
- }))));
2186
+ return null;
2187
+ }) }),
2188
+ showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md", children: [
2189
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Gi\u1EA3i th\xEDch chung:" }),
2190
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
2191
+ ] }),
2192
+ showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 space-y-3", children: correctAnswersMap.map((ansDef) => {
2193
+ const isBlankCorrect = getCorrectnessForBlank(ansDef.blankId);
2194
+ const userAnswerDisplay = userInputs[ansDef.blankId] || "Ch\u01B0a tr\u1EA3 l\u1EDDi";
2195
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `p-2 border rounded-md ${isBlankCorrect ? "border-green-500/50 bg-green-500/10" : "border-destructive/50 bg-destructive/10"}`, children: [
2196
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm", children: [
2197
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
2198
+ "\xD4 tr\u1ED1ng '",
2199
+ segments.find((s) => s.id === ansDef.blankId && s.type === "blank")?.id || ansDef.blankId,
2200
+ "':"
2201
+ ] }),
2202
+ ' B\u1EA1n \u0111\xE3 \u0111i\u1EC1n: "',
2203
+ userAnswerDisplay,
2204
+ '".'
2205
+ ] }),
2206
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs", children: [
2207
+ "\u0110\xE1p \xE1n ch\u1EA5p nh\u1EADn: ",
2208
+ ansDef.acceptedValues.join(", ")
2209
+ ] })
2210
+ ] }, `feedback-${ansDef.blankId}`);
2211
+ }) })
2212
+ ] })
2213
+ ] });
2184
2214
  };
2185
2215
  var buttonVariants = classVarianceAuthority.cva(
2186
2216
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
@@ -2207,16 +2237,16 @@ var buttonVariants = classVarianceAuthority.cva(
2207
2237
  }
2208
2238
  }
2209
2239
  );
2210
- var Button = React28__namespace.forwardRef(
2211
- (_a, ref) => {
2212
- var _b = _a, { className, variant, size, asChild = false } = _b, props = __objRest(_b, ["className", "variant", "size", "asChild"]);
2240
+ var Button = React9__namespace.forwardRef(
2241
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
2213
2242
  const Comp = asChild ? reactSlot.Slot : "button";
2214
- return /* @__PURE__ */ React28__namespace.createElement(
2243
+ return /* @__PURE__ */ jsxRuntime.jsx(
2215
2244
  Comp,
2216
- __spreadValues({
2245
+ {
2217
2246
  className: cn(buttonVariants({ variant, size, className })),
2218
- ref
2219
- }, props)
2247
+ ref,
2248
+ ...props
2249
+ }
2220
2250
  );
2221
2251
  }
2222
2252
  );
@@ -2228,9 +2258,9 @@ var SequenceQuestionUI = ({
2228
2258
  showCorrectAnswer = false
2229
2259
  }) => {
2230
2260
  const { prompt, items, points, explanation, id: questionId, correctOrder } = question;
2231
- const [selectedSequence, setSelectedSequence] = React28.useState([]);
2232
- const [availableItems, setAvailableItems] = React28.useState([]);
2233
- React28.useEffect(() => {
2261
+ const [selectedSequence, setSelectedSequence] = React9.useState([]);
2262
+ const [availableItems, setAvailableItems] = React9.useState([]);
2263
+ React9.useEffect(() => {
2234
2264
  const initialUserOrder = Array.isArray(userAnswer) ? userAnswer : [];
2235
2265
  const initialSelected = [];
2236
2266
  const initialAvailable = [...items];
@@ -2271,145 +2301,180 @@ var SequenceQuestionUI = ({
2271
2301
  if (!showCorrectAnswer || !Array.isArray(userAnswer) || userAnswer.length <= index) return null;
2272
2302
  const userItemId = userAnswer[index];
2273
2303
  const correctItemId = correctOrder[index];
2274
- return userItemId === correctItemId ? /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.CheckCircle, { className: "h-4 w-4 text-green-500 ml-2 flex-shrink-0" }) : /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.XCircle, { className: "h-4 w-4 text-destructive ml-2 flex-shrink-0" });
2304
+ return userItemId === correctItemId ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-4 w-4 text-green-500 ml-2 flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-4 w-4 text-destructive ml-2 flex-shrink-0" });
2275
2305
  };
2276
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: prompt })), points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0 space-y-6" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React28__namespace.default.createElement(Label, { className: "font-semibold" }, "S\u1EAFp x\u1EBFp c\xE1c m\u1EE5c sau theo \u0111\xFAng th\u1EE9 t\u1EF1:"), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-2" }, availableItems.map((item) => /* @__PURE__ */ React28__namespace.default.createElement(
2277
- Button,
2278
- {
2279
- key: item.id,
2280
- variant: "outline",
2281
- onClick: () => handleSelectItem(item),
2282
- className: "justify-start text-left h-auto py-2 px-3 whitespace-normal",
2283
- disabled: showCorrectAnswer
2284
- },
2285
- /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: item.content })
2286
- ))), availableItems.length === 0 && selectedSequence.length > 0 && /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, 'T\u1EA5t c\u1EA3 c\xE1c m\u1EE5c \u0111\xE3 \u0111\u01B0\u1EE3c ch\u1ECDn. Nh\u1EA5p v\xE0o m\u1EE5c trong "Th\u1EE9 t\u1EF1 b\u1EA1n \u0111\xE3 ch\u1ECDn" \u0111\u1EC3 b\u1ECF ch\u1ECDn.')), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React28__namespace.default.createElement(Label, { className: "font-semibold" }, "Th\u1EE9 t\u1EF1 b\u1EA1n \u0111\xE3 ch\u1ECDn:"), /* @__PURE__ */ React28__namespace.default.createElement(Button, { variant: "ghost", size: "sm", onClick: handleResetSequence, disabled: showCorrectAnswer || selectedSequence.length === 0 }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.RotateCcw, { className: "mr-2 h-3.5 w-3.5" }), " \u0110\u1EB7t l\u1EA1i")), selectedSequence.length === 0 ? /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-muted-foreground p-3 border border-dashed rounded-md" }, "Ch\u01B0a ch\u1ECDn m\u1EE5c n\xE0o. Nh\u1EA5p v\xE0o c\xE1c m\u1EE5c \u1EDF tr\xEAn \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u.") : /* @__PURE__ */ React28__namespace.default.createElement("ul", { className: "space-y-2" }, selectedSequence.map((item, index) => /* @__PURE__ */ React28__namespace.default.createElement(
2287
- "li",
2288
- {
2289
- key: item.id,
2290
- onClick: () => handleRemoveFromSequence(item, index),
2291
- className: `flex items-center justify-between p-3 border rounded-md whitespace-normal ${showCorrectAnswer ? (userAnswer == null ? void 0 : userAnswer[index]) === correctOrder[index] ? "border-green-500 bg-green-500/10" : "border-destructive bg-destructive/10" : "bg-muted/30 cursor-pointer hover:border-destructive/50"} transition-colors`
2292
- },
2293
- /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex-grow flex items-center" }, /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "font-semibold mr-2" }, index + 1, "."), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: item.content })),
2294
- showCorrectAnswer ? getFeedbackIcon(index) : /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.XCircle, { className: "h-4 w-4 text-muted-foreground hover:text-destructive flex-shrink-0" })
2295
- )))), showCorrectAnswer && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 space-y-2" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Th\u1EE9 t\u1EF1 \u0111\xFAng:"), /* @__PURE__ */ React28__namespace.default.createElement("ol", { className: "list-decimal list-inside text-sm text-accent-foreground/80 space-y-1 mt-1" }, correctOrder.map((itemId) => {
2296
- const item = items.find((i) => i.id === itemId);
2297
- return /* @__PURE__ */ React28__namespace.default.createElement("li", { key: itemId }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: item ? item.content : "Kh\xF4ng t\xECm th\u1EA5y m\u1EE5c" }));
2298
- }))), explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold" }, "Gi\u1EA3i th\xEDch:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })))));
2306
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
2307
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
2308
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
2309
+ points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
2310
+ "Points: ",
2311
+ points
2312
+ ] })
2313
+ ] }),
2314
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0 space-y-6", children: [
2315
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
2316
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "font-semibold", children: "S\u1EAFp x\u1EBFp c\xE1c m\u1EE5c sau theo \u0111\xFAng th\u1EE9 t\u1EF1:" }),
2317
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-2", children: availableItems.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
2318
+ Button,
2319
+ {
2320
+ variant: "outline",
2321
+ onClick: () => handleSelectItem(item),
2322
+ className: "justify-start text-left h-auto py-2 px-3 whitespace-normal",
2323
+ disabled: showCorrectAnswer,
2324
+ children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: item.content })
2325
+ },
2326
+ item.id
2327
+ )) }),
2328
+ availableItems.length === 0 && selectedSequence.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: 'T\u1EA5t c\u1EA3 c\xE1c m\u1EE5c \u0111\xE3 \u0111\u01B0\u1EE3c ch\u1ECDn. Nh\u1EA5p v\xE0o m\u1EE5c trong "Th\u1EE9 t\u1EF1 b\u1EA1n \u0111\xE3 ch\u1ECDn" \u0111\u1EC3 b\u1ECF ch\u1ECDn.' })
2329
+ ] }),
2330
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2331
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
2332
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "font-semibold", children: "Th\u1EE9 t\u1EF1 b\u1EA1n \u0111\xE3 ch\u1ECDn:" }),
2333
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: handleResetSequence, disabled: showCorrectAnswer || selectedSequence.length === 0, children: [
2334
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "mr-2 h-3.5 w-3.5" }),
2335
+ " \u0110\u1EB7t l\u1EA1i"
2336
+ ] })
2337
+ ] }),
2338
+ selectedSequence.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground p-3 border border-dashed rounded-md", children: "Ch\u01B0a ch\u1ECDn m\u1EE5c n\xE0o. Nh\u1EA5p v\xE0o c\xE1c m\u1EE5c \u1EDF tr\xEAn \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u." }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-2", children: selectedSequence.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
2339
+ "li",
2340
+ {
2341
+ onClick: () => handleRemoveFromSequence(item, index),
2342
+ className: `flex items-center justify-between p-3 border rounded-md whitespace-normal ${showCorrectAnswer ? userAnswer?.[index] === correctOrder[index] ? "border-green-500 bg-green-500/10" : "border-destructive bg-destructive/10" : "bg-muted/30 cursor-pointer hover:border-destructive/50"} transition-colors`,
2343
+ children: [
2344
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow flex items-center", children: [
2345
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold mr-2", children: [
2346
+ index + 1,
2347
+ "."
2348
+ ] }),
2349
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: item.content })
2350
+ ] }),
2351
+ showCorrectAnswer ? getFeedbackIcon(index) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-4 w-4 text-muted-foreground hover:text-destructive flex-shrink-0" })
2352
+ ]
2353
+ },
2354
+ item.id
2355
+ )) })
2356
+ ] }),
2357
+ showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
2358
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-accent/20 border border-accent rounded-md", children: [
2359
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Th\u1EE9 t\u1EF1 \u0111\xFAng:" }),
2360
+ /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "list-decimal list-inside text-sm text-accent-foreground/80 space-y-1 mt-1", children: correctOrder.map((itemId) => {
2361
+ const item = items.find((i) => i.id === itemId);
2362
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: item ? item.content : "Kh\xF4ng t\xECm th\u1EA5y m\u1EE5c" }) }, itemId);
2363
+ }) })
2364
+ ] }),
2365
+ explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 p-3 bg-muted/30 border border-muted rounded-md", children: [
2366
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "Gi\u1EA3i th\xEDch:" }),
2367
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-muted-foreground" })
2368
+ ] })
2369
+ ] })
2370
+ ] })
2371
+ ] });
2299
2372
  };
2300
2373
  var Select = SelectPrimitive__namespace.Root;
2301
2374
  var SelectValue = SelectPrimitive__namespace.Value;
2302
- var SelectTrigger = React28__namespace.forwardRef((_a, ref) => {
2303
- var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
2304
- return /* @__PURE__ */ React28__namespace.createElement(
2305
- SelectPrimitive__namespace.Trigger,
2306
- __spreadValues({
2307
- ref,
2308
- className: cn(
2309
- "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
2310
- className
2311
- )
2312
- }, props),
2313
- children,
2314
- /* @__PURE__ */ React28__namespace.createElement(SelectPrimitive__namespace.Icon, { asChild: true }, /* @__PURE__ */ React28__namespace.createElement(lucideReact.ChevronDown, { className: "h-4 w-4 opacity-50" }))
2315
- );
2316
- });
2375
+ var SelectTrigger = React9__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
2376
+ SelectPrimitive__namespace.Trigger,
2377
+ {
2378
+ ref,
2379
+ className: cn(
2380
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
2381
+ className
2382
+ ),
2383
+ ...props,
2384
+ children: [
2385
+ children,
2386
+ /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 opacity-50" }) })
2387
+ ]
2388
+ }
2389
+ ));
2317
2390
  SelectTrigger.displayName = SelectPrimitive__namespace.Trigger.displayName;
2318
- var SelectScrollUpButton = React28__namespace.forwardRef((_a, ref) => {
2319
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
2320
- return /* @__PURE__ */ React28__namespace.createElement(
2321
- SelectPrimitive__namespace.ScrollUpButton,
2322
- __spreadValues({
2323
- ref,
2324
- className: cn(
2325
- "flex cursor-default items-center justify-center py-1",
2326
- className
2327
- )
2328
- }, props),
2329
- /* @__PURE__ */ React28__namespace.createElement(lucideReact.ChevronUp, { className: "h-4 w-4" })
2330
- );
2331
- });
2391
+ var SelectScrollUpButton = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2392
+ SelectPrimitive__namespace.ScrollUpButton,
2393
+ {
2394
+ ref,
2395
+ className: cn(
2396
+ "flex cursor-default items-center justify-center py-1",
2397
+ className
2398
+ ),
2399
+ ...props,
2400
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "h-4 w-4" })
2401
+ }
2402
+ ));
2332
2403
  SelectScrollUpButton.displayName = SelectPrimitive__namespace.ScrollUpButton.displayName;
2333
- var SelectScrollDownButton = React28__namespace.forwardRef((_a, ref) => {
2334
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
2335
- return /* @__PURE__ */ React28__namespace.createElement(
2336
- SelectPrimitive__namespace.ScrollDownButton,
2337
- __spreadValues({
2338
- ref,
2339
- className: cn(
2340
- "flex cursor-default items-center justify-center py-1",
2341
- className
2342
- )
2343
- }, props),
2344
- /* @__PURE__ */ React28__namespace.createElement(lucideReact.ChevronDown, { className: "h-4 w-4" })
2345
- );
2346
- });
2404
+ var SelectScrollDownButton = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2405
+ SelectPrimitive__namespace.ScrollDownButton,
2406
+ {
2407
+ ref,
2408
+ className: cn(
2409
+ "flex cursor-default items-center justify-center py-1",
2410
+ className
2411
+ ),
2412
+ ...props,
2413
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4" })
2414
+ }
2415
+ ));
2347
2416
  SelectScrollDownButton.displayName = SelectPrimitive__namespace.ScrollDownButton.displayName;
2348
- var SelectContent = React28__namespace.forwardRef((_a, ref) => {
2349
- var _b = _a, { className, children, position = "popper" } = _b, props = __objRest(_b, ["className", "children", "position"]);
2350
- return /* @__PURE__ */ React28__namespace.createElement(SelectPrimitive__namespace.Portal, null, /* @__PURE__ */ React28__namespace.createElement(
2351
- SelectPrimitive__namespace.Content,
2352
- __spreadValues({
2353
- ref,
2354
- className: cn(
2355
- "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
2356
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
2357
- className
2358
- ),
2359
- position
2360
- }, props),
2361
- /* @__PURE__ */ React28__namespace.createElement(SelectScrollUpButton, null),
2362
- /* @__PURE__ */ React28__namespace.createElement(
2363
- SelectPrimitive__namespace.Viewport,
2364
- {
2365
- className: cn(
2366
- "p-1",
2367
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
2368
- )
2369
- },
2370
- children
2417
+ var SelectContent = React9__namespace.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
2418
+ SelectPrimitive__namespace.Content,
2419
+ {
2420
+ ref,
2421
+ className: cn(
2422
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
2423
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
2424
+ className
2371
2425
  ),
2372
- /* @__PURE__ */ React28__namespace.createElement(SelectScrollDownButton, null)
2373
- ));
2374
- });
2426
+ position,
2427
+ ...props,
2428
+ children: [
2429
+ /* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
2430
+ /* @__PURE__ */ jsxRuntime.jsx(
2431
+ SelectPrimitive__namespace.Viewport,
2432
+ {
2433
+ className: cn(
2434
+ "p-1",
2435
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
2436
+ ),
2437
+ children
2438
+ }
2439
+ ),
2440
+ /* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
2441
+ ]
2442
+ }
2443
+ ) }));
2375
2444
  SelectContent.displayName = SelectPrimitive__namespace.Content.displayName;
2376
- var SelectLabel = React28__namespace.forwardRef((_a, ref) => {
2377
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
2378
- return /* @__PURE__ */ React28__namespace.createElement(
2379
- SelectPrimitive__namespace.Label,
2380
- __spreadValues({
2381
- ref,
2382
- className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)
2383
- }, props)
2384
- );
2385
- });
2445
+ var SelectLabel = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2446
+ SelectPrimitive__namespace.Label,
2447
+ {
2448
+ ref,
2449
+ className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
2450
+ ...props
2451
+ }
2452
+ ));
2386
2453
  SelectLabel.displayName = SelectPrimitive__namespace.Label.displayName;
2387
- var SelectItem = React28__namespace.forwardRef((_a, ref) => {
2388
- var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
2389
- return /* @__PURE__ */ React28__namespace.createElement(
2390
- SelectPrimitive__namespace.Item,
2391
- __spreadValues({
2392
- ref,
2393
- className: cn(
2394
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
2395
- className
2396
- )
2397
- }, props),
2398
- /* @__PURE__ */ React28__namespace.createElement("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center" }, /* @__PURE__ */ React28__namespace.createElement(SelectPrimitive__namespace.ItemIndicator, null, /* @__PURE__ */ React28__namespace.createElement(lucideReact.Check, { className: "h-4 w-4" }))),
2399
- /* @__PURE__ */ React28__namespace.createElement(SelectPrimitive__namespace.ItemText, null, children)
2400
- );
2401
- });
2454
+ var SelectItem = React9__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
2455
+ SelectPrimitive__namespace.Item,
2456
+ {
2457
+ ref,
2458
+ className: cn(
2459
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
2460
+ className
2461
+ ),
2462
+ ...props,
2463
+ children: [
2464
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }) }) }),
2465
+ /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemText, { children })
2466
+ ]
2467
+ }
2468
+ ));
2402
2469
  SelectItem.displayName = SelectPrimitive__namespace.Item.displayName;
2403
- var SelectSeparator = React28__namespace.forwardRef((_a, ref) => {
2404
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
2405
- return /* @__PURE__ */ React28__namespace.createElement(
2406
- SelectPrimitive__namespace.Separator,
2407
- __spreadValues({
2408
- ref,
2409
- className: cn("-mx-1 my-1 h-px bg-muted", className)
2410
- }, props)
2411
- );
2412
- });
2470
+ var SelectSeparator = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2471
+ SelectPrimitive__namespace.Separator,
2472
+ {
2473
+ ref,
2474
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
2475
+ ...props
2476
+ }
2477
+ ));
2413
2478
  SelectSeparator.displayName = SelectPrimitive__namespace.Separator.displayName;
2414
2479
  var MatchingQuestionUI = ({
2415
2480
  question,
@@ -2418,16 +2483,16 @@ var MatchingQuestionUI = ({
2418
2483
  showCorrectAnswer = false
2419
2484
  }) => {
2420
2485
  const { prompt, prompts, options: initialOptions, points, explanation, correctAnswerMap, id: questionId } = question;
2421
- const [currentAnswers, setCurrentAnswers] = React28.useState({});
2422
- const [shuffledOptions, setShuffledOptions] = React28.useState(initialOptions);
2423
- React28.useEffect(() => {
2486
+ const [currentAnswers, setCurrentAnswers] = React9.useState({});
2487
+ const [shuffledOptions, setShuffledOptions] = React9.useState(initialOptions);
2488
+ React9.useEffect(() => {
2424
2489
  if (question.shuffleOptions) {
2425
2490
  setShuffledOptions([...initialOptions].sort(() => Math.random() - 0.5));
2426
2491
  } else {
2427
2492
  setShuffledOptions(initialOptions);
2428
2493
  }
2429
2494
  }, [initialOptions, question.shuffleOptions]);
2430
- React28.useEffect(() => {
2495
+ React9.useEffect(() => {
2431
2496
  if (userAnswer && typeof userAnswer === "object" && !Array.isArray(userAnswer)) {
2432
2497
  setCurrentAnswers(userAnswer);
2433
2498
  } else {
@@ -2437,14 +2502,13 @@ var MatchingQuestionUI = ({
2437
2502
  }
2438
2503
  }, [userAnswer, prompts]);
2439
2504
  const handleSelectChange = (promptId, optionId) => {
2440
- const newAnswers = __spreadProps(__spreadValues({}, currentAnswers), { [promptId]: optionId });
2505
+ const newAnswers = { ...currentAnswers, [promptId]: optionId };
2441
2506
  setCurrentAnswers(newAnswers);
2442
2507
  const hasSelection = Object.values(newAnswers).some((val) => val && val !== "");
2443
2508
  onAnswerChange(hasSelection ? newAnswers : null);
2444
2509
  };
2445
2510
  const getCorrectOptionIdForPrompt = (promptId) => {
2446
- var _a;
2447
- return (_a = correctAnswerMap.find((map) => map.promptId === promptId)) == null ? void 0 : _a.optionId;
2511
+ return correctAnswerMap.find((map) => map.promptId === promptId)?.optionId;
2448
2512
  };
2449
2513
  const getPlainText = (htmlString) => {
2450
2514
  if (!htmlString) return "";
@@ -2455,31 +2519,58 @@ var MatchingQuestionUI = ({
2455
2519
  }
2456
2520
  return htmlString.replace(/<[^>]*>?/gm, "");
2457
2521
  };
2458
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: prompt })), points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0 space-y-4" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-4" }, prompts.map((promptItem) => {
2459
- var _a;
2460
- const selectedOptionId = currentAnswers[promptItem.id] || "";
2461
- const correctOptionId = getCorrectOptionIdForPrompt(promptItem.id);
2462
- const isSelectionCorrect = showCorrectAnswer && selectedOptionId ? selectedOptionId === correctOptionId : null;
2463
- let borderColor = "border-muted";
2464
- if (showCorrectAnswer && selectedOptionId) {
2465
- borderColor = isSelectionCorrect ? "border-green-500" : "border-destructive";
2466
- }
2467
- return /* @__PURE__ */ React28__namespace.default.createElement("div", { key: promptItem.id, className: `p-3 border rounded-md ${borderColor} transition-colors bg-background` }, /* @__PURE__ */ React28__namespace.default.createElement(Label, { htmlFor: `select-prompt-${promptItem.id}`, className: "font-medium text-base block mb-2 whitespace-normal" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: promptItem.content })), /* @__PURE__ */ React28__namespace.default.createElement(
2468
- Select,
2469
- {
2470
- value: selectedOptionId,
2471
- onValueChange: (value) => handleSelectChange(promptItem.id, value),
2472
- disabled: showCorrectAnswer
2473
- },
2474
- /* @__PURE__ */ React28__namespace.default.createElement(SelectTrigger, { id: `select-prompt-${promptItem.id}` }, /* @__PURE__ */ React28__namespace.default.createElement(SelectValue, { placeholder: "Ch\u1ECDn \u0111\xE1p \xE1n..." })),
2475
- /* @__PURE__ */ React28__namespace.default.createElement(SelectContent, null, shuffledOptions.map((option) => /* @__PURE__ */ React28__namespace.default.createElement(SelectItem, { key: option.id, value: option.id, className: "whitespace-normal" }, getPlainText(option.content))))
2476
- ), showCorrectAnswer && selectedOptionId && (isSelectionCorrect ? /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.CheckCircle, { className: "h-5 w-5 text-green-500 mt-2 inline-block" }) : /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.XCircle, { className: "h-5 w-5 text-destructive mt-2 inline-block" })), showCorrectAnswer && !selectedOptionId && correctOptionId && /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xs text-muted-foreground mt-1" }, "Ch\u01B0a ch\u1ECDn. \u0110\xE1p \xE1n \u0111\xFAng: ", getPlainText((_a = shuffledOptions.find((o) => o.id === correctOptionId)) == null ? void 0 : _a.content)));
2477
- })), showCorrectAnswer && explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Gi\u1EA3i th\xEDch:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })), showCorrectAnswer && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 space-y-2" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "font-semibold text-md" }, "\u0110\xE1p \xE1n \u0111\xFAng:"), /* @__PURE__ */ React28__namespace.default.createElement("ul", { className: "list-disc list-inside space-y-1 text-sm" }, correctAnswerMap.map((map) => {
2478
- var _a, _b;
2479
- const promptText = (_a = prompts.find((p) => p.id === map.promptId)) == null ? void 0 : _a.content;
2480
- const optionText = (_b = initialOptions.find((o) => o.id === map.optionId)) == null ? void 0 : _b.content;
2481
- return /* @__PURE__ */ React28__namespace.default.createElement("li", { key: map.promptId }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: `<strong>${getPlainText(promptText) || "N/A"}</strong> gh\xE9p v\u1EDBi <strong>${getPlainText(optionText) || "N/A"}</strong>` }));
2482
- })))));
2522
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
2523
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
2524
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
2525
+ points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
2526
+ "Points: ",
2527
+ points
2528
+ ] })
2529
+ ] }),
2530
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0 space-y-4", children: [
2531
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-4", children: prompts.map((promptItem) => {
2532
+ const selectedOptionId = currentAnswers[promptItem.id] || "";
2533
+ const correctOptionId = getCorrectOptionIdForPrompt(promptItem.id);
2534
+ const isSelectionCorrect = showCorrectAnswer && selectedOptionId ? selectedOptionId === correctOptionId : null;
2535
+ let borderColor = "border-muted";
2536
+ if (showCorrectAnswer && selectedOptionId) {
2537
+ borderColor = isSelectionCorrect ? "border-green-500" : "border-destructive";
2538
+ }
2539
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `p-3 border rounded-md ${borderColor} transition-colors bg-background`, children: [
2540
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `select-prompt-${promptItem.id}`, className: "font-medium text-base block mb-2 whitespace-normal", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: promptItem.content }) }),
2541
+ /* @__PURE__ */ jsxRuntime.jsxs(
2542
+ Select,
2543
+ {
2544
+ value: selectedOptionId,
2545
+ onValueChange: (value) => handleSelectChange(promptItem.id, value),
2546
+ disabled: showCorrectAnswer,
2547
+ children: [
2548
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: `select-prompt-${promptItem.id}`, children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Ch\u1ECDn \u0111\xE1p \xE1n..." }) }),
2549
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: shuffledOptions.map((option) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: option.id, className: "whitespace-normal", children: getPlainText(option.content) }, option.id)) })
2550
+ ]
2551
+ }
2552
+ ),
2553
+ showCorrectAnswer && selectedOptionId && (isSelectionCorrect ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-5 w-5 text-green-500 mt-2 inline-block" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-5 w-5 text-destructive mt-2 inline-block" })),
2554
+ showCorrectAnswer && !selectedOptionId && correctOptionId && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: [
2555
+ "Ch\u01B0a ch\u1ECDn. \u0110\xE1p \xE1n \u0111\xFAng: ",
2556
+ getPlainText(shuffledOptions.find((o) => o.id === correctOptionId)?.content)
2557
+ ] })
2558
+ ] }, promptItem.id);
2559
+ }) }),
2560
+ showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md", children: [
2561
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Gi\u1EA3i th\xEDch:" }),
2562
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
2563
+ ] }),
2564
+ showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
2565
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-md", children: "\u0110\xE1p \xE1n \u0111\xFAng:" }),
2566
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside space-y-1 text-sm", children: correctAnswerMap.map((map) => {
2567
+ const promptText = prompts.find((p) => p.id === map.promptId)?.content;
2568
+ const optionText = initialOptions.find((o) => o.id === map.optionId)?.content;
2569
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: `<strong>${getPlainText(promptText) || "N/A"}</strong> gh\xE9p v\u1EDBi <strong>${getPlainText(optionText) || "N/A"}</strong>` }) }, map.promptId);
2570
+ }) })
2571
+ ] })
2572
+ ] })
2573
+ ] });
2483
2574
  };
2484
2575
  var DragAndDropQuestionUI = ({
2485
2576
  question,
@@ -2488,8 +2579,8 @@ var DragAndDropQuestionUI = ({
2488
2579
  showCorrectAnswer = false
2489
2580
  }) => {
2490
2581
  const { prompt, draggableItems, dropZones, points, explanation, answerMap, id: questionId, backgroundImageUrl } = question;
2491
- const [currentAnswers, setCurrentAnswers] = React28.useState({});
2492
- React28.useEffect(() => {
2582
+ const [currentAnswers, setCurrentAnswers] = React9.useState({});
2583
+ React9.useEffect(() => {
2493
2584
  if (userAnswer && typeof userAnswer === "object" && !Array.isArray(userAnswer)) {
2494
2585
  setCurrentAnswers(userAnswer);
2495
2586
  } else {
@@ -2499,41 +2590,70 @@ var DragAndDropQuestionUI = ({
2499
2590
  }
2500
2591
  }, [userAnswer, draggableItems]);
2501
2592
  const handleSelectChange = (draggableItemId, dropZoneId) => {
2502
- const newAnswers = __spreadProps(__spreadValues({}, currentAnswers), { [draggableItemId]: dropZoneId });
2593
+ const newAnswers = { ...currentAnswers, [draggableItemId]: dropZoneId };
2503
2594
  setCurrentAnswers(newAnswers);
2504
2595
  const hasSelection = Object.values(newAnswers).some((val) => val && val !== "");
2505
2596
  onAnswerChange(hasSelection ? newAnswers : null);
2506
2597
  };
2507
2598
  const getCorrectDropZoneIdForDraggable = (draggableItemId) => {
2508
- var _a;
2509
- return (_a = answerMap.find((map) => map.draggableId === draggableItemId)) == null ? void 0 : _a.dropZoneId;
2599
+ return answerMap.find((map) => map.draggableId === draggableItemId)?.dropZoneId;
2510
2600
  };
2511
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: prompt })), points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0 space-y-4" }, backgroundImageUrl && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mb-4 overflow-hidden rounded-md border" }, /* @__PURE__ */ React28__namespace.default.createElement("img", { src: backgroundImageUrl, alt: question.imageAltText || "Drag and drop background", className: "w-full h-auto object-contain max-h-[300px]", "data-ai-hint": "abstract pattern" })), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React28__namespace.default.createElement(Label, { className: "font-semibold" }, "Gh\xE9p c\xE1c m\u1EE5c sau v\xE0o \u0111\xFAng v\u1ECB tr\xED:"), draggableItems.map((item) => {
2512
- const selectedDropZoneId = currentAnswers[item.id] || "";
2513
- const correctDropZoneId = getCorrectDropZoneIdForDraggable(item.id);
2514
- const isSelectionCorrect = showCorrectAnswer && selectedDropZoneId ? selectedDropZoneId === correctDropZoneId : null;
2515
- let itemStyle = "flex flex-col sm:flex-row items-start sm:items-center justify-between p-3 border rounded-md transition-colors bg-background";
2516
- if (showCorrectAnswer && selectedDropZoneId) {
2517
- itemStyle += isSelectionCorrect ? " border-green-500" : " border-destructive";
2518
- } else {
2519
- itemStyle += " border-muted";
2520
- }
2521
- return /* @__PURE__ */ React28__namespace.default.createElement("div", { key: item.id, className: itemStyle }, /* @__PURE__ */ React28__namespace.default.createElement(Label, { htmlFor: `select-draggable-${item.id}`, className: "font-medium text-base mb-2 sm:mb-0 sm:mr-4 flex-1" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: item.content })), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex items-center space-x-2 w-full sm:w-auto" }, /* @__PURE__ */ React28__namespace.default.createElement(
2522
- Select,
2523
- {
2524
- value: selectedDropZoneId,
2525
- onValueChange: (value) => handleSelectChange(item.id, value),
2526
- disabled: showCorrectAnswer
2527
- },
2528
- /* @__PURE__ */ React28__namespace.default.createElement(SelectTrigger, { id: `select-draggable-${item.id}`, className: "w-full sm:min-w-[200px]" }, /* @__PURE__ */ React28__namespace.default.createElement(SelectValue, { placeholder: "Ch\u1ECDn v\u1ECB tr\xED..." })),
2529
- /* @__PURE__ */ React28__namespace.default.createElement(SelectContent, null, dropZones.map((zone) => /* @__PURE__ */ React28__namespace.default.createElement(SelectItem, { key: zone.id, value: zone.id }, zone.label.replace(/<[^>]*>?/gm, ""))))
2530
- ), showCorrectAnswer && selectedDropZoneId && (isSelectionCorrect ? /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.CheckCircle, { className: "h-5 w-5 text-green-500 flex-shrink-0" }) : /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.XCircle, { className: "h-5 w-5 text-destructive flex-shrink-0" }))));
2531
- })), showCorrectAnswer && explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Gi\u1EA3i th\xEDch:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })), showCorrectAnswer && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 space-y-2" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "font-semibold text-md" }, "\u0110\xE1p \xE1n \u0111\xFAng:"), /* @__PURE__ */ React28__namespace.default.createElement("ul", { className: "list-disc list-inside space-y-1 text-sm" }, answerMap.map((map) => {
2532
- var _a, _b;
2533
- const draggableText = (_a = draggableItems.find((d) => d.id === map.draggableId)) == null ? void 0 : _a.content;
2534
- const dropZoneText = (_b = dropZones.find((z3) => z3.id === map.dropZoneId)) == null ? void 0 : _b.label;
2535
- return /* @__PURE__ */ React28__namespace.default.createElement("li", { key: map.draggableId }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: `<strong>${draggableText || "N/A"}</strong> v\xE0o <strong>${dropZoneText || "N/A"}</strong>` }));
2536
- })))));
2601
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
2602
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
2603
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
2604
+ points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
2605
+ "Points: ",
2606
+ points
2607
+ ] })
2608
+ ] }),
2609
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0 space-y-4", children: [
2610
+ backgroundImageUrl && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 overflow-hidden rounded-md border", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: backgroundImageUrl, alt: question.imageAltText || "Drag and drop background", className: "w-full h-auto object-contain max-h-[300px]", "data-ai-hint": "abstract pattern" }) }),
2611
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
2612
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "font-semibold", children: "Gh\xE9p c\xE1c m\u1EE5c sau v\xE0o \u0111\xFAng v\u1ECB tr\xED:" }),
2613
+ draggableItems.map((item) => {
2614
+ const selectedDropZoneId = currentAnswers[item.id] || "";
2615
+ const correctDropZoneId = getCorrectDropZoneIdForDraggable(item.id);
2616
+ const isSelectionCorrect = showCorrectAnswer && selectedDropZoneId ? selectedDropZoneId === correctDropZoneId : null;
2617
+ let itemStyle = "flex flex-col sm:flex-row items-start sm:items-center justify-between p-3 border rounded-md transition-colors bg-background";
2618
+ if (showCorrectAnswer && selectedDropZoneId) {
2619
+ itemStyle += isSelectionCorrect ? " border-green-500" : " border-destructive";
2620
+ } else {
2621
+ itemStyle += " border-muted";
2622
+ }
2623
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: itemStyle, children: [
2624
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `select-draggable-${item.id}`, className: "font-medium text-base mb-2 sm:mb-0 sm:mr-4 flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: item.content }) }),
2625
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2 w-full sm:w-auto", children: [
2626
+ /* @__PURE__ */ jsxRuntime.jsxs(
2627
+ Select,
2628
+ {
2629
+ value: selectedDropZoneId,
2630
+ onValueChange: (value) => handleSelectChange(item.id, value),
2631
+ disabled: showCorrectAnswer,
2632
+ children: [
2633
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: `select-draggable-${item.id}`, className: "w-full sm:min-w-[200px]", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Ch\u1ECDn v\u1ECB tr\xED..." }) }),
2634
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: dropZones.map((zone) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: zone.id, children: zone.label.replace(/<[^>]*>?/gm, "") }, zone.id)) })
2635
+ ]
2636
+ }
2637
+ ),
2638
+ showCorrectAnswer && selectedDropZoneId && (isSelectionCorrect ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-5 w-5 text-green-500 flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-5 w-5 text-destructive flex-shrink-0" }))
2639
+ ] })
2640
+ ] }, item.id);
2641
+ })
2642
+ ] }),
2643
+ showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 p-3 bg-accent/20 border border-accent rounded-md", children: [
2644
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Gi\u1EA3i th\xEDch:" }),
2645
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
2646
+ ] }),
2647
+ showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
2648
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-md", children: "\u0110\xE1p \xE1n \u0111\xFAng:" }),
2649
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside space-y-1 text-sm", children: answerMap.map((map) => {
2650
+ const draggableText = draggableItems.find((d) => d.id === map.draggableId)?.content;
2651
+ const dropZoneText = dropZones.find((z3) => z3.id === map.dropZoneId)?.label;
2652
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: `<strong>${draggableText || "N/A"}</strong> v\xE0o <strong>${dropZoneText || "N/A"}</strong>` }) }, map.draggableId);
2653
+ }) })
2654
+ ] })
2655
+ ] })
2656
+ ] });
2537
2657
  };
2538
2658
  var HotspotQuestionUI = ({
2539
2659
  question,
@@ -2542,8 +2662,8 @@ var HotspotQuestionUI = ({
2542
2662
  showCorrectAnswer = false
2543
2663
  }) => {
2544
2664
  const { prompt, imageUrl, imageAltText, hotspots, points, explanation, correctAnswerIds, id: questionId } = question;
2545
- const [selectedIds, setSelectedIds] = React28.useState([]);
2546
- React28.useEffect(() => {
2665
+ const [selectedIds, setSelectedIds] = React9.useState([]);
2666
+ React9.useEffect(() => {
2547
2667
  if (Array.isArray(userAnswer)) {
2548
2668
  setSelectedIds(userAnswer);
2549
2669
  } else {
@@ -2603,32 +2723,54 @@ var HotspotQuestionUI = ({
2603
2723
  tempDiv.innerHTML = htmlString;
2604
2724
  return tempDiv.textContent || tempDiv.innerText || "";
2605
2725
  };
2606
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: prompt })), points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0 space-y-4" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "relative w-full border border-muted rounded-md overflow-hidden", style: { maxWidth: "100%", maxHeight: "500px" } }, /* @__PURE__ */ React28__namespace.default.createElement(
2607
- "img",
2608
- {
2609
- src: imageUrl,
2610
- alt: imageAltText || "Hotspot image",
2611
- className: "block max-w-full max-h-full object-contain",
2612
- "data-ai-hint": question.imageAltText ? question.imageAltText.split(" ").slice(0, 2).join(" ") : "diagram illustration"
2613
- }
2614
- ), hotspots.map((hotspot) => /* @__PURE__ */ React28__namespace.default.createElement(
2615
- "div",
2616
- {
2617
- key: hotspot.id,
2618
- title: getPlainText(hotspot.description) || `Hotspot ${hotspot.id}`,
2619
- style: getHotspotStyle(hotspot),
2620
- onClick: () => handleHotspotClick(hotspot.id),
2621
- "aria-pressed": selectedIds.includes(hotspot.id),
2622
- role: "button",
2623
- tabIndex: showCorrectAnswer ? -1 : 0,
2624
- onKeyDown: (e) => {
2625
- if (e.key === "Enter" || e.key === " ") handleHotspotClick(hotspot.id);
2626
- }
2627
- }
2628
- ))), showCorrectAnswer && explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Explanation:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })), showCorrectAnswer && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 space-y-2" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "font-semibold text-md" }, "Correct Hotspots:"), /* @__PURE__ */ React28__namespace.default.createElement("ul", { className: "list-disc list-inside space-y-1 text-sm" }, (correctAnswerIds || []).map((id) => {
2629
- const hotspot = hotspots.find((h) => h.id === id);
2630
- return /* @__PURE__ */ React28__namespace.default.createElement("li", { key: id }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: (hotspot == null ? void 0 : hotspot.description) || (hotspot == null ? void 0 : hotspot.id) || "N/A", className: "inline" }));
2631
- })))));
2726
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
2727
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
2728
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: prompt }) }),
2729
+ points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
2730
+ "Points: ",
2731
+ points
2732
+ ] })
2733
+ ] }),
2734
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0 space-y-4", children: [
2735
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full border border-muted rounded-md overflow-hidden", style: { maxWidth: "100%", maxHeight: "500px" }, children: [
2736
+ /* @__PURE__ */ jsxRuntime.jsx(
2737
+ "img",
2738
+ {
2739
+ src: imageUrl,
2740
+ alt: imageAltText || "Hotspot image",
2741
+ className: "block max-w-full max-h-full object-contain",
2742
+ "data-ai-hint": question.imageAltText ? question.imageAltText.split(" ").slice(0, 2).join(" ") : "diagram illustration"
2743
+ }
2744
+ ),
2745
+ hotspots.map((hotspot) => /* @__PURE__ */ jsxRuntime.jsx(
2746
+ "div",
2747
+ {
2748
+ title: getPlainText(hotspot.description) || `Hotspot ${hotspot.id}`,
2749
+ style: getHotspotStyle(hotspot),
2750
+ onClick: () => handleHotspotClick(hotspot.id),
2751
+ "aria-pressed": selectedIds.includes(hotspot.id),
2752
+ role: "button",
2753
+ tabIndex: showCorrectAnswer ? -1 : 0,
2754
+ onKeyDown: (e) => {
2755
+ if (e.key === "Enter" || e.key === " ") handleHotspotClick(hotspot.id);
2756
+ }
2757
+ },
2758
+ hotspot.id
2759
+ ))
2760
+ ] }),
2761
+ showCorrectAnswer && explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
2762
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
2763
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: explanation, className: "text-sm text-accent-foreground/80" })
2764
+ ] }),
2765
+ showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
2766
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-md", children: "Correct Hotspots:" }),
2767
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "list-disc list-inside space-y-1 text-sm", children: (correctAnswerIds || []).map((id) => {
2768
+ const hotspot = hotspots.find((h) => h.id === id);
2769
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: hotspot?.description || hotspot?.id || "N/A", className: "inline" }) }, id);
2770
+ }) })
2771
+ ] })
2772
+ ] })
2773
+ ] });
2632
2774
  };
2633
2775
  var loadScript = (src, async = true) => {
2634
2776
  return new Promise((resolve, reject) => {
@@ -2661,8 +2803,7 @@ var loadScript = (src, async = true) => {
2661
2803
  };
2662
2804
  var loadBlocklyScript = () => {
2663
2805
  return new Promise((resolve, reject) => {
2664
- var _a, _b;
2665
- if (typeof ((_a = window.Blockly) == null ? void 0 : _a.Blocks) !== "undefined" && typeof ((_b = window.Blockly) == null ? void 0 : _b.JavaScript) !== "undefined") {
2806
+ if (typeof window.Blockly?.Blocks !== "undefined" && typeof window.Blockly?.JavaScript !== "undefined") {
2666
2807
  resolve();
2667
2808
  return;
2668
2809
  }
@@ -2690,7 +2831,6 @@ var loadBlocklyScript = () => {
2690
2831
  }
2691
2832
  ];
2692
2833
  const tryLoadFromCDN = async (cdnIndex) => {
2693
- var _a2, _b2, _c;
2694
2834
  if (cdnIndex >= cdnOptions.length) {
2695
2835
  throw new Error("All Blockly CDN loading options failed");
2696
2836
  }
@@ -2699,9 +2839,9 @@ var loadBlocklyScript = () => {
2699
2839
  await loadScript(cdn.mainSrc);
2700
2840
  const BlocklyGlobal = window.Blockly;
2701
2841
  if (typeof BlocklyGlobal === "undefined") throw new Error(`Blockly global not found from ${cdn.name}.`);
2702
- if ((_b2 = (_a2 = BlocklyGlobal.utils) == null ? void 0 : _a2.global) == null ? void 0 : _b2.setPaths) {
2842
+ if (BlocklyGlobal.utils?.global?.setPaths) {
2703
2843
  BlocklyGlobal.utils.global.setPaths(cdn.mediaPath);
2704
- } else if ((_c = BlocklyGlobal.utils) == null ? void 0 : _c.global) {
2844
+ } else if (BlocklyGlobal.utils?.global) {
2705
2845
  BlocklyGlobal.utils.global.blocklyPath = cdn.mediaPath;
2706
2846
  BlocklyGlobal.MEDIA = cdn.mediaPath;
2707
2847
  } else {
@@ -2719,10 +2859,10 @@ var loadBlocklyScript = () => {
2719
2859
  });
2720
2860
  };
2721
2861
  var useBlocklyLoader = () => {
2722
- const [isLoading, setIsLoading] = React28.useState(true);
2723
- const [loadError, setLoadError] = React28.useState(null);
2724
- const [isReady, setIsReady] = React28.useState(false);
2725
- const attemptLoad = React28.useCallback(() => {
2862
+ const [isLoading, setIsLoading] = React9.useState(true);
2863
+ const [loadError, setLoadError] = React9.useState(null);
2864
+ const [isReady, setIsReady] = React9.useState(false);
2865
+ const attemptLoad = React9.useCallback(() => {
2726
2866
  setLoadError(null);
2727
2867
  setIsReady(false);
2728
2868
  loadBlocklyScript().then(() => {
@@ -2735,32 +2875,31 @@ var useBlocklyLoader = () => {
2735
2875
  setIsReady(false);
2736
2876
  });
2737
2877
  }, []);
2738
- React28.useEffect(() => {
2878
+ React9.useEffect(() => {
2739
2879
  if (isLoading && !isReady && !loadError) attemptLoad();
2740
2880
  }, [isLoading, isReady, loadError, attemptLoad]);
2741
- const retry = React28.useCallback(() => {
2881
+ const retry = React9.useCallback(() => {
2742
2882
  setLoadError(null);
2743
2883
  setIsReady(false);
2744
2884
  setIsLoading(true);
2745
2885
  }, []);
2746
2886
  return { isLoading, loadError, isReady, retry };
2747
2887
  };
2748
- var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
2888
+ var BlocklyProgrammingQuestionUI = React9__namespace.default.forwardRef(({
2749
2889
  question,
2750
2890
  userAnswer,
2751
2891
  showCorrectAnswer = false
2752
2892
  }, ref) => {
2753
- const blocklyDivRef = React28.useRef(null);
2754
- const workspaceRef = React28.useRef(null);
2755
- const [isInitializingComponent, setIsInitializingComponent] = React28.useState(false);
2756
- const [componentError, setComponentError] = React28.useState(null);
2893
+ const blocklyDivRef = React9.useRef(null);
2894
+ const workspaceRef = React9.useRef(null);
2895
+ const [isInitializingComponent, setIsInitializingComponent] = React9.useState(false);
2896
+ const [componentError, setComponentError] = React9.useState(null);
2757
2897
  const { isLoading: blocklyLoading, loadError: blocklyLoadError, isReady: blocklyReady, retry } = useBlocklyLoader();
2758
- React28.useImperativeHandle(ref, () => ({
2898
+ React9.useImperativeHandle(ref, () => ({
2759
2899
  getWorkspaceXml: () => {
2760
- var _a, _b;
2761
2900
  if (workspaceRef.current && blocklyReady) {
2762
2901
  const LocalBlockly = window.Blockly;
2763
- if (!((_a = LocalBlockly == null ? void 0 : LocalBlockly.Xml) == null ? void 0 : _a.workspaceToDom) || !((_b = LocalBlockly == null ? void 0 : LocalBlockly.Xml) == null ? void 0 : _b.domToText)) {
2902
+ if (!LocalBlockly?.Xml?.workspaceToDom || !LocalBlockly?.Xml?.domToText) {
2764
2903
  console.warn("Blockly.Xml methods not available for XML serialization in getWorkspaceXml.");
2765
2904
  return null;
2766
2905
  }
@@ -2775,11 +2914,10 @@ var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
2775
2914
  return null;
2776
2915
  }
2777
2916
  }));
2778
- const initializeBlocklyWorkspace = React28.useCallback(() => {
2779
- var _a;
2917
+ const initializeBlocklyWorkspace = React9.useCallback(() => {
2780
2918
  if (!blocklyReady || !blocklyDivRef.current) return;
2781
2919
  const LocalBlockly = window.Blockly;
2782
- if (!(LocalBlockly == null ? void 0 : LocalBlockly.inject) || !(LocalBlockly == null ? void 0 : LocalBlockly.Xml) || !(LocalBlockly == null ? void 0 : LocalBlockly.Events) || !(LocalBlockly == null ? void 0 : LocalBlockly.Themes)) {
2920
+ if (!LocalBlockly?.inject || !LocalBlockly?.Xml || !LocalBlockly?.Events || !LocalBlockly?.Themes) {
2783
2921
  setComponentError("Blockly library not fully loaded.");
2784
2922
  setIsInitializingComponent(false);
2785
2923
  return;
@@ -2811,7 +2949,7 @@ var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
2811
2949
  }
2812
2950
  }
2813
2951
  setIsInitializingComponent(true);
2814
- if ((_a = workspaceRef.current) == null ? void 0 : _a.dispose) {
2952
+ if (workspaceRef.current?.dispose) {
2815
2953
  try {
2816
2954
  workspaceRef.current.dispose();
2817
2955
  } catch (e) {
@@ -2883,13 +3021,12 @@ var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
2883
3021
  showCorrectAnswer,
2884
3022
  userAnswer
2885
3023
  ]);
2886
- React28.useEffect(() => {
3024
+ React9.useEffect(() => {
2887
3025
  if (blocklyReady && blocklyDivRef.current) {
2888
3026
  initializeBlocklyWorkspace();
2889
3027
  }
2890
3028
  return () => {
2891
- var _a;
2892
- if ((_a = workspaceRef.current) == null ? void 0 : _a.dispose) {
3029
+ if (workspaceRef.current?.dispose) {
2893
3030
  try {
2894
3031
  workspaceRef.current.dispose();
2895
3032
  } catch (disposeError) {
@@ -2899,14 +3036,14 @@ var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
2899
3036
  }
2900
3037
  };
2901
3038
  }, [blocklyReady, question.id, initializeBlocklyWorkspace]);
2902
- React28.useEffect(() => {
3039
+ React9.useEffect(() => {
2903
3040
  let resizeTimeout;
2904
3041
  const handleResize = () => {
2905
3042
  clearTimeout(resizeTimeout);
2906
3043
  resizeTimeout = setTimeout(() => {
2907
3044
  if (workspaceRef.current && blocklyReady) {
2908
3045
  const LocalBlockly = window.Blockly;
2909
- if (LocalBlockly == null ? void 0 : LocalBlockly.svgResize) {
3046
+ if (LocalBlockly?.svgResize) {
2910
3047
  LocalBlockly.svgResize(workspaceRef.current);
2911
3048
  }
2912
3049
  }
@@ -2920,64 +3057,91 @@ var BlocklyProgrammingQuestionUI = React28__namespace.default.forwardRef(({
2920
3057
  }, [blocklyReady]);
2921
3058
  const workspaceHeight = showCorrectAnswer ? "300px" : "450px";
2922
3059
  const workspaceContainerId = `blockly-workspace-container-${question.id}`;
2923
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: question.prompt })), question.points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", question.points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0" }, blocklyLoading && /* @__PURE__ */ React28__namespace.default.createElement(
2924
- "div",
2925
- {
2926
- style: {
2927
- height: workspaceHeight,
2928
- width: "100%",
2929
- borderRadius: "0.375rem",
2930
- border: "1px solid hsl(var(--border))",
2931
- backgroundColor: "hsl(var(--background))"
2932
- },
2933
- className: "flex items-center justify-center"
2934
- },
2935
- /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "text-center" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-muted-foreground animate-pulse mb-2" }, "Loading Blockly Environment..."), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "w-8 h-8 border-4 border-muted border-t-primary rounded-full animate-spin mx-auto" }))
2936
- ), blocklyLoadError && !blocklyLoading && /* @__PURE__ */ React28__namespace.default.createElement(
2937
- "div",
2938
- {
2939
- style: {
2940
- height: workspaceHeight,
2941
- width: "100%",
2942
- borderRadius: "0.375rem",
2943
- border: "1px solid hsl(var(--destructive))",
2944
- backgroundColor: "hsl(var(--card))"
2945
- },
2946
- className: "flex items-center justify-center p-4"
2947
- },
2948
- /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "text-destructive text-center" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "font-semibold text-lg" }, "Failed to load Blockly"), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm mt-2 mb-3" }, blocklyLoadError), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "space-x-2" }, /* @__PURE__ */ React28__namespace.default.createElement(
2949
- "button",
2950
- {
2951
- onClick: retry,
2952
- className: "px-4 py-2 bg-primary text-primary-foreground rounded hover:bg-primary/90 transition-colors text-sm"
2953
- },
2954
- "Try Again"
2955
- ), /* @__PURE__ */ React28__namespace.default.createElement(
2956
- "button",
2957
- {
2958
- onClick: () => window.location.reload(),
2959
- className: "px-4 py-2 bg-destructive text-destructive-foreground rounded hover:bg-destructive/90 transition-colors text-sm"
2960
- },
2961
- "Refresh Page"
2962
- )))
2963
- ), !blocklyLoading && !blocklyLoadError && /* @__PURE__ */ React28__namespace.default.createElement(
2964
- "div",
2965
- {
2966
- id: workspaceContainerId,
2967
- ref: blocklyDivRef,
2968
- style: {
2969
- height: workspaceHeight,
2970
- width: "100%",
2971
- borderRadius: "0.375rem",
2972
- border: `1px solid ${componentError ? "hsl(var(--destructive))" : "hsl(var(--border))"}`,
2973
- backgroundColor: "hsl(var(--card))",
2974
- position: "relative",
2975
- userSelect: "none",
2976
- overflow: "hidden"
2977
- },
2978
- "aria-label": `Blockly programming workspace for question: ${question.prompt}`
2979
- }
2980
- ), showCorrectAnswer && question.explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Explanation:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: question.explanation, className: "text-sm text-accent-foreground/80" }))));
3060
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
3061
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
3062
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: question.prompt }) }),
3063
+ question.points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
3064
+ "Points: ",
3065
+ question.points
3066
+ ] })
3067
+ ] }),
3068
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
3069
+ blocklyLoading && /* @__PURE__ */ jsxRuntime.jsx(
3070
+ "div",
3071
+ {
3072
+ style: {
3073
+ height: workspaceHeight,
3074
+ width: "100%",
3075
+ borderRadius: "0.375rem",
3076
+ border: "1px solid hsl(var(--border))",
3077
+ backgroundColor: "hsl(var(--background))"
3078
+ },
3079
+ className: "flex items-center justify-center",
3080
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
3081
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground animate-pulse mb-2", children: "Loading Blockly Environment..." }),
3082
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 border-4 border-muted border-t-primary rounded-full animate-spin mx-auto" })
3083
+ ] })
3084
+ }
3085
+ ),
3086
+ blocklyLoadError && !blocklyLoading && /* @__PURE__ */ jsxRuntime.jsx(
3087
+ "div",
3088
+ {
3089
+ style: {
3090
+ height: workspaceHeight,
3091
+ width: "100%",
3092
+ borderRadius: "0.375rem",
3093
+ border: "1px solid hsl(var(--destructive))",
3094
+ backgroundColor: "hsl(var(--card))"
3095
+ },
3096
+ className: "flex items-center justify-center p-4",
3097
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-destructive text-center", children: [
3098
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-lg", children: "Failed to load Blockly" }),
3099
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-2 mb-3", children: blocklyLoadError }),
3100
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-x-2", children: [
3101
+ /* @__PURE__ */ jsxRuntime.jsx(
3102
+ "button",
3103
+ {
3104
+ onClick: retry,
3105
+ className: "px-4 py-2 bg-primary text-primary-foreground rounded hover:bg-primary/90 transition-colors text-sm",
3106
+ children: "Try Again"
3107
+ }
3108
+ ),
3109
+ /* @__PURE__ */ jsxRuntime.jsx(
3110
+ "button",
3111
+ {
3112
+ onClick: () => window.location.reload(),
3113
+ className: "px-4 py-2 bg-destructive text-destructive-foreground rounded hover:bg-destructive/90 transition-colors text-sm",
3114
+ children: "Refresh Page"
3115
+ }
3116
+ )
3117
+ ] })
3118
+ ] })
3119
+ }
3120
+ ),
3121
+ !blocklyLoading && !blocklyLoadError && /* @__PURE__ */ jsxRuntime.jsx(
3122
+ "div",
3123
+ {
3124
+ id: workspaceContainerId,
3125
+ ref: blocklyDivRef,
3126
+ style: {
3127
+ height: workspaceHeight,
3128
+ width: "100%",
3129
+ borderRadius: "0.375rem",
3130
+ border: `1px solid ${componentError ? "hsl(var(--destructive))" : "hsl(var(--border))"}`,
3131
+ backgroundColor: "hsl(var(--card))",
3132
+ position: "relative",
3133
+ userSelect: "none",
3134
+ overflow: "hidden"
3135
+ },
3136
+ "aria-label": `Blockly programming workspace for question: ${question.prompt}`
3137
+ }
3138
+ ),
3139
+ showCorrectAnswer && question.explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
3140
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
3141
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: question.explanation, className: "text-sm text-accent-foreground/80" })
3142
+ ] })
3143
+ ] })
3144
+ ] });
2981
3145
  });
2982
3146
  BlocklyProgrammingQuestionUI.displayName = "BlocklyProgrammingQuestionUI";
2983
3147
  var SCRATCH_JS_ENGINE_LAYOUT_PATH = "/static/scratch-blocks/js/blockly_compressed_vertical.js";
@@ -3047,18 +3211,17 @@ var loadScript2 = (src) => {
3047
3211
  loadedScriptPromises.set(fullSrc, promise);
3048
3212
  return promise;
3049
3213
  };
3050
- var ScratchProgrammingQuestionUI = React28.forwardRef(({
3214
+ var ScratchProgrammingQuestionUI = React9.forwardRef(({
3051
3215
  question,
3052
3216
  userAnswer,
3053
3217
  showCorrectAnswer = false
3054
3218
  }, ref) => {
3055
- const blocklyDivRef = React28.useRef(null);
3056
- const workspaceRef = React28.useRef(null);
3057
- const [isBlocklyReady, setIsBlocklyReady] = React28.useState(false);
3058
- const [componentError, setComponentError] = React28.useState(null);
3059
- const [isLoadingScripts, setIsLoadingScripts] = React28.useState(true);
3060
- const attemptLoadScripts = React28.useCallback(async () => {
3061
- var _a, _b, _c, _d;
3219
+ const blocklyDivRef = React9.useRef(null);
3220
+ const workspaceRef = React9.useRef(null);
3221
+ const [isBlocklyReady, setIsBlocklyReady] = React9.useState(false);
3222
+ const [componentError, setComponentError] = React9.useState(null);
3223
+ const [isLoadingScripts, setIsLoadingScripts] = React9.useState(true);
3224
+ const attemptLoadScripts = React9.useCallback(async () => {
3062
3225
  setIsLoadingScripts(true);
3063
3226
  setComponentError(null);
3064
3227
  console.log("ScratchUI: Starting script loading sequence...");
@@ -3069,19 +3232,19 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
3069
3232
  if (typeof BlocklyGlobalEngine === "undefined") {
3070
3233
  throw new Error(`Blockly global object (window.Blockly) not found after loading engine script: ${SCRATCH_JS_ENGINE_LAYOUT_PATH}.`);
3071
3234
  }
3072
- console.log(`ScratchUI: After engine/layout load: Blockly defined: ${!!BlocklyGlobalEngine}, Blockly.Blocks defined: ${!!(BlocklyGlobalEngine == null ? void 0 : BlocklyGlobalEngine.Blocks)}, Blockly.Msg defined: ${!!(BlocklyGlobalEngine == null ? void 0 : BlocklyGlobalEngine.Msg)}, Blockly.ScratchMsgs defined: ${!!(BlocklyGlobalEngine == null ? void 0 : BlocklyGlobalEngine.ScratchMsgs)}`);
3235
+ console.log(`ScratchUI: After engine/layout load: Blockly defined: ${!!BlocklyGlobalEngine}, Blockly.Blocks defined: ${!!BlocklyGlobalEngine?.Blocks}, Blockly.Msg defined: ${!!BlocklyGlobalEngine?.Msg}, Blockly.ScratchMsgs defined: ${!!BlocklyGlobalEngine?.ScratchMsgs}`);
3073
3236
  console.log("ScratchUI: Attempting to load Messages:", SCRATCH_JS_MSG_EN_PATH);
3074
3237
  await loadScript2(SCRATCH_JS_MSG_EN_PATH);
3075
3238
  let BlocklyGlobalAfterMsg = window.Blockly;
3076
3239
  if (!BlocklyGlobalAfterMsg) throw new Error("Blockly global disappeared after loading message script.");
3077
- console.log(`ScratchUI: After en.js load: Blockly defined: ${!!BlocklyGlobalAfterMsg}, Blockly.Msg defined: ${!!(BlocklyGlobalAfterMsg == null ? void 0 : BlocklyGlobalAfterMsg.Msg)}, Keys in Blockly.Msg: ${(BlocklyGlobalAfterMsg == null ? void 0 : BlocklyGlobalAfterMsg.Msg) ? Object.keys(BlocklyGlobalAfterMsg.Msg).length : "N/A"}. Sample Blockly.Msg.LOGIC_HUE: ${(_a = BlocklyGlobalAfterMsg == null ? void 0 : BlocklyGlobalAfterMsg.Msg) == null ? void 0 : _a.LOGIC_HUE}`);
3240
+ console.log(`ScratchUI: After en.js load: Blockly defined: ${!!BlocklyGlobalAfterMsg}, Blockly.Msg defined: ${!!BlocklyGlobalAfterMsg?.Msg}, Keys in Blockly.Msg: ${BlocklyGlobalAfterMsg?.Msg ? Object.keys(BlocklyGlobalAfterMsg.Msg).length : "N/A"}. Sample Blockly.Msg.LOGIC_HUE: ${BlocklyGlobalAfterMsg?.Msg?.LOGIC_HUE}`);
3078
3241
  if (!BlocklyGlobalAfterMsg.Msg || Object.keys(BlocklyGlobalAfterMsg.Msg).length === 0) {
3079
3242
  console.warn("ScratchUI: Blockly.Msg appears unpopulated or empty. Checking if Blockly.ScratchMsgs.addLocaleData can be used...");
3080
3243
  if (BlocklyGlobalAfterMsg.ScratchMsgs && typeof BlocklyGlobalAfterMsg.ScratchMsgs.addLocaleData === "function") {
3081
3244
  console.log("ScratchUI: Blockly.ScratchMsgs.addLocaleData is available. Attempting to call Blockly.ScratchMsgs.addLocaleData('en').");
3082
3245
  BlocklyGlobalAfterMsg.ScratchMsgs.addLocaleData("en");
3083
3246
  BlocklyGlobalAfterMsg = window.Blockly;
3084
- console.log(`ScratchUI: After attempting addLocaleData('en'): Keys in Blockly.Msg: ${(BlocklyGlobalAfterMsg == null ? void 0 : BlocklyGlobalAfterMsg.Msg) ? Object.keys(BlocklyGlobalAfterMsg.Msg).length : "N/A"}. Sample Blockly.Msg.LOGIC_HUE: ${(_b = BlocklyGlobalAfterMsg == null ? void 0 : BlocklyGlobalAfterMsg.Msg) == null ? void 0 : _b.LOGIC_HUE}`);
3247
+ console.log(`ScratchUI: After attempting addLocaleData('en'): Keys in Blockly.Msg: ${BlocklyGlobalAfterMsg?.Msg ? Object.keys(BlocklyGlobalAfterMsg.Msg).length : "N/A"}. Sample Blockly.Msg.LOGIC_HUE: ${BlocklyGlobalAfterMsg?.Msg?.LOGIC_HUE}`);
3085
3248
  if (!BlocklyGlobalAfterMsg.Msg || Object.keys(BlocklyGlobalAfterMsg.Msg).length === 0) {
3086
3249
  throw new Error("Blockly.Msg still empty after calling addLocaleData. Message loading failed critically.");
3087
3250
  }
@@ -3095,8 +3258,8 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
3095
3258
  if (!BlocklyGlobalBlocks || !BlocklyGlobalBlocks.Blocks) {
3096
3259
  throw new Error(`Blockly.Blocks not defined after loading ${SCRATCH_JS_BLOCK_DEFINITIONS_PATH}.`);
3097
3260
  }
3098
- console.log(`ScratchUI: After block definitions load: Blockly.Blocks defined: ${!!BlocklyGlobalBlocks.Blocks}. Keys: ${BlocklyGlobalBlocks.Blocks ? Object.keys(BlocklyGlobalBlocks.Blocks).slice(0, 10).join(", ") + "..." : "N/A"}. Essential block motion_movesteps defined: ${!!((_c = BlocklyGlobalBlocks.Blocks) == null ? void 0 : _c.motion_movesteps)}`);
3099
- if (typeof ((_d = BlocklyGlobalBlocks.Blocks) == null ? void 0 : _d.motion_movesteps) === "undefined") {
3261
+ console.log(`ScratchUI: After block definitions load: Blockly.Blocks defined: ${!!BlocklyGlobalBlocks.Blocks}. Keys: ${BlocklyGlobalBlocks.Blocks ? Object.keys(BlocklyGlobalBlocks.Blocks).slice(0, 10).join(", ") + "..." : "N/A"}. Essential block motion_movesteps defined: ${!!BlocklyGlobalBlocks.Blocks?.motion_movesteps}`);
3262
+ if (typeof BlocklyGlobalBlocks.Blocks?.motion_movesteps === "undefined") {
3100
3263
  throw new Error(`Essential Scratch blocks (e.g., motion_movesteps) not found after loading block definitions. Available blocks: ${Object.keys(BlocklyGlobalBlocks.Blocks || {}).join(", ")}`);
3101
3264
  }
3102
3265
  setIsBlocklyReady(true);
@@ -3109,13 +3272,13 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
3109
3272
  setIsLoadingScripts(false);
3110
3273
  }
3111
3274
  }, []);
3112
- React28.useEffect(() => {
3275
+ React9.useEffect(() => {
3113
3276
  attemptLoadScripts();
3114
3277
  }, [attemptLoadScripts]);
3115
- React28.useImperativeHandle(ref, () => ({
3278
+ React9.useImperativeHandle(ref, () => ({
3116
3279
  getWorkspaceXml: () => {
3117
3280
  const LocalBlockly = window.Blockly;
3118
- if (workspaceRef.current && (LocalBlockly == null ? void 0 : LocalBlockly.Xml)) {
3281
+ if (workspaceRef.current && LocalBlockly?.Xml) {
3119
3282
  try {
3120
3283
  const xml = LocalBlockly.Xml.workspaceToDom(workspaceRef.current);
3121
3284
  return LocalBlockly.Xml.domToText(xml);
@@ -3127,8 +3290,7 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
3127
3290
  return null;
3128
3291
  }
3129
3292
  }));
3130
- const initializeWorkspace = React28.useCallback(() => {
3131
- var _a, _b;
3293
+ const initializeWorkspace = React9.useCallback(() => {
3132
3294
  const LocalBlockly = window.Blockly;
3133
3295
  if (!isBlocklyReady || !blocklyDivRef.current || !LocalBlockly) {
3134
3296
  console.warn("ScratchUI: Conditions not met for workspace initialization. isBlocklyReady:", isBlocklyReady, "blocklyDivRef.current:", !!blocklyDivRef.current, "LocalBlockly:", !!LocalBlockly);
@@ -3143,7 +3305,7 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
3143
3305
  console.error("ScratchUI: Blockly.Msg is empty or missing common keys before injection. Current Msg keys count:", Object.keys(LocalBlockly.Msg).length, "CATEGORY_MOTION:", LocalBlockly.Msg.CATEGORY_MOTION);
3144
3306
  return;
3145
3307
  }
3146
- if (typeof ((_a = LocalBlockly.Blocks) == null ? void 0 : _a.motion_movesteps) === "undefined" || typeof ((_b = LocalBlockly.Blocks) == null ? void 0 : _b.event_whenflagclicked) === "undefined") {
3308
+ if (typeof LocalBlockly.Blocks?.motion_movesteps === "undefined" || typeof LocalBlockly.Blocks?.event_whenflagclicked === "undefined") {
3147
3309
  const availableBlocks = Object.keys(LocalBlockly.Blocks || {}).join(", ");
3148
3310
  setComponentError(`ScratchUI: Essential Scratch block definitions (e.g., motion_movesteps, event_whenflagclicked) are missing before injection. Available blocks: ${availableBlocks}`);
3149
3311
  return;
@@ -3231,13 +3393,13 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
3231
3393
  setComponentError(`ScratchUI: Workspace initialization failed: ${e.message || String(e)}. Check console for details. Toolbox used: ${question.toolboxDefinition ? "Custom" : "Default Simplified"}.`);
3232
3394
  }
3233
3395
  }, [question, showCorrectAnswer, userAnswer, isBlocklyReady]);
3234
- React28.useEffect(() => {
3396
+ React9.useEffect(() => {
3235
3397
  if (isBlocklyReady) {
3236
3398
  initializeWorkspace();
3237
3399
  }
3238
3400
  const handleResize = () => {
3239
3401
  const LocalBlocklyResize = window.Blockly;
3240
- if (workspaceRef.current && (LocalBlocklyResize == null ? void 0 : LocalBlocklyResize.svgResize)) {
3402
+ if (workspaceRef.current && LocalBlocklyResize?.svgResize) {
3241
3403
  LocalBlocklyResize.svgResize(workspaceRef.current);
3242
3404
  }
3243
3405
  };
@@ -3258,75 +3420,106 @@ var ScratchProgrammingQuestionUI = React28.forwardRef(({
3258
3420
  }, [isBlocklyReady, initializeWorkspace]);
3259
3421
  const workspaceHeight = showCorrectAnswer ? "300px" : "450px";
3260
3422
  if (isLoadingScripts) {
3261
- return /* @__PURE__ */ React28__namespace.default.createElement("div", { style: { height: workspaceHeight, width: "100%", display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid hsl(var(--border))", borderRadius: "0.375rem", backgroundColor: "hsl(var(--background))" } }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "text-center" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "w-8 h-8 border-4 border-muted border-t-primary rounded-full animate-spin mx-auto mb-2" }), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-muted-foreground" }, "Loading Scratch Assets...")));
3423
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: workspaceHeight, width: "100%", display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid hsl(var(--border))", borderRadius: "0.375rem", backgroundColor: "hsl(var(--background))" }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
3424
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 border-4 border-muted border-t-primary rounded-full animate-spin mx-auto mb-2" }),
3425
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "Loading Scratch Assets..." })
3426
+ ] }) });
3262
3427
  }
3263
3428
  if (componentError) {
3264
- return /* @__PURE__ */ React28__namespace.default.createElement("div", { style: { height: workspaceHeight, width: "100%", color: "hsl(var(--destructive))", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", border: "1px solid hsl(var(--destructive))", borderRadius: "0.375rem", padding: "1rem", backgroundColor: "hsl(var(--card))" } }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "font-semibold text-lg mb-2" }, "Failed to load Scratch Workspace."), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-muted-foreground mb-3 text-center" }, componentError), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xs text-muted-foreground mb-4 text-center" }, "Please ensure all Scratch/Blockly JavaScript files are correctly copied to your", /* @__PURE__ */ React28__namespace.default.createElement("code", null, "public/static/scratch-blocks/js"), " directory. Check browser console for more details.", /* @__PURE__ */ React28__namespace.default.createElement("br", null), /* @__PURE__ */ React28__namespace.default.createElement("strong", null, "CRITICAL: Ensure you have copied the CSS files from ", /* @__PURE__ */ React28__namespace.default.createElement("code", null, "node_modules/scratch-blocks/css/"), " (e.g., ", /* @__PURE__ */ React28__namespace.default.createElement("code", null, "vertical.css"), ") to ", /* @__PURE__ */ React28__namespace.default.createElement("code", null, "public/static/scratch-blocks/css/"), " and linked it in your main layout. Without CSS, blocks will not render correctly.")), /* @__PURE__ */ React28__namespace.default.createElement(Button, { onClick: attemptLoadScripts, variant: "outline" }, "Try Reloading Scripts"));
3429
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { height: workspaceHeight, width: "100%", color: "hsl(var(--destructive))", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", border: "1px solid hsl(var(--destructive))", borderRadius: "0.375rem", padding: "1rem", backgroundColor: "hsl(var(--card))" }, children: [
3430
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-lg mb-2", children: "Failed to load Scratch Workspace." }),
3431
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-3 text-center", children: componentError }),
3432
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mb-4 text-center", children: [
3433
+ "Please ensure all Scratch/Blockly JavaScript files are correctly copied to your",
3434
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "public/static/scratch-blocks/js" }),
3435
+ " directory. Check browser console for more details.",
3436
+ /* @__PURE__ */ jsxRuntime.jsx("br", {}),
3437
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
3438
+ "CRITICAL: Ensure you have copied the CSS files from ",
3439
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "node_modules/scratch-blocks/css/" }),
3440
+ " (e.g., ",
3441
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "vertical.css" }),
3442
+ ") to ",
3443
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: "public/static/scratch-blocks/css/" }),
3444
+ " and linked it in your main layout. Without CSS, blocks will not render correctly."
3445
+ ] })
3446
+ ] }),
3447
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: attemptLoadScripts, variant: "outline", children: "Try Reloading Scripts" })
3448
+ ] });
3265
3449
  }
3266
3450
  if (!isBlocklyReady && !isLoadingScripts) {
3267
- return /* @__PURE__ */ React28__namespace.default.createElement("div", { style: { height: workspaceHeight, width: "100%", display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid hsl(var(--border))", borderRadius: "0.375rem", backgroundColor: "hsl(var(--background))" } }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-muted-foreground" }, "Scratch environment did not initialize (Blockly not ready). Check console for script loading errors."));
3451
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: workspaceHeight, width: "100%", display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid hsl(var(--border))", borderRadius: "0.375rem", backgroundColor: "hsl(var(--background))" }, children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "Scratch environment did not initialize (Blockly not ready). Check console for script loading errors." }) });
3268
3452
  }
3269
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, question.prompt), question.points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", question.points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0" }, /* @__PURE__ */ React28__namespace.default.createElement(
3270
- "div",
3271
- {
3272
- ref: blocklyDivRef,
3273
- style: {
3274
- height: workspaceHeight,
3275
- width: "100%",
3276
- borderRadius: "0.375rem",
3277
- backgroundColor: "hsl(var(--card))",
3278
- position: "relative",
3279
- userSelect: "none",
3280
- overflow: "hidden",
3281
- display: isLoadingScripts || componentError || !isBlocklyReady ? "none" : "block"
3282
- },
3283
- "aria-label": `Scratch programming workspace for question: ${question.prompt}`
3284
- }
3285
- ), showCorrectAnswer && question.explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Explanation:"), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-accent-foreground/80" }, question.explanation))));
3453
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
3454
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
3455
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: question.prompt }),
3456
+ question.points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
3457
+ "Points: ",
3458
+ question.points
3459
+ ] })
3460
+ ] }),
3461
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
3462
+ /* @__PURE__ */ jsxRuntime.jsx(
3463
+ "div",
3464
+ {
3465
+ ref: blocklyDivRef,
3466
+ style: {
3467
+ height: workspaceHeight,
3468
+ width: "100%",
3469
+ borderRadius: "0.375rem",
3470
+ backgroundColor: "hsl(var(--card))",
3471
+ position: "relative",
3472
+ userSelect: "none",
3473
+ overflow: "hidden",
3474
+ display: isLoadingScripts || componentError || !isBlocklyReady ? "none" : "block"
3475
+ },
3476
+ "aria-label": `Scratch programming workspace for question: ${question.prompt}`
3477
+ }
3478
+ ),
3479
+ showCorrectAnswer && question.explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
3480
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
3481
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-accent-foreground/80", children: question.explanation })
3482
+ ] })
3483
+ ] })
3484
+ ] });
3286
3485
  });
3287
3486
  ScratchProgrammingQuestionUI.displayName = "ScratchProgrammingQuestionUI";
3288
3487
  var Tabs = TabsPrimitive__namespace.Root;
3289
- var TabsList = React28__namespace.forwardRef((_a, ref) => {
3290
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
3291
- return /* @__PURE__ */ React28__namespace.createElement(
3292
- TabsPrimitive__namespace.List,
3293
- __spreadValues({
3294
- ref,
3295
- className: cn(
3296
- "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
3297
- className
3298
- )
3299
- }, props)
3300
- );
3301
- });
3488
+ var TabsList = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3489
+ TabsPrimitive__namespace.List,
3490
+ {
3491
+ ref,
3492
+ className: cn(
3493
+ "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
3494
+ className
3495
+ ),
3496
+ ...props
3497
+ }
3498
+ ));
3302
3499
  TabsList.displayName = TabsPrimitive__namespace.List.displayName;
3303
- var TabsTrigger = React28__namespace.forwardRef((_a, ref) => {
3304
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
3305
- return /* @__PURE__ */ React28__namespace.createElement(
3306
- TabsPrimitive__namespace.Trigger,
3307
- __spreadValues({
3308
- ref,
3309
- className: cn(
3310
- "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
3311
- className
3312
- )
3313
- }, props)
3314
- );
3315
- });
3500
+ var TabsTrigger = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3501
+ TabsPrimitive__namespace.Trigger,
3502
+ {
3503
+ ref,
3504
+ className: cn(
3505
+ "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
3506
+ className
3507
+ ),
3508
+ ...props
3509
+ }
3510
+ ));
3316
3511
  TabsTrigger.displayName = TabsPrimitive__namespace.Trigger.displayName;
3317
- var TabsContent = React28__namespace.forwardRef((_a, ref) => {
3318
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
3319
- return /* @__PURE__ */ React28__namespace.createElement(
3320
- TabsPrimitive__namespace.Content,
3321
- __spreadValues({
3322
- ref,
3323
- className: cn(
3324
- "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
3325
- className
3326
- )
3327
- }, props)
3328
- );
3329
- });
3512
+ var TabsContent = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3513
+ TabsPrimitive__namespace.Content,
3514
+ {
3515
+ ref,
3516
+ className: cn(
3517
+ "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
3518
+ className
3519
+ ),
3520
+ ...props
3521
+ }
3522
+ ));
3330
3523
  TabsContent.displayName = TabsPrimitive__namespace.Content.displayName;
3331
3524
  var TOAST_LIMIT = 1;
3332
3525
  var TOAST_REMOVE_DELAY = 1e6;
@@ -3352,15 +3545,17 @@ var addToRemoveQueue = (toastId) => {
3352
3545
  var reducer = (state, action) => {
3353
3546
  switch (action.type) {
3354
3547
  case "ADD_TOAST":
3355
- return __spreadProps(__spreadValues({}, state), {
3548
+ return {
3549
+ ...state,
3356
3550
  toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT)
3357
- });
3551
+ };
3358
3552
  case "UPDATE_TOAST":
3359
- return __spreadProps(__spreadValues({}, state), {
3553
+ return {
3554
+ ...state,
3360
3555
  toasts: state.toasts.map(
3361
- (t) => t.id === action.toast.id ? __spreadValues(__spreadValues({}, t), action.toast) : t
3556
+ (t) => t.id === action.toast.id ? { ...t, ...action.toast } : t
3362
3557
  )
3363
- });
3558
+ };
3364
3559
  case "DISMISS_TOAST": {
3365
3560
  const { toastId } = action;
3366
3561
  if (toastId) {
@@ -3370,23 +3565,27 @@ var reducer = (state, action) => {
3370
3565
  addToRemoveQueue(toast2.id);
3371
3566
  });
3372
3567
  }
3373
- return __spreadProps(__spreadValues({}, state), {
3568
+ return {
3569
+ ...state,
3374
3570
  toasts: state.toasts.map(
3375
- (t) => t.id === toastId || toastId === void 0 ? __spreadProps(__spreadValues({}, t), {
3571
+ (t) => t.id === toastId || toastId === void 0 ? {
3572
+ ...t,
3376
3573
  open: false
3377
- }) : t
3574
+ } : t
3378
3575
  )
3379
- });
3576
+ };
3380
3577
  }
3381
3578
  case "REMOVE_TOAST":
3382
3579
  if (action.toastId === void 0) {
3383
- return __spreadProps(__spreadValues({}, state), {
3580
+ return {
3581
+ ...state,
3384
3582
  toasts: []
3385
- });
3583
+ };
3386
3584
  }
3387
- return __spreadProps(__spreadValues({}, state), {
3585
+ return {
3586
+ ...state,
3388
3587
  toasts: state.toasts.filter((t) => t.id !== action.toastId)
3389
- });
3588
+ };
3390
3589
  }
3391
3590
  };
3392
3591
  var listeners = [];
@@ -3397,23 +3596,23 @@ function dispatch(action) {
3397
3596
  listener(memoryState);
3398
3597
  });
3399
3598
  }
3400
- function toast(_a) {
3401
- var props = __objRest(_a, []);
3599
+ function toast({ ...props }) {
3402
3600
  const id = genId();
3403
3601
  const update = (props2) => dispatch({
3404
3602
  type: "UPDATE_TOAST",
3405
- toast: __spreadProps(__spreadValues({}, props2), { id })
3603
+ toast: { ...props2, id }
3406
3604
  });
3407
3605
  const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
3408
3606
  dispatch({
3409
3607
  type: "ADD_TOAST",
3410
- toast: __spreadProps(__spreadValues({}, props), {
3608
+ toast: {
3609
+ ...props,
3411
3610
  id,
3412
3611
  open: true,
3413
3612
  onOpenChange: (open) => {
3414
3613
  if (!open) dismiss();
3415
3614
  }
3416
- })
3615
+ }
3417
3616
  });
3418
3617
  return {
3419
3618
  id,
@@ -3422,8 +3621,8 @@ function toast(_a) {
3422
3621
  };
3423
3622
  }
3424
3623
  function useToast() {
3425
- const [state, setState] = React28__namespace.useState(memoryState);
3426
- React28__namespace.useEffect(() => {
3624
+ const [state, setState] = React9__namespace.useState(memoryState);
3625
+ React9__namespace.useEffect(() => {
3427
3626
  listeners.push(setState);
3428
3627
  return () => {
3429
3628
  const index = listeners.indexOf(setState);
@@ -3432,13 +3631,12 @@ function useToast() {
3432
3631
  }
3433
3632
  };
3434
3633
  }, [state]);
3435
- return __spreadProps(__spreadValues({}, state), {
3634
+ return {
3635
+ ...state,
3436
3636
  toast,
3437
3637
  dismiss: (toastId) => dispatch({ type: "DISMISS_TOAST", toastId })
3438
- });
3638
+ };
3439
3639
  }
3440
-
3441
- // src/react-ui/components/ui/CodingQuestionUI.tsx
3442
3640
  var languageMap = {
3443
3641
  cpp: langCpp.cpp(),
3444
3642
  javascript: langJavascript.javascript({ typescript: true }),
@@ -3455,15 +3653,15 @@ var CodingQuestionUI = ({
3455
3653
  userAnswer,
3456
3654
  showCorrectAnswer = false
3457
3655
  }) => {
3458
- const [code, setCode] = React28.useState("");
3459
- const [isRunningTests, setIsRunningTests] = React28.useState(false);
3460
- const [testResults, setTestResults] = React28.useState([]);
3656
+ const [code, setCode] = React9.useState("");
3657
+ const [isRunningTests, setIsRunningTests] = React9.useState(false);
3658
+ const [testResults, setTestResults] = React9.useState([]);
3461
3659
  const { toast: toast2 } = useToast();
3462
- React28.useEffect(() => {
3660
+ React9.useEffect(() => {
3463
3661
  const initialCode = typeof userAnswer === "string" ? userAnswer : question.functionSignature || "";
3464
3662
  setCode(initialCode);
3465
3663
  }, [question.id, userAnswer, question.functionSignature]);
3466
- const handleCodeChange = React28.useCallback((value) => {
3664
+ const handleCodeChange = React9.useCallback((value) => {
3467
3665
  setCode(value);
3468
3666
  onAnswerChange(value);
3469
3667
  }, [onAnswerChange]);
@@ -3485,28 +3683,67 @@ var CodingQuestionUI = ({
3485
3683
  }
3486
3684
  };
3487
3685
  const langExtension = languageMap[question.codingLanguage];
3488
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full border-none shadow-none" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, { className: "p-0 pb-4" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl mb-1 font-body" }, /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: question.prompt })), question.points && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-sm text-muted-foreground" }, "Points: ", question.points)), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "p-0 space-y-4" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "font-mono text-sm border rounded-md overflow-hidden" }, /* @__PURE__ */ React28__namespace.default.createElement(
3489
- CodeMirror__default.default,
3490
- {
3491
- value: code,
3492
- height: "300px",
3493
- extensions: [langExtension],
3494
- onChange: handleCodeChange,
3495
- readOnly: showCorrectAnswer,
3496
- theme: "dark"
3497
- }
3498
- )), !showCorrectAnswer && /* @__PURE__ */ React28__namespace.default.createElement(Button, { onClick: handleRunPublicTests, disabled: isRunningTests }, isRunningTests ? /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.Play, { className: "mr-2 h-4 w-4" }), isRunningTests ? "Running..." : "Run Public Tests"), /* @__PURE__ */ React28__namespace.default.createElement(Tabs, { defaultValue: "tests", className: "w-full" }, /* @__PURE__ */ React28__namespace.default.createElement(TabsList, null, /* @__PURE__ */ React28__namespace.default.createElement(TabsTrigger, { value: "tests" }, "Test Cases"), showCorrectAnswer && /* @__PURE__ */ React28__namespace.default.createElement(TabsTrigger, { value: "solution" }, "Solution")), /* @__PURE__ */ React28__namespace.default.createElement(TabsContent, { value: "tests" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "space-y-2 p-2 border rounded-md min-h-[100px]" }, testResults.length > 0 ? testResults.map((result, index) => /* @__PURE__ */ React28__namespace.default.createElement("div", { key: result.testCaseId, className: "flex items-center text-sm" }, result.passed ? /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.CheckCircle, { className: "h-4 w-4 text-green-500 mr-2" }) : /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.XCircle, { className: "h-4 w-4 text-destructive mr-2" }), /* @__PURE__ */ React28__namespace.default.createElement("span", null, "Test Case #", index + 1, ": ", result.passed ? "Passed" : "Failed"), !result.passed && /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xs text-muted-foreground ml-2" }, "- ", result.reasoning))) : /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, showCorrectAnswer ? "Final results are shown in the quiz summary." : "Click 'Run Public Tests' to see results."))), showCorrectAnswer && /* @__PURE__ */ React28__namespace.default.createElement(TabsContent, { value: "solution" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "font-mono text-sm border rounded-md overflow-hidden" }, /* @__PURE__ */ React28__namespace.default.createElement(
3499
- CodeMirror__default.default,
3500
- {
3501
- value: question.solutionCode,
3502
- height: "300px",
3503
- extensions: [langExtension],
3504
- readOnly: true,
3505
- theme: "dark"
3506
- }
3507
- )))), showCorrectAnswer && question.explanation && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm font-semibold text-accent-foreground" }, "Explanation:"), /* @__PURE__ */ React28__namespace.default.createElement(MarkdownRenderer, { content: question.explanation, className: "text-sm text-accent-foreground/80" }))));
3686
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full border-none shadow-none", children: [
3687
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "p-0 pb-4", children: [
3688
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl mb-1 font-body", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: question.prompt }) }),
3689
+ question.points && /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "text-sm text-muted-foreground", children: [
3690
+ "Points: ",
3691
+ question.points
3692
+ ] })
3693
+ ] }),
3694
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0 space-y-4", children: [
3695
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-sm border rounded-md overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
3696
+ CodeMirror__default.default,
3697
+ {
3698
+ value: code,
3699
+ height: "300px",
3700
+ extensions: [langExtension],
3701
+ onChange: handleCodeChange,
3702
+ readOnly: showCorrectAnswer,
3703
+ theme: "dark"
3704
+ }
3705
+ ) }),
3706
+ !showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsxs(Button, { onClick: handleRunPublicTests, disabled: isRunningTests, children: [
3707
+ isRunningTests ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "mr-2 h-4 w-4" }),
3708
+ isRunningTests ? "Running..." : "Run Public Tests"
3709
+ ] }),
3710
+ /* @__PURE__ */ jsxRuntime.jsxs(Tabs, { defaultValue: "tests", className: "w-full", children: [
3711
+ /* @__PURE__ */ jsxRuntime.jsxs(TabsList, { children: [
3712
+ /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { value: "tests", children: "Test Cases" }),
3713
+ showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { value: "solution", children: "Solution" })
3714
+ ] }),
3715
+ /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "tests", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 p-2 border rounded-md min-h-[100px]", children: testResults.length > 0 ? testResults.map((result, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm", children: [
3716
+ result.passed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-4 w-4 text-green-500 mr-2" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-4 w-4 text-destructive mr-2" }),
3717
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3718
+ "Test Case #",
3719
+ index + 1,
3720
+ ": ",
3721
+ result.passed ? "Passed" : "Failed"
3722
+ ] }),
3723
+ !result.passed && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground ml-2", children: [
3724
+ "- ",
3725
+ result.reasoning
3726
+ ] })
3727
+ ] }, result.testCaseId)) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: showCorrectAnswer ? "Final results are shown in the quiz summary." : "Click 'Run Public Tests' to see results." }) }) }),
3728
+ showCorrectAnswer && /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "solution", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-sm border rounded-md overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
3729
+ CodeMirror__default.default,
3730
+ {
3731
+ value: question.solutionCode,
3732
+ height: "300px",
3733
+ extensions: [langExtension],
3734
+ readOnly: true,
3735
+ theme: "dark"
3736
+ }
3737
+ ) }) })
3738
+ ] }),
3739
+ showCorrectAnswer && question.explanation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-accent/20 border border-accent rounded-md", children: [
3740
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-accent-foreground", children: "Explanation:" }),
3741
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer, { content: question.explanation, className: "text-sm text-accent-foreground/80" })
3742
+ ] })
3743
+ ] })
3744
+ ] });
3508
3745
  };
3509
- var QuestionRenderer = React28__namespace.default.forwardRef(({
3746
+ var QuestionRenderer = React9__namespace.default.forwardRef(({
3510
3747
  question,
3511
3748
  onAnswerChange,
3512
3749
  userAnswer,
@@ -3521,131 +3758,125 @@ var QuestionRenderer = React28__namespace.default.forwardRef(({
3521
3758
  };
3522
3759
  switch (question.questionType) {
3523
3760
  case "multiple_choice":
3524
- return /* @__PURE__ */ React28__namespace.default.createElement(MultipleChoiceQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3761
+ return /* @__PURE__ */ jsxRuntime.jsx(MultipleChoiceQuestionUI, { ...commonProps, question });
3525
3762
  case "true_false":
3526
- return /* @__PURE__ */ React28__namespace.default.createElement(TrueFalseQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3763
+ return /* @__PURE__ */ jsxRuntime.jsx(TrueFalseQuestionUI, { ...commonProps, question });
3527
3764
  case "multiple_response":
3528
- return /* @__PURE__ */ React28__namespace.default.createElement(MultipleResponseQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3765
+ return /* @__PURE__ */ jsxRuntime.jsx(MultipleResponseQuestionUI, { ...commonProps, question });
3529
3766
  case "short_answer":
3530
- return /* @__PURE__ */ React28__namespace.default.createElement(ShortAnswerQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3767
+ return /* @__PURE__ */ jsxRuntime.jsx(ShortAnswerQuestionUI, { ...commonProps, question });
3531
3768
  case "numeric":
3532
- return /* @__PURE__ */ React28__namespace.default.createElement(NumericQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3769
+ return /* @__PURE__ */ jsxRuntime.jsx(NumericQuestionUI, { ...commonProps, question });
3533
3770
  case "fill_in_the_blanks":
3534
- return /* @__PURE__ */ React28__namespace.default.createElement(FillInTheBlanksQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3771
+ return /* @__PURE__ */ jsxRuntime.jsx(FillInTheBlanksQuestionUI, { ...commonProps, question });
3535
3772
  case "sequence":
3536
- return /* @__PURE__ */ React28__namespace.default.createElement(SequenceQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3773
+ return /* @__PURE__ */ jsxRuntime.jsx(SequenceQuestionUI, { ...commonProps, question });
3537
3774
  case "matching":
3538
- return /* @__PURE__ */ React28__namespace.default.createElement(MatchingQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3775
+ return /* @__PURE__ */ jsxRuntime.jsx(MatchingQuestionUI, { ...commonProps, question });
3539
3776
  case "drag_and_drop":
3540
- return /* @__PURE__ */ React28__namespace.default.createElement(DragAndDropQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3777
+ return /* @__PURE__ */ jsxRuntime.jsx(DragAndDropQuestionUI, { ...commonProps, question });
3541
3778
  case "hotspot":
3542
- return /* @__PURE__ */ React28__namespace.default.createElement(HotspotQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3779
+ return /* @__PURE__ */ jsxRuntime.jsx(HotspotQuestionUI, { ...commonProps, question });
3543
3780
  case "blockly_programming":
3544
- return /* @__PURE__ */ React28__namespace.default.createElement(BlocklyProgrammingQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question, ref }));
3781
+ return /* @__PURE__ */ jsxRuntime.jsx(BlocklyProgrammingQuestionUI, { ...commonProps, question, ref });
3545
3782
  case "scratch_programming":
3546
- return /* @__PURE__ */ React28__namespace.default.createElement(ScratchProgrammingQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question, ref }));
3783
+ return /* @__PURE__ */ jsxRuntime.jsx(ScratchProgrammingQuestionUI, { ...commonProps, question, ref });
3547
3784
  case "coding":
3548
- return /* @__PURE__ */ React28__namespace.default.createElement(CodingQuestionUI, __spreadProps(__spreadValues({}, commonProps), { question }));
3785
+ return /* @__PURE__ */ jsxRuntime.jsx(CodingQuestionUI, { ...commonProps, question });
3549
3786
  default:
3550
- return /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "p-4 border border-destructive bg-destructive/10 rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "font-semibold text-destructive" }, t("unsupportedQuestionType")), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t("unsupportedQuestionTypeDescription")), /* @__PURE__ */ React28__namespace.default.createElement("pre", { className: "mt-2 p-2 bg-muted rounded text-xs font-code overflow-x-auto" }, JSON.stringify(question, null, 2)));
3787
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 border border-destructive bg-destructive/10 rounded-md", children: [
3788
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-destructive", children: t("unsupportedQuestionType") }),
3789
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: t("unsupportedQuestionTypeDescription") }),
3790
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-2 p-2 bg-muted rounded text-xs font-code overflow-x-auto", children: JSON.stringify(question, null, 2) })
3791
+ ] });
3551
3792
  }
3552
3793
  });
3553
3794
  QuestionRenderer.displayName = "QuestionRenderer";
3554
- var Progress = React28__namespace.forwardRef((_a, ref) => {
3555
- var _b = _a, { className, value } = _b, props = __objRest(_b, ["className", "value"]);
3556
- return /* @__PURE__ */ React28__namespace.createElement(
3557
- ProgressPrimitive__namespace.Root,
3558
- __spreadValues({
3559
- ref,
3560
- className: cn(
3561
- "relative h-4 w-full overflow-hidden rounded-full bg-secondary",
3562
- className
3563
- )
3564
- }, props),
3565
- /* @__PURE__ */ React28__namespace.createElement(
3795
+ var Progress = React9__namespace.forwardRef(({ className, value, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3796
+ ProgressPrimitive__namespace.Root,
3797
+ {
3798
+ ref,
3799
+ className: cn(
3800
+ "relative h-4 w-full overflow-hidden rounded-full bg-secondary",
3801
+ className
3802
+ ),
3803
+ ...props,
3804
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3566
3805
  ProgressPrimitive__namespace.Indicator,
3567
3806
  {
3568
3807
  className: "h-full w-full flex-1 bg-primary transition-all",
3569
3808
  style: { transform: `translateX(-${100 - (value || 0)}%)` }
3570
3809
  }
3571
3810
  )
3572
- );
3573
- });
3811
+ }
3812
+ ));
3574
3813
  Progress.displayName = ProgressPrimitive__namespace.Root.displayName;
3575
3814
  var Accordion = AccordionPrimitive__namespace.Root;
3576
- var AccordionItem = React28__namespace.forwardRef((_a, ref) => {
3577
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
3578
- return /* @__PURE__ */ React28__namespace.createElement(
3579
- AccordionPrimitive__namespace.Item,
3580
- __spreadValues({
3581
- ref,
3582
- className: cn("border-b", className)
3583
- }, props)
3584
- );
3585
- });
3815
+ var AccordionItem = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3816
+ AccordionPrimitive__namespace.Item,
3817
+ {
3818
+ ref,
3819
+ className: cn("border-b", className),
3820
+ ...props
3821
+ }
3822
+ ));
3586
3823
  AccordionItem.displayName = "AccordionItem";
3587
- var AccordionTrigger = React28__namespace.forwardRef((_a, ref) => {
3588
- var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
3589
- return /* @__PURE__ */ React28__namespace.createElement(AccordionPrimitive__namespace.Header, { className: "flex" }, /* @__PURE__ */ React28__namespace.createElement(
3590
- AccordionPrimitive__namespace.Trigger,
3591
- __spreadValues({
3592
- ref,
3593
- className: cn(
3594
- "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
3595
- className
3596
- )
3597
- }, props),
3598
- children,
3599
- /* @__PURE__ */ React28__namespace.createElement(lucideReact.ChevronDown, { className: "h-4 w-4 shrink-0 transition-transform duration-200" })
3600
- ));
3601
- });
3824
+ var AccordionTrigger = React9__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(AccordionPrimitive__namespace.Header, { className: "flex", children: /* @__PURE__ */ jsxRuntime.jsxs(
3825
+ AccordionPrimitive__namespace.Trigger,
3826
+ {
3827
+ ref,
3828
+ className: cn(
3829
+ "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
3830
+ className
3831
+ ),
3832
+ ...props,
3833
+ children: [
3834
+ children,
3835
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 shrink-0 transition-transform duration-200" })
3836
+ ]
3837
+ }
3838
+ ) }));
3602
3839
  AccordionTrigger.displayName = AccordionPrimitive__namespace.Trigger.displayName;
3603
- var AccordionContent = React28__namespace.forwardRef((_a, ref) => {
3604
- var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
3605
- return /* @__PURE__ */ React28__namespace.createElement(
3606
- AccordionPrimitive__namespace.Content,
3607
- __spreadValues({
3608
- ref,
3609
- className: "overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
3610
- }, props),
3611
- /* @__PURE__ */ React28__namespace.createElement("div", { className: cn("pb-4 pt-0", className) }, children)
3612
- );
3613
- });
3840
+ var AccordionContent = React9__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3841
+ AccordionPrimitive__namespace.Content,
3842
+ {
3843
+ ref,
3844
+ className: "overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
3845
+ ...props,
3846
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("pb-4 pt-0", className), children })
3847
+ }
3848
+ ));
3614
3849
  AccordionContent.displayName = AccordionPrimitive__namespace.Content.displayName;
3615
- var ScrollArea = React28__namespace.forwardRef((_a, ref) => {
3616
- var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
3617
- return /* @__PURE__ */ React28__namespace.createElement(
3618
- ScrollAreaPrimitive__namespace.Root,
3619
- __spreadValues({
3620
- ref,
3621
- className: cn("relative overflow-hidden", className)
3622
- }, props),
3623
- /* @__PURE__ */ React28__namespace.createElement(ScrollAreaPrimitive__namespace.Viewport, { className: "h-full w-full rounded-[inherit]" }, children),
3624
- /* @__PURE__ */ React28__namespace.createElement(ScrollBar, null),
3625
- /* @__PURE__ */ React28__namespace.createElement(ScrollAreaPrimitive__namespace.Corner, null)
3626
- );
3627
- });
3850
+ var ScrollArea = React9__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
3851
+ ScrollAreaPrimitive__namespace.Root,
3852
+ {
3853
+ ref,
3854
+ className: cn("relative overflow-hidden", className),
3855
+ ...props,
3856
+ children: [
3857
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.Viewport, { className: "h-full w-full rounded-[inherit]", children }),
3858
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollBar, {}),
3859
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.Corner, {})
3860
+ ]
3861
+ }
3862
+ ));
3628
3863
  ScrollArea.displayName = ScrollAreaPrimitive__namespace.Root.displayName;
3629
- var ScrollBar = React28__namespace.forwardRef((_a, ref) => {
3630
- var _b = _a, { className, orientation = "vertical" } = _b, props = __objRest(_b, ["className", "orientation"]);
3631
- return /* @__PURE__ */ React28__namespace.createElement(
3632
- ScrollAreaPrimitive__namespace.ScrollAreaScrollbar,
3633
- __spreadValues({
3634
- ref,
3635
- orientation,
3636
- className: cn(
3637
- "flex touch-none select-none transition-colors",
3638
- orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
3639
- orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
3640
- className
3641
- )
3642
- }, props),
3643
- /* @__PURE__ */ React28__namespace.createElement(ScrollAreaPrimitive__namespace.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-border" })
3644
- );
3645
- });
3864
+ var ScrollBar = React9__namespace.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3865
+ ScrollAreaPrimitive__namespace.ScrollAreaScrollbar,
3866
+ {
3867
+ ref,
3868
+ orientation,
3869
+ className: cn(
3870
+ "flex touch-none select-none transition-colors",
3871
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
3872
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
3873
+ className
3874
+ ),
3875
+ ...props,
3876
+ children: /* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-border" })
3877
+ }
3878
+ ));
3646
3879
  ScrollBar.displayName = ScrollAreaPrimitive__namespace.ScrollAreaScrollbar.displayName;
3647
-
3648
- // src/react-ui/components/ui/QuizResult.tsx
3649
3880
  var QuizResult = ({
3650
3881
  result,
3651
3882
  quizTitle,
@@ -3654,7 +3885,6 @@ var QuizResult = ({
3654
3885
  showReviewButton = false,
3655
3886
  isReviewLoading = false
3656
3887
  }) => {
3657
- var _a, _b, _c, _d;
3658
3888
  const { t } = reactI18next.useTranslation();
3659
3889
  const getAnswerDisplay = (answer) => {
3660
3890
  if (answer === null || answer === void 0) return t("practiceFlow.results.notAnswered");
@@ -3673,39 +3903,165 @@ var QuizResult = ({
3673
3903
  }
3674
3904
  return String(answer);
3675
3905
  };
3676
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full max-w-3xl mx-auto shadow-xl" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-3xl font-headline text-center" }, t("practiceFlow.results.title", { quizTitle })), /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, { className: "text-center text-lg" }, t("practiceFlow.results.description"))), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "bg-secondary/50" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-xl flex items-center" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.BarChart2, { className: "mr-2 h-5 w-5 text-primary" }), t("practiceFlow.results.overallScore"))), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "grid grid-cols-1 md:grid-cols-3 gap-4 text-center" }, /* @__PURE__ */ React28__namespace.default.createElement("div", null, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-3xl font-bold text-primary" }, result.score, " / ", result.maxScore), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t("practiceFlow.results.points"))), /* @__PURE__ */ React28__namespace.default.createElement("div", null, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-3xl font-bold text-primary" }, result.percentage.toFixed(2), "%"), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t("practiceFlow.results.percentage"))), /* @__PURE__ */ React28__namespace.default.createElement("div", null, result.passed !== void 0 && (result.passed ? /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex flex-col items-center text-green-600" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.CheckCircle, { className: "h-10 w-10" }), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xl font-semibold mt-1" }, t("practiceFlow.results.passed"))) : /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex flex-col items-center text-destructive" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.XCircle, { className: "h-10 w-10" }), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xl font-semibold mt-1" }, t("practiceFlow.results.failed"))))))), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 text-sm" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.Clock, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ React28__namespace.default.createElement("span", null, t("practiceFlow.results.timeSpent")), /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "font-semibold" }, (_b = (_a = result.totalTimeSpentSeconds) == null ? void 0 : _a.toFixed(0)) != null ? _b : "N/A", " ", t("practiceFlow.results.timeUnit"))), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.Percent, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ React28__namespace.default.createElement("span", null, t("practiceFlow.results.avgTimePerQuestion")), /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "font-semibold" }, (_d = (_c = result.averageTimePerQuestionSeconds) == null ? void 0 : _c.toFixed(1)) != null ? _d : "N/A", " ", t("practiceFlow.results.timeUnit")))), result.scormStatus && result.scormStatus !== "idle" && result.scormStatus !== "no_api" && /* @__PURE__ */ React28__namespace.default.createElement(Card, null, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-lg" }, "SCORM Sync Status")), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: `flex items-center ${result.scormStatus === "error" ? "text-destructive" : "text-muted-foreground"}` }, result.scormStatus === "error" && /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.AlertTriangle, { className: "mr-2 h-4 w-4" }), "Status: ", /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "font-semibold ml-1" }, result.scormStatus)), result.scormError && /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xs text-destructive mt-1" }, "Details: ", result.scormError))), result.webhookStatus && result.webhookStatus !== "idle" && /* @__PURE__ */ React28__namespace.default.createElement(Card, null, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-lg" }, "Webhook Sync Status")), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React28__namespace.default.createElement("p", { className: `flex items-center ${result.webhookStatus === "error" ? "text-destructive" : "text-muted-foreground"}` }, result.webhookStatus === "error" && /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.AlertTriangle, { className: "mr-2 h-4 w-4" }), "Status: ", /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "font-semibold ml-1" }, result.webhookStatus)), result.webhookError && /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xs text-destructive mt-1" }, "Details: ", result.webhookError))), /* @__PURE__ */ React28__namespace.default.createElement(Accordion, { type: "single", collapsible: true, className: "w-full" }, /* @__PURE__ */ React28__namespace.default.createElement(AccordionItem, { value: "question-breakdown" }, /* @__PURE__ */ React28__namespace.default.createElement(AccordionTrigger, { className: "text-lg font-semibold" }, t("practiceFlow.results.questionBreakdown")), /* @__PURE__ */ React28__namespace.default.createElement(AccordionContent, null, /* @__PURE__ */ React28__namespace.default.createElement(ScrollArea, { className: "h-[300px] pr-4" }, /* @__PURE__ */ React28__namespace.default.createElement("ul", { className: "space-y-4" }, result.questionResults.map((qResult, index) => {
3677
- var _a2, _b2;
3678
- return /* @__PURE__ */ React28__namespace.default.createElement("li", { key: qResult.questionId, className: "p-4 border rounded-md bg-background" }, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React28__namespace.default.createElement("h4", { className: "font-semibold" }, t("common.questions", { count: index + 1 })), qResult.isCorrect ? /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "text-green-600 font-medium flex items-center" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.CheckCircle, { className: "mr-1 h-4 w-4" }), " ", t("practiceFlow.results.passed")) : /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "text-destructive font-medium flex items-center" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.XCircle, { className: "mr-1 h-4 w-4" }), " ", t("practiceFlow.results.failed"))), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "font-medium" }, t("practiceFlow.results.yourAnswer")), " ", getAnswerDisplay(qResult.userAnswer)), !qResult.isCorrect && /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "font-medium" }, t("practiceFlow.results.correctAnswer")), " ", getAnswerDisplay(qResult.correctAnswer)), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xs text-muted-foreground" }, /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "font-medium" }, t("practiceFlow.results.pointsEarned")), " ", qResult.pointsEarned), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-xs text-muted-foreground" }, /* @__PURE__ */ React28__namespace.default.createElement("span", { className: "font-medium" }, t("practiceFlow.results.timeSpent")), " ", (_b2 = (_a2 = qResult.timeSpentSeconds) == null ? void 0 : _a2.toFixed(0)) != null ? _b2 : "N/A", t("practiceFlow.results.timeUnit")));
3679
- }))))))), /* @__PURE__ */ React28__namespace.default.createElement(CardFooter, { className: "flex flex-col sm:flex-row justify-between gap-2" }, onExitQuiz && /* @__PURE__ */ React28__namespace.default.createElement(Button, { variant: "outline", onClick: onExitQuiz, className: "w-full sm:w-auto" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.LogOut, { className: "mr-2 h-4 w-4" }), t("common.exit")), showReviewButton && onGenerateReview && /* @__PURE__ */ React28__namespace.default.createElement(
3680
- Button,
3681
- {
3682
- onClick: onGenerateReview,
3683
- disabled: isReviewLoading,
3684
- className: "w-full sm:w-auto"
3685
- },
3686
- isReviewLoading ? /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.Wand2, { className: "mr-2 h-4 w-4" }),
3687
- isReviewLoading ? t("practiceFlow.results.generatingReview") : t("practiceFlow.results.generateReview")
3688
- )));
3906
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full max-w-3xl mx-auto shadow-xl", children: [
3907
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { children: [
3908
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-3xl font-headline text-center", children: t("practiceFlow.results.title", { quizTitle }) }),
3909
+ /* @__PURE__ */ jsxRuntime.jsx(CardDescription, { className: "text-center text-lg", children: t("practiceFlow.results.description") })
3910
+ ] }),
3911
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "space-y-6", children: [
3912
+ /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "bg-secondary/50", children: [
3913
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(CardTitle, { className: "text-xl flex items-center", children: [
3914
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BarChart2, { className: "mr-2 h-5 w-5 text-primary" }),
3915
+ t("practiceFlow.results.overallScore")
3916
+ ] }) }),
3917
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "grid grid-cols-1 md:grid-cols-3 gap-4 text-center", children: [
3918
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3919
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-3xl font-bold text-primary", children: [
3920
+ result.score,
3921
+ " / ",
3922
+ result.maxScore
3923
+ ] }),
3924
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: t("practiceFlow.results.points") })
3925
+ ] }),
3926
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3927
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-3xl font-bold text-primary", children: [
3928
+ result.percentage.toFixed(2),
3929
+ "%"
3930
+ ] }),
3931
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: t("practiceFlow.results.percentage") })
3932
+ ] }),
3933
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: result.passed !== void 0 && (result.passed ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-green-600", children: [
3934
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-10 w-10" }),
3935
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xl font-semibold mt-1", children: t("practiceFlow.results.passed") })
3936
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-destructive", children: [
3937
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-10 w-10" }),
3938
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xl font-semibold mt-1", children: t("practiceFlow.results.failed") })
3939
+ ] })) })
3940
+ ] })
3941
+ ] }),
3942
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 text-sm", children: [
3943
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md", children: [
3944
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-5 w-5 text-primary" }),
3945
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("practiceFlow.results.timeSpent") }),
3946
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
3947
+ result.totalTimeSpentSeconds?.toFixed(0) ?? "N/A",
3948
+ " ",
3949
+ t("practiceFlow.results.timeUnit")
3950
+ ] })
3951
+ ] }),
3952
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md", children: [
3953
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Percent, { className: "h-5 w-5 text-primary" }),
3954
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("practiceFlow.results.avgTimePerQuestion") }),
3955
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
3956
+ result.averageTimePerQuestionSeconds?.toFixed(1) ?? "N/A",
3957
+ " ",
3958
+ t("practiceFlow.results.timeUnit")
3959
+ ] })
3960
+ ] })
3961
+ ] }),
3962
+ result.scormStatus && result.scormStatus !== "idle" && result.scormStatus !== "no_api" && /* @__PURE__ */ jsxRuntime.jsxs(Card, { children: [
3963
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-lg", children: "SCORM Sync Status" }) }),
3964
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { children: [
3965
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: `flex items-center ${result.scormStatus === "error" ? "text-destructive" : "text-muted-foreground"}`, children: [
3966
+ result.scormStatus === "error" && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "mr-2 h-4 w-4" }),
3967
+ "Status: ",
3968
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold ml-1", children: result.scormStatus })
3969
+ ] }),
3970
+ result.scormError && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-destructive mt-1", children: [
3971
+ "Details: ",
3972
+ result.scormError
3973
+ ] })
3974
+ ] })
3975
+ ] }),
3976
+ result.webhookStatus && result.webhookStatus !== "idle" && /* @__PURE__ */ jsxRuntime.jsxs(Card, { children: [
3977
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-lg", children: "Webhook Sync Status" }) }),
3978
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { children: [
3979
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: `flex items-center ${result.webhookStatus === "error" ? "text-destructive" : "text-muted-foreground"}`, children: [
3980
+ result.webhookStatus === "error" && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "mr-2 h-4 w-4" }),
3981
+ "Status: ",
3982
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold ml-1", children: result.webhookStatus })
3983
+ ] }),
3984
+ result.webhookError && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-destructive mt-1", children: [
3985
+ "Details: ",
3986
+ result.webhookError
3987
+ ] })
3988
+ ] })
3989
+ ] }),
3990
+ /* @__PURE__ */ jsxRuntime.jsx(Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ jsxRuntime.jsxs(AccordionItem, { value: "question-breakdown", children: [
3991
+ /* @__PURE__ */ jsxRuntime.jsx(AccordionTrigger, { className: "text-lg font-semibold", children: t("practiceFlow.results.questionBreakdown") }),
3992
+ /* @__PURE__ */ jsxRuntime.jsx(AccordionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { className: "h-[300px] pr-4", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-4", children: result.questionResults.map((qResult, index) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "p-4 border rounded-md bg-background", children: [
3993
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-2", children: [
3994
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold", children: t("common.questions", { count: index + 1 }) }),
3995
+ qResult.isCorrect ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-green-600 font-medium flex items-center", children: [
3996
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "mr-1 h-4 w-4" }),
3997
+ " ",
3998
+ t("practiceFlow.results.passed")
3999
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-destructive font-medium flex items-center", children: [
4000
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "mr-1 h-4 w-4" }),
4001
+ " ",
4002
+ t("practiceFlow.results.failed")
4003
+ ] })
4004
+ ] }),
4005
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm", children: [
4006
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: t("practiceFlow.results.yourAnswer") }),
4007
+ " ",
4008
+ getAnswerDisplay(qResult.userAnswer)
4009
+ ] }),
4010
+ !qResult.isCorrect && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm", children: [
4011
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: t("practiceFlow.results.correctAnswer") }),
4012
+ " ",
4013
+ getAnswerDisplay(qResult.correctAnswer)
4014
+ ] }),
4015
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
4016
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: t("practiceFlow.results.pointsEarned") }),
4017
+ " ",
4018
+ qResult.pointsEarned
4019
+ ] }),
4020
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
4021
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: t("practiceFlow.results.timeSpent") }),
4022
+ " ",
4023
+ qResult.timeSpentSeconds?.toFixed(0) ?? "N/A",
4024
+ t("practiceFlow.results.timeUnit")
4025
+ ] })
4026
+ ] }, qResult.questionId)) }) }) })
4027
+ ] }) })
4028
+ ] }),
4029
+ /* @__PURE__ */ jsxRuntime.jsxs(CardFooter, { className: "flex flex-col sm:flex-row justify-between gap-2", children: [
4030
+ onExitQuiz && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", onClick: onExitQuiz, className: "w-full sm:w-auto", children: [
4031
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, { className: "mr-2 h-4 w-4" }),
4032
+ t("common.exit")
4033
+ ] }),
4034
+ showReviewButton && onGenerateReview && /* @__PURE__ */ jsxRuntime.jsxs(
4035
+ Button,
4036
+ {
4037
+ onClick: onGenerateReview,
4038
+ disabled: isReviewLoading,
4039
+ className: "w-full sm:w-auto",
4040
+ children: [
4041
+ isReviewLoading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wand2, { className: "mr-2 h-4 w-4" }),
4042
+ isReviewLoading ? t("practiceFlow.results.generatingReview") : t("practiceFlow.results.generateReview")
4043
+ ]
4044
+ }
4045
+ )
4046
+ ] })
4047
+ ] });
3689
4048
  };
3690
-
3691
- // src/react-ui/components/ui/QuizPlayer.tsx
3692
4049
  var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
3693
- var _a;
3694
- const [engine, setEngine] = React28.useState(null);
3695
- const [currentQuestion, setCurrentQuestion] = React28.useState(null);
3696
- const [currentQuestionNumber, setCurrentQuestionNumber] = React28.useState(0);
3697
- const [totalQuestions, setTotalQuestions] = React28.useState(0);
3698
- const [userAnswer, setUserAnswer] = React28.useState(null);
3699
- const [quizFinished, setQuizFinished] = React28.useState(false);
3700
- const [finalResult, setFinalResult] = React28.useState(null);
3701
- const [timeLeft, setTimeLeft] = React28.useState(null);
3702
- const [isLoading, setIsLoading] = React28.useState(true);
3703
- const [error, setError] = React28.useState(null);
4050
+ const [engine, setEngine] = React9.useState(null);
4051
+ const [currentQuestion, setCurrentQuestion] = React9.useState(null);
4052
+ const [currentQuestionNumber, setCurrentQuestionNumber] = React9.useState(0);
4053
+ const [totalQuestions, setTotalQuestions] = React9.useState(0);
4054
+ const [userAnswer, setUserAnswer] = React9.useState(null);
4055
+ const [quizFinished, setQuizFinished] = React9.useState(false);
4056
+ const [finalResult, setFinalResult] = React9.useState(null);
4057
+ const [timeLeft, setTimeLeft] = React9.useState(null);
4058
+ const [isLoading, setIsLoading] = React9.useState(true);
4059
+ const [error, setError] = React9.useState(null);
3704
4060
  const { t } = reactI18next.useTranslation();
3705
- const programmingQuestionRef = React28.useRef(null);
3706
- const engineRef = React28.useRef(null);
3707
- const isInitializedRef = React28.useRef(false);
3708
- const callbacks = React28.useMemo(() => ({
4061
+ const programmingQuestionRef = React9.useRef(null);
4062
+ const engineRef = React9.useRef(null);
4063
+ const isInitializedRef = React9.useRef(false);
4064
+ const callbacks = React9.useMemo(() => ({
3709
4065
  onQuizStart: (initialData) => {
3710
4066
  setCurrentQuestion(initialData.initialQuestion);
3711
4067
  setCurrentQuestionNumber(initialData.currentQuestionNumber);
@@ -3714,11 +4070,10 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
3714
4070
  setIsLoading(false);
3715
4071
  },
3716
4072
  onQuestionChange: (question, qNum, total) => {
3717
- var _a2;
3718
4073
  setCurrentQuestion(question);
3719
4074
  setCurrentQuestionNumber(qNum);
3720
4075
  setTotalQuestions(total);
3721
- const existingAnswer = (_a2 = engineRef.current) == null ? void 0 : _a2.getUserAnswer((question == null ? void 0 : question.id) || "");
4076
+ const existingAnswer = engineRef.current?.getUserAnswer(question?.id || "");
3722
4077
  setUserAnswer(existingAnswer !== void 0 ? existingAnswer : null);
3723
4078
  },
3724
4079
  onQuizFinish: (results) => {
@@ -3734,19 +4089,19 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
3734
4089
  setError("Time's up! Your quiz has been submitted automatically.");
3735
4090
  }
3736
4091
  }), [onQuizComplete]);
3737
- const handleAnswerChange = React28.useCallback((answer) => {
3738
- if ((currentQuestion == null ? void 0 : currentQuestion.questionType) !== "blockly_programming" && (currentQuestion == null ? void 0 : currentQuestion.questionType) !== "scratch_programming") {
4092
+ const handleAnswerChange = React9.useCallback((answer) => {
4093
+ if (currentQuestion?.questionType !== "blockly_programming" && currentQuestion?.questionType !== "scratch_programming") {
3739
4094
  setUserAnswer(answer);
3740
4095
  }
3741
- }, [currentQuestion == null ? void 0 : currentQuestion.questionType]);
3742
- const quizConfigKey = React28.useMemo(() => {
4096
+ }, [currentQuestion?.questionType]);
4097
+ const quizConfigKey = React9.useMemo(() => {
3743
4098
  return JSON.stringify({
3744
4099
  id: quizConfig.id,
3745
4100
  version: quizConfig.version,
3746
4101
  title: quizConfig.title
3747
4102
  });
3748
4103
  }, [quizConfig.id, quizConfig.version, quizConfig.title]);
3749
- React28.useEffect(() => {
4104
+ React9.useEffect(() => {
3750
4105
  if (isInitializedRef.current) {
3751
4106
  return;
3752
4107
  }
@@ -3782,8 +4137,7 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
3782
4137
  isInitializedRef.current = false;
3783
4138
  };
3784
4139
  }, [quizConfigKey, callbacks, quizConfig]);
3785
- const handleSubmitAnswer = React28.useCallback(() => {
3786
- var _a2;
4140
+ const handleSubmitAnswer = React9.useCallback(() => {
3787
4141
  const currentEngine = engineRef.current;
3788
4142
  if (!currentEngine || !currentQuestion) return;
3789
4143
  let answerToSubmit = null;
@@ -3791,7 +4145,7 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
3791
4145
  if (programmingQuestionRef.current && typeof programmingQuestionRef.current.getWorkspaceXml === "function") {
3792
4146
  answerToSubmit = programmingQuestionRef.current.getWorkspaceXml();
3793
4147
  } else {
3794
- answerToSubmit = (_a2 = currentEngine.getUserAnswer(currentQuestion.id)) != null ? _a2 : null;
4148
+ answerToSubmit = currentEngine.getUserAnswer(currentQuestion.id) ?? null;
3795
4149
  }
3796
4150
  } else {
3797
4151
  answerToSubmit = userAnswer;
@@ -3800,7 +4154,7 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
3800
4154
  currentEngine.submitAnswer(currentQuestion.id, answerToSubmit);
3801
4155
  }
3802
4156
  }, [currentQuestion, userAnswer]);
3803
- const handleNext = React28.useCallback(() => {
4157
+ const handleNext = React9.useCallback(() => {
3804
4158
  const currentEngine = engineRef.current;
3805
4159
  if (!currentEngine) return;
3806
4160
  handleSubmitAnswer();
@@ -3810,77 +4164,128 @@ var QuizPlayer = ({ quizConfig, onQuizComplete, onExitQuiz }) => {
3810
4164
  handleFinishQuiz();
3811
4165
  }
3812
4166
  }, [handleSubmitAnswer]);
3813
- const handlePrevious = React28.useCallback(() => {
4167
+ const handlePrevious = React9.useCallback(() => {
3814
4168
  const currentEngine = engineRef.current;
3815
4169
  if (!currentEngine || currentEngine.getCurrentQuestionNumber() <= 1) return;
3816
4170
  handleSubmitAnswer();
3817
4171
  currentEngine.previousQuestion();
3818
4172
  }, [handleSubmitAnswer]);
3819
- const handleFinishQuiz = React28.useCallback(async () => {
4173
+ const handleFinishQuiz = React9.useCallback(async () => {
3820
4174
  const currentEngine = engineRef.current;
3821
4175
  if (!currentEngine) return;
3822
4176
  setIsLoading(true);
3823
4177
  handleSubmitAnswer();
3824
4178
  await currentEngine.calculateResults();
3825
4179
  }, [handleSubmitAnswer]);
3826
- const progressPercent = React28.useMemo(() => {
4180
+ const progressPercent = React9.useMemo(() => {
3827
4181
  if (totalQuestions === 0) return 0;
3828
4182
  return quizFinished ? 100 : Math.max(0, Math.min(100, (currentQuestionNumber - 1) / totalQuestions * 100));
3829
4183
  }, [currentQuestionNumber, totalQuestions, quizFinished]);
3830
- const formatTime = React28.useCallback((seconds) => {
4184
+ const formatTime = React9.useCallback((seconds) => {
3831
4185
  if (seconds === null) return "-:--";
3832
4186
  const mins = Math.floor(seconds / 60);
3833
4187
  const secs = seconds % 60;
3834
4188
  return `${mins}:${secs < 10 ? "0" : ""}${secs}`;
3835
4189
  }, []);
3836
4190
  if (isLoading) {
3837
- return /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex flex-col items-center justify-center h-64" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.Loader2, { className: "h-12 w-12 animate-spin text-primary" }), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "mt-4 text-muted-foreground" }, t("common.loading")));
4191
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center h-64", children: [
4192
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-12 w-12 animate-spin text-primary" }),
4193
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-muted-foreground", children: t("common.loading") })
4194
+ ] });
3838
4195
  }
3839
4196
  if (error) {
3840
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full max-w-2xl mx-auto shadow-xl" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-destructive flex items-center" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.AlertCircle, { className: "mr-2 h-6 w-6" }), "Quiz Error")), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React28__namespace.default.createElement("p", null, error)), /* @__PURE__ */ React28__namespace.default.createElement(CardFooter, null, onExitQuiz && /* @__PURE__ */ React28__namespace.default.createElement(Button, { variant: "outline", onClick: onExitQuiz }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.LogOut, { className: "mr-2 h-4 w-4" }), " ", t("common.exit"))));
4197
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full max-w-2xl mx-auto shadow-xl", children: [
4198
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(CardTitle, { className: "text-destructive flex items-center", children: [
4199
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "mr-2 h-6 w-6" }),
4200
+ "Quiz Error"
4201
+ ] }) }),
4202
+ /* @__PURE__ */ jsxRuntime.jsx(CardContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: error }) }),
4203
+ /* @__PURE__ */ jsxRuntime.jsx(CardFooter, { children: onExitQuiz && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", onClick: onExitQuiz, children: [
4204
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, { className: "mr-2 h-4 w-4" }),
4205
+ " ",
4206
+ t("common.exit")
4207
+ ] }) })
4208
+ ] });
3841
4209
  }
3842
4210
  if (quizFinished && finalResult) {
3843
- return /* @__PURE__ */ React28__namespace.default.createElement(QuizResult, { result: finalResult, onExitQuiz, quizTitle: quizConfig.title });
4211
+ return /* @__PURE__ */ jsxRuntime.jsx(QuizResult, { result: finalResult, onExitQuiz, quizTitle: quizConfig.title });
3844
4212
  }
3845
4213
  if (!currentQuestion) {
3846
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full max-w-2xl mx-auto shadow-xl" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, null, "Quiz Ended"), /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, null, "No more questions, or quiz not loaded correctly.")), /* @__PURE__ */ React28__namespace.default.createElement(CardFooter, null, onExitQuiz && /* @__PURE__ */ React28__namespace.default.createElement(Button, { variant: "outline", onClick: onExitQuiz }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.LogOut, { className: "mr-2 h-4 w-4" }), " ", t("common.exit"))));
4214
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full max-w-2xl mx-auto shadow-xl", children: [
4215
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { children: [
4216
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { children: "Quiz Ended" }),
4217
+ /* @__PURE__ */ jsxRuntime.jsx(CardDescription, { children: "No more questions, or quiz not loaded correctly." })
4218
+ ] }),
4219
+ /* @__PURE__ */ jsxRuntime.jsx(CardFooter, { children: onExitQuiz && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", onClick: onExitQuiz, children: [
4220
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, { className: "mr-2 h-4 w-4" }),
4221
+ " ",
4222
+ t("common.exit")
4223
+ ] }) })
4224
+ ] });
3847
4225
  }
3848
- return /* @__PURE__ */ React28__namespace.default.createElement(Card, { className: "w-full max-w-3xl mx-auto shadow-xl" }, /* @__PURE__ */ React28__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React28__namespace.default.createElement(CardTitle, { className: "text-2xl font-headline" }, quizConfig.title), timeLeft !== null && /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "flex items-center text-sm text-muted-foreground" }, /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.Clock, { className: "mr-1 h-4 w-4" }), t("practiceFlow.player.timeLeft", { time: formatTime(timeLeft) }))), quizConfig.description && /* @__PURE__ */ React28__namespace.default.createElement(CardDescription, null, quizConfig.description), /* @__PURE__ */ React28__namespace.default.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React28__namespace.default.createElement(
3849
- Progress,
3850
- {
3851
- value: progressPercent,
3852
- "aria-label": `Quiz progress: ${currentQuestionNumber} of ${totalQuestions} questions`,
3853
- className: "w-full"
3854
- }
3855
- ), /* @__PURE__ */ React28__namespace.default.createElement("p", { className: "text-sm text-muted-foreground mt-1 text-right" }, t("practiceFlow.player.questionProgress", { current: currentQuestionNumber, total: totalQuestions })))), /* @__PURE__ */ React28__namespace.default.createElement(CardContent, { className: "min-h-[200px]" }, /* @__PURE__ */ React28__namespace.default.createElement(
3856
- QuestionRenderer,
3857
- {
3858
- question: currentQuestion,
3859
- onAnswerChange: handleAnswerChange,
3860
- userAnswer,
3861
- showCorrectAnswer: ((_a = quizConfig.settings) == null ? void 0 : _a.showCorrectAnswers) === "immediately",
3862
- key: currentQuestion.id,
3863
- ref: currentQuestion.questionType === "blockly_programming" || currentQuestion.questionType === "scratch_programming" ? programmingQuestionRef : null
3864
- }
3865
- )), /* @__PURE__ */ React28__namespace.default.createElement(CardFooter, { className: "flex justify-between items-center" }, /* @__PURE__ */ React28__namespace.default.createElement(
3866
- Button,
3867
- {
3868
- variant: "outline",
3869
- onClick: handlePrevious,
3870
- disabled: currentQuestionNumber <= 1
3871
- },
3872
- /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.ChevronLeft, { className: "mr-2 h-4 w-4" }),
3873
- " ",
3874
- t("common.previous")
3875
- ), onExitQuiz && /* @__PURE__ */ React28__namespace.default.createElement(
3876
- Button,
3877
- {
3878
- variant: "ghost",
3879
- onClick: onExitQuiz,
3880
- className: "text-muted-foreground hover:text-destructive"
3881
- },
3882
- t("common.exit")
3883
- ), /* @__PURE__ */ React28__namespace.default.createElement(Button, { onClick: handleNext }, currentQuestionNumber === totalQuestions ? t("practiceFlow.player.finishQuiz") : t("common.next"), currentQuestionNumber !== totalQuestions && /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.ChevronRight, { className: "ml-2 h-4 w-4" }), currentQuestionNumber === totalQuestions && /* @__PURE__ */ React28__namespace.default.createElement(lucideReact.CheckCircle, { className: "ml-2 h-4 w-4" }))));
4226
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "w-full max-w-3xl mx-auto shadow-xl", children: [
4227
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { children: [
4228
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
4229
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-2xl font-headline", children: quizConfig.title }),
4230
+ timeLeft !== null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm text-muted-foreground", children: [
4231
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "mr-1 h-4 w-4" }),
4232
+ t("practiceFlow.player.timeLeft", { time: formatTime(timeLeft) })
4233
+ ] })
4234
+ ] }),
4235
+ quizConfig.description && /* @__PURE__ */ jsxRuntime.jsx(CardDescription, { children: quizConfig.description }),
4236
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2", children: [
4237
+ /* @__PURE__ */ jsxRuntime.jsx(
4238
+ Progress,
4239
+ {
4240
+ value: progressPercent,
4241
+ "aria-label": `Quiz progress: ${currentQuestionNumber} of ${totalQuestions} questions`,
4242
+ className: "w-full"
4243
+ }
4244
+ ),
4245
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1 text-right", children: t("practiceFlow.player.questionProgress", { current: currentQuestionNumber, total: totalQuestions }) })
4246
+ ] })
4247
+ ] }),
4248
+ /* @__PURE__ */ jsxRuntime.jsx(CardContent, { className: "min-h-[200px]", children: /* @__PURE__ */ jsxRuntime.jsx(
4249
+ QuestionRenderer,
4250
+ {
4251
+ question: currentQuestion,
4252
+ onAnswerChange: handleAnswerChange,
4253
+ userAnswer,
4254
+ showCorrectAnswer: quizConfig.settings?.showCorrectAnswers === "immediately",
4255
+ ref: currentQuestion.questionType === "blockly_programming" || currentQuestion.questionType === "scratch_programming" ? programmingQuestionRef : null
4256
+ },
4257
+ currentQuestion.id
4258
+ ) }),
4259
+ /* @__PURE__ */ jsxRuntime.jsxs(CardFooter, { className: "flex justify-between items-center", children: [
4260
+ /* @__PURE__ */ jsxRuntime.jsxs(
4261
+ Button,
4262
+ {
4263
+ variant: "outline",
4264
+ onClick: handlePrevious,
4265
+ disabled: currentQuestionNumber <= 1,
4266
+ children: [
4267
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "mr-2 h-4 w-4" }),
4268
+ " ",
4269
+ t("common.previous")
4270
+ ]
4271
+ }
4272
+ ),
4273
+ onExitQuiz && /* @__PURE__ */ jsxRuntime.jsx(
4274
+ Button,
4275
+ {
4276
+ variant: "ghost",
4277
+ onClick: onExitQuiz,
4278
+ className: "text-muted-foreground hover:text-destructive",
4279
+ children: t("common.exit")
4280
+ }
4281
+ ),
4282
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { onClick: handleNext, children: [
4283
+ currentQuestionNumber === totalQuestions ? t("practiceFlow.player.finishQuiz") : t("common.next"),
4284
+ currentQuestionNumber !== totalQuestions && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "ml-2 h-4 w-4" }),
4285
+ currentQuestionNumber === totalQuestions && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "ml-2 h-4 w-4" })
4286
+ ] })
4287
+ ] })
4288
+ ] });
3884
4289
  };
3885
4290
 
3886
4291
  // src/player.ts
@@ -3892,7 +4297,7 @@ function mountQuizPlayer(targetElementId, quizConfig) {
3892
4297
  return;
3893
4298
  }
3894
4299
  const AppContainer = () => {
3895
- const [quizResult, setQuizResult] = React28.useState(null);
4300
+ const [quizResult, setQuizResult] = React9.useState(null);
3896
4301
  const handleQuizComplete = (result) => {
3897
4302
  console.log("Quiz Complete (captured inside React AppContainer):", result);
3898
4303
  setQuizResult(result);
@@ -3909,20 +4314,20 @@ function mountQuizPlayer(targetElementId, quizConfig) {
3909
4314
  }
3910
4315
  };
3911
4316
  if (quizResult) {
3912
- return React28__namespace.default.createElement(QuizResult, {
4317
+ return React9__namespace.default.createElement(QuizResult, {
3913
4318
  result: quizResult,
3914
4319
  quizTitle: quizConfig.title,
3915
4320
  onExitQuiz: handleExit
3916
4321
  });
3917
4322
  }
3918
- return React28__namespace.default.createElement(QuizPlayer, {
4323
+ return React9__namespace.default.createElement(QuizPlayer, {
3919
4324
  quizConfig,
3920
4325
  onQuizComplete: handleQuizComplete,
3921
4326
  onExitQuiz: handleExit
3922
4327
  });
3923
4328
  };
3924
4329
  const root = ReactDOM__default.default.createRoot(targetElement);
3925
- root.render(React28__namespace.default.createElement(React28__namespace.default.StrictMode, null, React28__namespace.default.createElement(AppContainer)));
4330
+ root.render(React9__namespace.default.createElement(React9__namespace.default.StrictMode, null, React9__namespace.default.createElement(AppContainer)));
3926
4331
  }
3927
4332
 
3928
4333
  exports.mountQuizPlayer = mountQuizPlayer;