@intellectif/lk-react 19.0.0 → 21.0.0

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.
Files changed (176) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +31 -2
  3. package/dist/{WrittenResponse-DT_Yqbq1.d.cts → WrittenResponse-BVPmrmQL.d.cts} +8 -2
  4. package/dist/{WrittenResponse-CeHY5u3W.d.ts → WrittenResponse-IAJvvL-g.d.ts} +8 -2
  5. package/dist/ai/LkAiProvider.cjs +3 -3
  6. package/dist/ai/LkAiProvider.d.cts +5 -3
  7. package/dist/ai/LkAiProvider.d.ts +5 -3
  8. package/dist/ai/LkAiProvider.js +1 -1
  9. package/dist/ai/useAiHelp.cjs +5 -5
  10. package/dist/ai/useAiHelp.d.cts +8 -2
  11. package/dist/ai/useAiHelp.d.ts +8 -2
  12. package/dist/ai/useAiHelp.js +3 -3
  13. package/dist/{chunk-7NNMDZUU.js → chunk-5MO7ACSB.js} +10 -10
  14. package/dist/{chunk-7NNMDZUU.js.map → chunk-5MO7ACSB.js.map} +1 -1
  15. package/dist/{chunk-7K4B2EMU.js → chunk-5Q2KFIZQ.js} +4 -4
  16. package/dist/{chunk-7K4B2EMU.js.map → chunk-5Q2KFIZQ.js.map} +1 -1
  17. package/dist/{chunk-APZ6JFN6.cjs → chunk-6SNNMVBV.cjs} +5 -5
  18. package/dist/{chunk-APZ6JFN6.cjs.map → chunk-6SNNMVBV.cjs.map} +1 -1
  19. package/dist/{chunk-L7WY5YFQ.cjs → chunk-7JBXDQTD.cjs} +5 -5
  20. package/dist/{chunk-L7WY5YFQ.cjs.map → chunk-7JBXDQTD.cjs.map} +1 -1
  21. package/dist/{chunk-UXV3V5NH.js → chunk-AY3WN4FB.js} +27 -15
  22. package/dist/chunk-AY3WN4FB.js.map +1 -0
  23. package/dist/{chunk-XAIJNGTU.cjs → chunk-CB2A56Q7.cjs} +137 -51
  24. package/dist/chunk-CB2A56Q7.cjs.map +1 -0
  25. package/dist/chunk-CQ37UD4T.cjs +251 -0
  26. package/dist/chunk-CQ37UD4T.cjs.map +1 -0
  27. package/dist/{chunk-YRNEOBXA.js → chunk-EYLBSS4Y.js} +11 -2
  28. package/dist/chunk-EYLBSS4Y.js.map +1 -0
  29. package/dist/{chunk-AHGB5HH6.cjs → chunk-GHKMX5UT.cjs} +11 -2
  30. package/dist/chunk-GHKMX5UT.cjs.map +1 -0
  31. package/dist/chunk-GND3OBVE.js +241 -0
  32. package/dist/chunk-GND3OBVE.js.map +1 -0
  33. package/dist/{chunk-WKA6FUR4.cjs → chunk-GPGCBDBN.cjs} +66 -26
  34. package/dist/chunk-GPGCBDBN.cjs.map +1 -0
  35. package/dist/{chunk-55W2472T.cjs → chunk-GW3YIPDE.cjs} +18 -18
  36. package/dist/{chunk-55W2472T.cjs.map → chunk-GW3YIPDE.cjs.map} +1 -1
  37. package/dist/{chunk-JVWLXVYG.cjs → chunk-J2QIHZ3H.cjs} +2 -2
  38. package/dist/chunk-J2QIHZ3H.cjs.map +1 -0
  39. package/dist/{chunk-SEQTGOOU.js → chunk-KRM6DX47.js} +151 -50
  40. package/dist/chunk-KRM6DX47.js.map +1 -0
  41. package/dist/{chunk-2X2NZF46.cjs → chunk-LHFC2BCM.cjs} +104 -27
  42. package/dist/chunk-LHFC2BCM.cjs.map +1 -0
  43. package/dist/{chunk-G7AGM2Y7.js → chunk-M3K6BF5M.js} +4 -4
  44. package/dist/{chunk-G7AGM2Y7.js.map → chunk-M3K6BF5M.js.map} +1 -1
  45. package/dist/chunk-M4UJBLVR.cjs +55 -0
  46. package/dist/chunk-M4UJBLVR.cjs.map +1 -0
  47. package/dist/{chunk-YNDKAYW3.js → chunk-MCTNLUKO.js} +2 -2
  48. package/dist/{chunk-YNDKAYW3.js.map → chunk-MCTNLUKO.js.map} +1 -1
  49. package/dist/{chunk-ZDYNAWDZ.cjs → chunk-MPHX3Y42.cjs} +2 -2
  50. package/dist/{chunk-ZDYNAWDZ.cjs.map → chunk-MPHX3Y42.cjs.map} +1 -1
  51. package/dist/{chunk-YIYMBCPV.js → chunk-NDIKHMLJ.js} +19 -10
  52. package/dist/chunk-NDIKHMLJ.js.map +1 -0
  53. package/dist/{chunk-LX2X4KC7.cjs → chunk-NILHU555.cjs} +7 -7
  54. package/dist/{chunk-LX2X4KC7.cjs.map → chunk-NILHU555.cjs.map} +1 -1
  55. package/dist/{chunk-ZME5FMED.cjs → chunk-OKEN6NJL.cjs} +157 -54
  56. package/dist/chunk-OKEN6NJL.cjs.map +1 -0
  57. package/dist/chunk-PJMXHDY5.js +51 -0
  58. package/dist/chunk-PJMXHDY5.js.map +1 -0
  59. package/dist/{chunk-OWV7VRMQ.js → chunk-U2BX2RWY.js} +93 -16
  60. package/dist/chunk-U2BX2RWY.js.map +1 -0
  61. package/dist/{chunk-TEAVPUMX.js → chunk-U4KTOH7C.js} +2 -2
  62. package/dist/chunk-U4KTOH7C.js.map +1 -0
  63. package/dist/{chunk-FEOFX4QW.js → chunk-U6SEII5V.js} +3 -3
  64. package/dist/{chunk-FEOFX4QW.js.map → chunk-U6SEII5V.js.map} +1 -1
  65. package/dist/{chunk-XUQ4KRTT.cjs → chunk-W2P5RILE.cjs} +156 -55
  66. package/dist/chunk-W2P5RILE.cjs.map +1 -0
  67. package/dist/{chunk-XHF376ES.js → chunk-W6TWQY6S.js} +152 -49
  68. package/dist/chunk-W6TWQY6S.js.map +1 -0
  69. package/dist/{chunk-NGFTSRKO.cjs → chunk-WGHMWBEJ.cjs} +126 -48
  70. package/dist/chunk-WGHMWBEJ.cjs.map +1 -0
  71. package/dist/{chunk-5QCZZBOX.js → chunk-WNDNQ6NR.js} +120 -42
  72. package/dist/chunk-WNDNQ6NR.js.map +1 -0
  73. package/dist/{chunk-NZM3M5IR.cjs → chunk-WXESKL75.cjs} +47 -35
  74. package/dist/chunk-WXESKL75.cjs.map +1 -0
  75. package/dist/{chunk-BY4UQH2Y.js → chunk-X4YWXZ4J.js} +3 -3
  76. package/dist/{chunk-BY4UQH2Y.js.map → chunk-X4YWXZ4J.js.map} +1 -1
  77. package/dist/{chunk-KOFA54C7.js → chunk-XB2AHEU5.js} +55 -15
  78. package/dist/chunk-XB2AHEU5.js.map +1 -0
  79. package/dist/{chunk-NLTU5JFD.js → chunk-XM5Q2ZCH.js} +132 -46
  80. package/dist/chunk-XM5Q2ZCH.js.map +1 -0
  81. package/dist/{chunk-CQNKZ435.cjs → chunk-XZCMUDFP.cjs} +14 -12
  82. package/dist/chunk-XZCMUDFP.cjs.map +1 -0
  83. package/dist/{chunk-SXU26SFO.cjs → chunk-Y4IDF5UO.cjs} +20 -11
  84. package/dist/chunk-Y4IDF5UO.cjs.map +1 -0
  85. package/dist/{chunk-3FBBP5QR.cjs → chunk-YWJ5XJDU.cjs} +6 -6
  86. package/dist/{chunk-3FBBP5QR.cjs.map → chunk-YWJ5XJDU.cjs.map} +1 -1
  87. package/dist/{chunk-4NERCBOW.js → chunk-ZDDBWUUA.js} +10 -8
  88. package/dist/chunk-ZDDBWUUA.js.map +1 -0
  89. package/dist/components/ActivityPreview.cjs +17 -17
  90. package/dist/components/ActivityPreview.d.cts +2 -2
  91. package/dist/components/ActivityPreview.d.ts +2 -2
  92. package/dist/components/ActivityPreview.js +16 -16
  93. package/dist/components/ActivitySequence.cjs +18 -18
  94. package/dist/components/ActivitySequence.d.cts +34 -4
  95. package/dist/components/ActivitySequence.d.ts +34 -4
  96. package/dist/components/ActivitySequence.js +17 -17
  97. package/dist/components/Dictation.cjs +10 -10
  98. package/dist/components/Dictation.d.cts +1 -1
  99. package/dist/components/Dictation.d.ts +1 -1
  100. package/dist/components/Dictation.js +9 -9
  101. package/dist/components/FillInTheBlanks.cjs +10 -10
  102. package/dist/components/FillInTheBlanks.d.cts +1 -1
  103. package/dist/components/FillInTheBlanks.d.ts +1 -1
  104. package/dist/components/FillInTheBlanks.js +9 -9
  105. package/dist/components/GapSelect.cjs +10 -10
  106. package/dist/components/GapSelect.d.cts +1 -1
  107. package/dist/components/GapSelect.d.ts +1 -1
  108. package/dist/components/GapSelect.js +9 -9
  109. package/dist/components/InteractiveVideo.cjs +17 -17
  110. package/dist/components/InteractiveVideo.d.cts +32 -3
  111. package/dist/components/InteractiveVideo.d.ts +32 -3
  112. package/dist/components/InteractiveVideo.js +15 -15
  113. package/dist/components/MultipleChoice.cjs +10 -10
  114. package/dist/components/MultipleChoice.d.cts +1 -1
  115. package/dist/components/MultipleChoice.d.ts +1 -1
  116. package/dist/components/MultipleChoice.js +9 -9
  117. package/dist/components/PronunciationFeedback.cjs +4 -4
  118. package/dist/components/PronunciationFeedback.d.cts +1 -1
  119. package/dist/components/PronunciationFeedback.d.ts +1 -1
  120. package/dist/components/PronunciationFeedback.js +3 -3
  121. package/dist/components/ReadAloud.cjs +8 -8
  122. package/dist/components/ReadAloud.d.cts +2 -2
  123. package/dist/components/ReadAloud.d.ts +2 -2
  124. package/dist/components/ReadAloud.js +7 -7
  125. package/dist/components/StimulusPanel.cjs +4 -4
  126. package/dist/components/StimulusPanel.d.cts +1 -1
  127. package/dist/components/StimulusPanel.d.ts +1 -1
  128. package/dist/components/StimulusPanel.js +3 -3
  129. package/dist/components/WrittenResponse.cjs +7 -6
  130. package/dist/components/WrittenResponse.d.cts +3 -3
  131. package/dist/components/WrittenResponse.d.ts +3 -3
  132. package/dist/components/WrittenResponse.js +6 -5
  133. package/dist/i18n/LkIntlProvider.cjs +7 -7
  134. package/dist/i18n/LkIntlProvider.d.cts +2 -2
  135. package/dist/i18n/LkIntlProvider.d.ts +2 -2
  136. package/dist/i18n/LkIntlProvider.js +1 -1
  137. package/dist/index.cjs +41 -41
  138. package/dist/index.cjs.map +1 -1
  139. package/dist/index.d.cts +2 -2
  140. package/dist/index.d.ts +2 -2
  141. package/dist/index.js +19 -19
  142. package/dist/index.js.map +1 -1
  143. package/dist/theme/skin.css +55 -0
  144. package/dist/{types-BY-4S5Rd.d.ts → types-7lGlo0zE.d.ts} +56 -1
  145. package/dist/{types-DtPYG1hh.d.cts → types-Botf4Wqj.d.cts} +56 -1
  146. package/package.json +3 -3
  147. package/dist/chunk-2X2NZF46.cjs.map +0 -1
  148. package/dist/chunk-4NERCBOW.js.map +0 -1
  149. package/dist/chunk-5QCZZBOX.js.map +0 -1
  150. package/dist/chunk-AHGB5HH6.cjs.map +0 -1
  151. package/dist/chunk-BRPEB43C.cjs +0 -13
  152. package/dist/chunk-BRPEB43C.cjs.map +0 -1
  153. package/dist/chunk-CQNKZ435.cjs.map +0 -1
  154. package/dist/chunk-JVWLXVYG.cjs.map +0 -1
  155. package/dist/chunk-KGDGYRBL.cjs +0 -117
  156. package/dist/chunk-KGDGYRBL.cjs.map +0 -1
  157. package/dist/chunk-KOFA54C7.js.map +0 -1
  158. package/dist/chunk-NGFTSRKO.cjs.map +0 -1
  159. package/dist/chunk-NLTU5JFD.js.map +0 -1
  160. package/dist/chunk-NZM3M5IR.cjs.map +0 -1
  161. package/dist/chunk-OWV7VRMQ.js.map +0 -1
  162. package/dist/chunk-QRT2W5N5.js +0 -114
  163. package/dist/chunk-QRT2W5N5.js.map +0 -1
  164. package/dist/chunk-RCMYEWBE.js +0 -11
  165. package/dist/chunk-RCMYEWBE.js.map +0 -1
  166. package/dist/chunk-SEQTGOOU.js.map +0 -1
  167. package/dist/chunk-SXU26SFO.cjs.map +0 -1
  168. package/dist/chunk-TEAVPUMX.js.map +0 -1
  169. package/dist/chunk-UXV3V5NH.js.map +0 -1
  170. package/dist/chunk-WKA6FUR4.cjs.map +0 -1
  171. package/dist/chunk-XAIJNGTU.cjs.map +0 -1
  172. package/dist/chunk-XHF376ES.js.map +0 -1
  173. package/dist/chunk-XUQ4KRTT.cjs.map +0 -1
  174. package/dist/chunk-YIYMBCPV.js.map +0 -1
  175. package/dist/chunk-YRNEOBXA.js.map +0 -1
  176. package/dist/chunk-ZME5FMED.cjs.map +0 -1
@@ -1,15 +1,16 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
- var chunkKGDGYRBL_cjs = require('./chunk-KGDGYRBL.cjs');
5
- var chunkZDYNAWDZ_cjs = require('./chunk-ZDYNAWDZ.cjs');
4
+ var chunkCQ37UD4T_cjs = require('./chunk-CQ37UD4T.cjs');
5
+ var chunkMPHX3Y42_cjs = require('./chunk-MPHX3Y42.cjs');
6
6
  var chunkOD65OZIW_cjs = require('./chunk-OD65OZIW.cjs');
7
- var chunkBRPEB43C_cjs = require('./chunk-BRPEB43C.cjs');
8
- var chunkAPZ6JFN6_cjs = require('./chunk-APZ6JFN6.cjs');
7
+ var chunkM4UJBLVR_cjs = require('./chunk-M4UJBLVR.cjs');
8
+ var chunkJ2QIHZ3H_cjs = require('./chunk-J2QIHZ3H.cjs');
9
+ var chunk6SNNMVBV_cjs = require('./chunk-6SNNMVBV.cjs');
9
10
  var chunkEANGKAKG_cjs = require('./chunk-EANGKAKG.cjs');
10
- var chunkL7WY5YFQ_cjs = require('./chunk-L7WY5YFQ.cjs');
11
+ var chunk7JBXDQTD_cjs = require('./chunk-7JBXDQTD.cjs');
11
12
  var chunkXOR4MPEN_cjs = require('./chunk-XOR4MPEN.cjs');
12
- var chunkAHGB5HH6_cjs = require('./chunk-AHGB5HH6.cjs');
13
+ var chunkGHKMX5UT_cjs = require('./chunk-GHKMX5UT.cjs');
13
14
  var lkCore = require('@intellectif/lk-core');
14
15
  var react = require('react');
15
16
  var jsxRuntime = require('react/jsx-runtime');
@@ -312,12 +313,16 @@ function Dictation({
312
313
  theme,
313
314
  locale,
314
315
  disabled,
315
- ai: aiProp
316
+ ai: aiProp,
317
+ delivery,
318
+ scoring
316
319
  }) {
317
320
  const isExam = renderMode === "exam";
318
321
  const isReview = renderMode === "review";
319
- const s = chunkAHGB5HH6_cjs.useLkStrings(strings);
320
- const ai = chunkZDYNAWDZ_cjs.useLearnerAi(aiProp);
322
+ const s = chunkGHKMX5UT_cjs.useLkStrings(strings);
323
+ const ai = chunkMPHX3Y42_cjs.useLearnerAi(aiProp);
324
+ const policy = chunkM4UJBLVR_cjs.useDeliveryPolicy(delivery);
325
+ const scoringPolicy = chunkCQ37UD4T_cjs.useItemScoringPolicy(scoring);
321
326
  const hasKey = typeof data.transcript === "string";
322
327
  const devError = react.useMemo(() => {
323
328
  if (!chunkEANGKAKG_cjs.isDevelopment()) {
@@ -343,12 +348,22 @@ function Dictation({
343
348
  const { state, start, complete, getTimeSpent, reset } = chunkXOR4MPEN_cjs.useActivityState(
344
349
  defaultSubmitted === true ? "completed" : "idle"
345
350
  );
351
+ const tries = chunkCQ37UD4T_cjs.useTries({
352
+ policy: scoringPolicy,
353
+ graded: renderMode === "practice" && policy.feedback,
354
+ submitted: state === "completed",
355
+ disabled: disabled === true
356
+ });
357
+ const resetTries = tries.reset;
346
358
  const isControlled = value !== void 0;
347
359
  const [internalText, setInternalText] = react.useState(() => textOf(defaultValue));
348
360
  const [internalRevealed, setInternalRevealed] = react.useState(() => hintsOf(defaultValue));
349
361
  const [result, setResult] = react.useState(null);
350
362
  const [summary, setSummary] = react.useState(null);
351
363
  const [solutionShown, setSolutionShown] = react.useState(false);
364
+ const formRef = react.useRef(null);
365
+ const feedbackRef = react.useRef(null);
366
+ const focusAfterRef = react.useRef(null);
352
367
  const text = isControlled ? textOf(value) : internalText;
353
368
  const revealed = isControlled ? hintsOf(value) : internalRevealed;
354
369
  const defaultValueRef = react.useRef(defaultValue);
@@ -370,8 +385,9 @@ function Dictation({
370
385
  setResult(null);
371
386
  setSummary(null);
372
387
  setSolutionShown(false);
388
+ resetTries();
373
389
  reset(defaultSubmittedRef.current === true ? "completed" : "idle");
374
- }, [data, reset]);
390
+ }, [data, reset, resetTries]);
375
391
  const textChangedRef = react.useRef(
376
392
  null
377
393
  );
@@ -396,6 +412,18 @@ function Dictation({
396
412
  () => slowMediaFor(data, s.dictationSlowRecording),
397
413
  [data, s.dictationSlowRecording]
398
414
  );
415
+ react.useEffect(() => {
416
+ const target = focusAfterRef.current;
417
+ if (target === null) {
418
+ return;
419
+ }
420
+ focusAfterRef.current = null;
421
+ if (target === "answer") {
422
+ formRef.current?.querySelector("textarea")?.focus();
423
+ } else {
424
+ feedbackRef.current?.focus();
425
+ }
426
+ });
399
427
  if (devError) {
400
428
  throw devError;
401
429
  }
@@ -407,12 +435,14 @@ function Dictation({
407
435
  const submitted = state === "completed";
408
436
  const inactive = disabled === true || submitted || isReview;
409
437
  const revealing = isReview ? outcome?.status === "scored" : !isExam && submitted;
438
+ const marking = revealing && policy.feedback;
439
+ const solutionsShown = policy.solutions && !tries.open;
410
440
  const transcript = isExam || !hasKey ? void 0 : data.transcript;
411
441
  const hintWords = react.useMemo(
412
442
  () => transcript === void 0 ? [] : hintWordsOf(transcript),
413
443
  [transcript]
414
444
  );
415
- const hintsOffered = data.hints?.mode === "progressive-words" && !isExam && !isReview && transcript !== void 0;
445
+ const hintsOffered = policy.hints && data.hints?.mode === "progressive-words" && !isExam && !isReview && transcript !== void 0;
416
446
  const hintsEnabled = hintsOffered && !submitted;
417
447
  const hintTotal = hintWords.length;
418
448
  const hintsShown = Math.min(revealed, hintTotal);
@@ -475,7 +505,7 @@ function Dictation({
475
505
  fireInteraction("hint-requested", { revealed: next, total: hintTotal });
476
506
  };
477
507
  const resetHints = () => {
478
- if (inactive || hintsShown === 0) {
508
+ if (inactive || hintsShown === 0 || scoringPolicy.hintPenalty > 0) {
479
509
  return;
480
510
  }
481
511
  revealButtonRef.current?.focus();
@@ -495,9 +525,18 @@ function Dictation({
495
525
  fireInteraction("submitted", { length: text.length, hintsRevealed: reportedHints });
496
526
  return;
497
527
  }
498
- const scoringResult = lkCore.score("dictation", data, response);
528
+ const answer = lkCore.score("dictation", data, response);
499
529
  complete();
500
530
  const timeSpent = getTimeSpent();
531
+ const counted = tries.record({
532
+ score: answer.score,
533
+ maxScore: answer.maxScore,
534
+ hintsRevealed: reportedHints
535
+ });
536
+ const mine = counted.tries[counted.tries.length - 1];
537
+ const costed = mine === void 0 ? answer.score : mine.scored;
538
+ const scoringResult = costed === answer.score ? answer : { ...answer, score: costed, passed: lkCore.computePassThreshold(data, costed) };
539
+ const passed2 = counted.score === scoringResult.score ? scoringResult.passed : lkCore.computePassThreshold(data, counted.score);
501
540
  const xapiStatement = lkCore.xAPIBuilder.buildAnsweredStatement({
502
541
  actor: chunkEANGKAKG_cjs.ANONYMOUS_ACTOR,
503
542
  object: {
@@ -509,23 +548,23 @@ function Dictation({
509
548
  timeSpentMs: timeSpent,
510
549
  response: response.text
511
550
  });
512
- setResult(scoringResult);
513
- onComplete?.({
514
- score: scoringResult.score,
515
- maxScore: scoringResult.maxScore,
516
- passed: scoringResult.passed,
517
- timeSpent,
518
- xapiStatement
519
- });
520
- const correctWords = scoringResult.details.filter(
551
+ setResult(answer);
552
+ onComplete?.(
553
+ chunkJ2QIHZ3H_cjs.stampTake(
554
+ { score: counted.score, maxScore: counted.maxScore, passed: passed2, timeSpent, xapiStatement },
555
+ chunkJ2QIHZ3H_cjs.mintTake()
556
+ )
557
+ );
558
+ const correctWords = answer.details.filter(
521
559
  (detail) => (detail.outcome ?? (detail.correct ? "correct" : "incorrect")) === "correct"
522
560
  ).length;
561
+ setSolutionShown(false);
523
562
  setSummary({
524
563
  text: `${s.answerSubmitted} ${s.scoreAnnouncement(
525
- Math.round(scoringResult.score * 100),
526
- scoringResult.passed
527
- )} ${s.dictationWordsSummary(correctWords, scoringResult.details.length)}`,
528
- feedback: scoringResult.feedback
564
+ Math.round(counted.score * 100),
565
+ passed2
566
+ )} ${s.dictationWordsSummary(correctWords, answer.details.length)}`,
567
+ feedback: answer.feedback
529
568
  });
530
569
  fireInteraction("submitted", {
531
570
  length: text.length,
@@ -533,24 +572,33 @@ function Dictation({
533
572
  score: scoringResult.score
534
573
  });
535
574
  };
575
+ const retry = () => {
576
+ reset("idle");
577
+ setSummary(null);
578
+ focusAfterRef.current = "answer";
579
+ };
580
+ const closeTries = () => {
581
+ tries.close();
582
+ focusAfterRef.current = "feedback";
583
+ };
536
584
  const given = isControlled ? value : defaultValue;
537
585
  const hasResponse = given !== void 0 && given.type === "dictation";
538
586
  const contentLang = typeof data.locale === "string" ? data.locale.replaceAll("_", "-") : void 0;
539
587
  const localeDir = chunkOD65OZIW_cjs.localeDirectionOf(data.locale);
540
588
  const alignment = react.useMemo(
541
- () => revealing && transcript !== void 0 && (!isReview || hasResponse) ? lkCore.alignDictation(data, text) : null,
542
- [revealing, transcript, isReview, hasResponse, data, text]
589
+ () => marking && transcript !== void 0 && (!isReview || hasResponse) ? lkCore.alignDictation(data, text) : null,
590
+ [marking, transcript, isReview, hasResponse, data, text]
543
591
  );
544
592
  const sentenceDiff = react.useMemo(
545
- () => alignment !== null && alignment.attempt !== "" ? lkCore.diffDictationChars(alignment.reference, alignment.attempt) : null,
546
- [alignment]
593
+ () => alignment !== null && alignment.attempt !== "" && solutionsShown ? lkCore.diffDictationChars(alignment.reference, alignment.attempt) : null,
594
+ [alignment, solutionsShown]
547
595
  );
548
596
  const detailWords = react.useMemo(
549
597
  () => isReview && alignment === null && outcome?.status === "scored" ? wordsFromDetails(outcome.details) : null,
550
598
  [isReview, alignment, outcome]
551
599
  );
552
600
  const words = alignment?.words ?? detailWords;
553
- const marksShown = revealing && words !== null;
601
+ const marksShown = marking && words !== null;
554
602
  const contentDir = localeDir ?? chunkOD65OZIW_cjs.textDirectionOf(transcript ?? words?.map((word) => word.reference).join(" ") ?? "");
555
603
  const nothingTyped = alignment !== null ? alignment.attempt === "" : words?.every((word) => word.attempt === "") ?? false;
556
604
  const passed = isReview ? outcome?.status === "scored" || outcome?.status === "graded" ? outcome.passed : void 0 : result?.passed;
@@ -581,17 +629,19 @@ function Dictation({
581
629
  case "correct":
582
630
  return s.dictationWordCorrect(word.attempt);
583
631
  case "incorrect":
584
- return s.dictationWordWrong(word.attempt, word.reference);
632
+ return solutionsShown ? s.dictationWordWrong(word.attempt, word.reference) : s.dictationWordWrongUnnamed(word.attempt);
585
633
  case "missing":
586
- return s.dictationWordMissing(word.reference);
634
+ return solutionsShown ? s.dictationWordMissing(word.reference) : s.dictationWordMissingUnnamed;
587
635
  default:
588
636
  return s.dictationWordExtra(word.attempt);
589
637
  }
590
638
  };
591
639
  const acceptedTranscripts = transcript === void 0 || !Array.isArray(data.acceptedTranscripts) ? void 0 : data.acceptedTranscripts.filter((entry) => typeof entry === "string");
640
+ const extra = !isReview && policy.feedback && submitted && tries.counted !== null ? chunkCQ37UD4T_cjs.triesSummary(s, scoringPolicy, tries.counted, tries.open) : "";
592
641
  return /* @__PURE__ */ jsxRuntime.jsxs(
593
642
  "form",
594
643
  {
644
+ ref: formRef,
595
645
  className: "lk-dc",
596
646
  "aria-labelledby": VISIBLE_TEXT_RE.test(data.title) ? titleId : void 0,
597
647
  lang: locale,
@@ -604,7 +654,7 @@ function Dictation({
604
654
  data.media !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "lk-dc-recording", children: [
605
655
  /* @__PURE__ */ jsxRuntime.jsx("legend", { className: "lk-dc-recording-label", children: s.dictationRecording }),
606
656
  /* @__PURE__ */ jsxRuntime.jsx(
607
- chunkL7WY5YFQ_cjs.ActivityMedia,
657
+ chunk7JBXDQTD_cjs.ActivityMedia,
608
658
  {
609
659
  media: data.media,
610
660
  renderMode,
@@ -621,7 +671,7 @@ function Dictation({
621
671
  slowMedia !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "lk-dc-recording", "data-slow": "true", children: [
622
672
  /* @__PURE__ */ jsxRuntime.jsx("legend", { className: "lk-dc-recording-label", children: s.dictationSlowRecording }),
623
673
  /* @__PURE__ */ jsxRuntime.jsx(
624
- chunkL7WY5YFQ_cjs.ActivityMedia,
674
+ chunk7JBXDQTD_cjs.ActivityMedia,
625
675
  {
626
676
  media: slowMedia,
627
677
  renderMode,
@@ -647,7 +697,8 @@ function Dictation({
647
697
  children: s.dictationRevealNextWord(hintsShown, hintTotal)
648
698
  }
649
699
  ),
650
- hintsShown > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
700
+ /* @__PURE__ */ jsxRuntime.jsx(chunkCQ37UD4T_cjs.HintCost, { policy: scoringPolicy, strings: s }),
701
+ hintsShown > 0 && scoringPolicy.hintPenalty <= 0 ? /* @__PURE__ */ jsxRuntime.jsx(
651
702
  "button",
652
703
  {
653
704
  type: "button",
@@ -676,7 +727,7 @@ function Dictation({
676
727
  value: text,
677
728
  disabled: inactive,
678
729
  "aria-disabled": inactive || void 0,
679
- "data-correct": revealing && passed !== void 0 && (!isReview || hasResponse) ? String(passed) : void 0,
730
+ "data-correct": marking && passed !== void 0 && (!isReview || hasResponse) ? String(passed) : void 0,
680
731
  onChange: (event) => handleTextChange(event.target.value)
681
732
  }
682
733
  ),
@@ -704,14 +755,18 @@ function Dictation({
704
755
  "aria-hidden": "true",
705
756
  dir: contentDir ?? "auto",
706
757
  lang: contentLang,
707
- ...word.status === "incorrect" && alignment !== null ? { "data-marks": "characters" } : {},
708
- children: word.status === "incorrect" && alignment !== null ? /* @__PURE__ */ jsxRuntime.jsx(
758
+ ...word.status === "incorrect" && alignment !== null && solutionsShown ? { "data-marks": "characters" } : {},
759
+ children: word.status === "incorrect" && alignment !== null && solutionsShown ? /* @__PURE__ */ jsxRuntime.jsx(
709
760
  CharOps,
710
761
  {
711
762
  ops: lkCore.diffDictationChars(word.reference, word.attempt),
712
763
  contentDir
713
764
  }
714
- ) : word.status === "missing" ? word.reference : word.attempt
765
+ ) : word.status === "missing" ? (
766
+ // Where the answer may not show, a gap the size of
767
+ // nothing in particular: the word is missing, not named.
768
+ solutionsShown ? word.reference : "\u2026"
769
+ ) : word.attempt
715
770
  }
716
771
  )
717
772
  ]
@@ -728,7 +783,7 @@ function Dictation({
728
783
  ] }),
729
784
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-dc-diff-note", children: s.dictationDiffNote })
730
785
  ] }),
731
- transcript !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
786
+ transcript !== void 0 && solutionsShown ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
732
787
  /* @__PURE__ */ jsxRuntime.jsx(
733
788
  "button",
734
789
  {
@@ -755,9 +810,31 @@ function Dictation({
755
810
  ) : null
756
811
  ] }) : null
757
812
  ] }) : null,
758
- /* @__PURE__ */ jsxRuntime.jsx(chunkBRPEB43C_cjs.FeedbackRegion, { id: `${data.id}-feedback`, children: /* @__PURE__ */ jsxRuntime.jsx(AnnouncementText, { announcement: isReview ? reviewSummary : summary }) }),
759
813
  /* @__PURE__ */ jsxRuntime.jsx(
760
- chunkKGDGYRBL_cjs.AiExplanation,
814
+ chunkM4UJBLVR_cjs.FeedbackRegion,
815
+ {
816
+ id: `${data.id}-feedback`,
817
+ ...scoringPolicy.retries > 0 ? { ref: feedbackRef } : {},
818
+ children: /* @__PURE__ */ jsxRuntime.jsx(
819
+ AnnouncementText,
820
+ {
821
+ announcement: isReview ? policy.feedback || !chunkM4UJBLVR_cjs.outcomeShowsMarks(outcome) ? reviewSummary : null : policy.feedback || summary === null ? summary === null || extra === "" ? summary : { ...summary, text: `${summary.text} ${extra}` } : { text: s.answerSubmitted, feedback: null }
822
+ }
823
+ )
824
+ }
825
+ ),
826
+ /* @__PURE__ */ jsxRuntime.jsx(
827
+ chunkCQ37UD4T_cjs.TryActions,
828
+ {
829
+ tries,
830
+ solutions: policy.solutions && transcript !== void 0,
831
+ strings: s,
832
+ onRetry: retry,
833
+ onClose: closeTries
834
+ }
835
+ ),
836
+ /* @__PURE__ */ jsxRuntime.jsx(
837
+ chunkCQ37UD4T_cjs.AiExplanation,
761
838
  {
762
839
  ai,
763
840
  data,
@@ -767,7 +844,8 @@ function Dictation({
767
844
  outcome,
768
845
  locale,
769
846
  onInteraction,
770
- strings: s
847
+ strings: s,
848
+ delivery: tries.open ? { ...policy, solutions: false } : policy
771
849
  }
772
850
  )
773
851
  ]
@@ -775,10 +853,10 @@ function Dictation({
775
853
  );
776
854
  }
777
855
  function Dictation2(props) {
778
- const strings = chunkAHGB5HH6_cjs.useLkStrings(props.strings);
779
- return /* @__PURE__ */ jsxRuntime.jsx(chunkAPZ6JFN6_cjs.ActivityErrorBoundary, { activityTitle: props.data?.title, strings, children: /* @__PURE__ */ jsxRuntime.jsx(Dictation, { ...props }) });
856
+ const strings = chunkGHKMX5UT_cjs.useLkStrings(props.strings);
857
+ return /* @__PURE__ */ jsxRuntime.jsx(chunk6SNNMVBV_cjs.ActivityErrorBoundary, { activityTitle: props.data?.title, strings, children: /* @__PURE__ */ jsxRuntime.jsx(Dictation, { ...props }) });
780
858
  }
781
859
 
782
860
  exports.Dictation = Dictation2;
783
- //# sourceMappingURL=chunk-NGFTSRKO.cjs.map
784
- //# sourceMappingURL=chunk-NGFTSRKO.cjs.map
861
+ //# sourceMappingURL=chunk-WGHMWBEJ.cjs.map
862
+ //# sourceMappingURL=chunk-WGHMWBEJ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Dictation/Dictation.tsx","../src/components/Dictation/index.tsx"],"names":["characterDirectionOf","jsx","jsxs","Fragment","useLkStrings","useLearnerAi","useDeliveryPolicy","useItemScoringPolicy","useMemo","isDevelopment","assertRedacted","result","validateActivity","ActivitySchemaError","useActivityState","useTries","useState","useRef","useEffect","useId","score","computePassThreshold","passed","xAPIBuilder","ANONYMOUS_ACTOR","objectIdFor","xapiDefinitionFor","stampTake","mintTake","localeDirectionOf","alignDictation","diffDictationChars","textDirectionOf","triesSummary","ActivityMedia","HintCost","DICTATION_MAX_TEXT_LENGTH","FeedbackRegion","outcomeShowsMarks","TryActions","AiExplanation","Dictation","ActivityErrorBoundary"],"mappings":"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","file":"chunk-WGHMWBEJ.cjs","sourcesContent":["'use client';\n\nimport {\n type ActivityMedia as ActivityMediaData,\n ActivitySchemaError,\n alignDictation,\n assertRedacted,\n computePassThreshold,\n DICTATION_MAX_TEXT_LENGTH,\n type DictationAlignment,\n type DictationCharOp,\n type DictationData,\n type DictationLearnerResponse,\n type DictationWordAlignment,\n diffDictationChars,\n type LearnerResponse,\n type MediaPlaybackPolicy,\n type ScoringDetail,\n type ScoringResult,\n score,\n validateActivity,\n xAPIBuilder,\n xapiDefinitionFor,\n} from '@intellectif/lk-core';\nimport { type CSSProperties, useEffect, useId, useMemo, useRef, useState } from 'react';\nimport { useLearnerAi } from '../../ai/LkAiProvider.js';\nimport { useActivityState } from '../../hooks/useActivityState.js';\nimport { characterDirectionOf, localeDirectionOf, textDirectionOf } from '../../i18n/direction.js';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ANONYMOUS_ACTOR, isDevelopment, objectIdFor } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { AiExplanation } from '../shared/AiHelp.js';\nimport { outcomeShowsMarks, useDeliveryPolicy } from '../shared/delivery.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport { mintTake, stampTake } from '../shared/sequence-slot.js';\nimport {\n HintCost,\n TryActions,\n triesSummary,\n useItemScoringPolicy,\n useTries,\n} from '../shared/tries.js';\nimport type { ActivityProps, Renderable } from '../types.js';\n\n// Nothing shuffles and nothing is seeded, so the shared contract is the whole\n// contract. Kept as an interface so a later prop lands here, not in a union.\nexport interface DictationProps extends ActivityProps<DictationData> {}\n\n/** How long the learner has to stop typing before one `text-changed` is emitted. */\nconst TEXT_CHANGED_DEBOUNCE_MS = 500;\n\n/**\n * Present for assistive technology, invisible on screen. Inline rather than\n * left to the optional skin: the per-word sentences are the marks' only\n * channel for a screen reader, and without this a page that does not load the\n * skin would show every one of them beside its word.\n */\nconst VISUALLY_HIDDEN: CSSProperties = {\n position: 'absolute',\n width: 1,\n height: 1,\n margin: -1,\n padding: 0,\n overflow: 'hidden',\n clip: 'rect(0 0 0 0)',\n clipPath: 'inset(50%)',\n whiteSpace: 'nowrap',\n border: 0,\n};\n\n/**\n * Names playback groups. Per mount and never derived from `useId`: two\n * separately hydrated roots derive the same id for the same tree position, and\n * their recordings would pause each other. The name never reaches the DOM.\n */\nlet recordingsGroupCount = 0;\n\n/**\n * A token with nothing spelled in it, as the scorer reads it once composed:\n * only punctuation, control, format and default-ignorable characters, the acute\n * accent, grave accent (and its fullwidth form) and modifier apostrophe the\n * scorer folds into an apostrophe, and the Arabic tatweel and N'Ko lajanyalan\n * it removes.\n */\nconst UNSPELLED_RE = new RegExp(\n `^[\\\\p{P}\\\\p{Cc}\\\\p{Cf}\\\\p{Default_Ignorable_Code_Point}${String.fromCodePoint(0xb4, 0x60, 0xff40, 0x2bc, 0x640, 0x7fa)}]*$`,\n 'u',\n);\n/**\n * Spacing as the scorer reads it: whitespace and NEXT LINE — but not the\n * byte-order mark, which JavaScript counts as whitespace and the scorer\n * removes as a format character, joining the letters on either side.\n */\nconst HINT_SPACE_RE = new RegExp(\n `(?:(?!${String.fromCodePoint(0xfeff)})\\\\s|${String.fromCodePoint(0x85)})+`,\n 'u',\n);\n/** A title with something in it a screen reader can say. */\nconst VISIBLE_TEXT_RE = /[^\\p{White_Space}\\p{Cc}\\p{Cf}\\p{Default_Ignorable_Code_Point}]/u;\n\nconst BULLET = '•';\nconst LEFT_TO_RIGHT_MARK = String.fromCodePoint(0x200e);\nconst RIGHT_TO_LEFT_MARK = String.fromCodePoint(0x200f);\n/**\n * Code points a font draws onto the character before them rather than beside\n * it — combining marks (variation selectors among them), the joiners, emoji\n * skin tones, tag characters, the vowel and final jamo of an old Hangul\n * syllable, and the Thai and Lao SARA AM, whose ring the font sets on the\n * consonant before — so their own span can have no width to decorate.\n */\nconst EXTENDING_RE = new RegExp(\n `^[\\\\p{M}${String.fromCodePoint(0x200c, 0x200d, 0xe33, 0xeb3)}${String.fromCodePoint(0x1f3fb)}-${String.fromCodePoint(0x1f3ff)}${String.fromCodePoint(0xe0020)}-${String.fromCodePoint(0xe007f)}${String.fromCodePoint(0x1160)}-${String.fromCodePoint(0x11ff)}${String.fromCodePoint(0xd7b0)}-${String.fromCodePoint(0xd7ff)}]$`,\n 'u',\n);\n/**\n * Viramas that stack the letter after them into one glyph without asking —\n * Devanagari, Bengali, Gujarati, Oriya, Telugu, Kannada and Malayalam, the\n * Burmese stacker, the Khmer coeng, and the Tai Tham, Balinese, Javanese,\n * Chakma and Brahmi signs. Not the Tamil, Gurmukhi or Sinhala virama, which is\n * drawn visibly and leaves most letters after it on their own.\n */\nconst STACKING_VIRAMAS = new Set(\n [\n 0x94d, 0x9cd, 0xacd, 0xb4d, 0xc4d, 0xccd, 0xd4d, 0x1039, 0x17d2, 0x1a60, 0x1b44, 0xa9c0,\n 0x11046, 0x11133,\n ].map((point) => String.fromCodePoint(point)),\n);\n/**\n * The letters a visible virama still draws into the letter before it: the\n * Gurmukhi RA, YA and VA, which fonts set under the consonant, and the Tamil\n * conjuncts KSSA (க்ஷ) and SHRII (ஸ்ரீ), which fonts draw as one glyph —\n * each keyed by its letter, virama and the letter before the virama.\n */\nconst SUBJOINED_AFTER = new Map<string, (before: readonly string[]) => boolean>([\n ...[0xa30, 0xa2f, 0xa35].map(\n (point) =>\n [\n String.fromCodePoint(point),\n (before: readonly string[]) => before.at(-1) === String.fromCodePoint(0xa4d),\n ] as const,\n ),\n [\n String.fromCodePoint(0xbb7),\n (before: readonly string[]) =>\n before.at(-1) === String.fromCodePoint(0xbcd) &&\n before.at(-2) === String.fromCodePoint(0xb95),\n ],\n [\n String.fromCodePoint(0xbb0),\n (before: readonly string[]) =>\n before.at(-1) === String.fromCodePoint(0xbcd) &&\n before.at(-2) === String.fromCodePoint(0xbb8),\n ],\n]);\n/** Viramas after which a zero-width joiner asks for a conjunct: every stacking one, and the Sinhala. */\nconst JOINABLE_VIRAMAS = new Set([...STACKING_VIRAMAS, String.fromCodePoint(0xdca)]);\nconst ZERO_WIDTH_JOINER = String.fromCodePoint(0x200d);\n/**\n * Characters the font draws into the letter after them: MALAYALAM LETTER DOT\n * REPH, set on the consonant after it, and the MONGOLIAN VOWEL SEPARATOR, which\n * has no width of its own and changes the shape of the vowel after it.\n */\nconst PREPENDED = new Set([String.fromCodePoint(0xd4e), String.fromCodePoint(0x180e)]);\nconst ARABIC_LAM = String.fromCodePoint(0x644);\n/** The alefs a lam before them fuses with into one glyph, in every Arabic font. */\nconst LIGATING_ALEFS = new Set(\n [0x622, 0x623, 0x625, 0x627, 0x671, 0x672, 0x673, 0x675].map((point) =>\n String.fromCodePoint(point),\n ),\n);\nconst REGIONAL_INDICATOR_RE = /^[\\u{1f1e6}-\\u{1f1ff}]$/u;\nconst LETTER_RE = /^\\p{L}$/u;\n/** A letter of any script but Latin, Greek or Cyrillic, whose common ligatures are only decorative. */\nconst LETTER_WITH_REQUIRED_LIGATURES_RE =\n /(?![\\p{Script=Latin}\\p{Script=Greek}\\p{Script=Cyrillic}])\\p{L}/u;\nconst DIGIT_RE = /^\\p{Nd}$/u;\n/**\n * Signs laid out with the European number beside them (bidirectional class\n * ET): the currency signs, the degree, plus-minus and per-mille signs.\n */\nconst NUMBER_SIGN_RE = new RegExp(\n `^[\\\\p{Sc}${String.fromCodePoint(0xb0, 0xb1, 0x2030, 0x2031, 0x212e, 0x2213)}]$`,\n 'u',\n);\n/** Letters of the Arabic, Syriac and Thaana blocks and their presentation forms: before them, a European number is not one. */\nconst ARABIC_LETTER_BLOCKS: readonly (readonly [number, number])[] = [\n [0x0600, 0x07bf],\n [0x0860, 0x08ff],\n [0xfb50, 0xfdff],\n [0xfe70, 0xfeff],\n];\n\n/**\n * The text out of a `LearnerResponse`. A response of another activity type\n * reads as nothing typed rather than throwing — a controlled host that has not\n * yet swapped its state on an activity change would otherwise crash the attempt.\n */\nfunction textOf(response: LearnerResponse | undefined): string {\n return response !== undefined && response.type === 'dictation' ? response.text : '';\n}\n\n/** The hint count out of a `LearnerResponse`; absent or another type reads as none. */\nfunction hintsOf(response: LearnerResponse | undefined): number {\n if (response === undefined || response.type !== 'dictation') {\n return 0;\n }\n const revealed = response.hintsRevealed;\n return typeof revealed === 'number' && Number.isInteger(revealed) && revealed > 0 ? revealed : 0;\n}\n\n/** The response this component emits; `hintsRevealed` is written only when there is one. */\nfunction responseOf(text: string, hintsRevealed: number): DictationLearnerResponse {\n return { type: 'dictation', text, ...(hintsRevealed > 0 ? { hintsRevealed } : {}) };\n}\n\n/**\n * The slower recording as an `ActivityMedia`: it follows the recording's\n * playback policy minus `maxPlays` (`controls`, `seek`, `rate`, the native\n * hints), so a locked scrubber or a fixed speed binds both files — and it never\n * carries a budget of its own, so `<ActivityMedia>` never demands a binding\n * for it. `undefined` when there is no slow recording.\n */\nfunction slowMediaFor(\n data: Renderable<DictationData>,\n label: string,\n): ActivityMediaData | undefined {\n const slow = data.slowMedia;\n if (slow === undefined) {\n return undefined;\n }\n const policy: MediaPlaybackPolicy = data.media?.playback ?? {};\n const { maxPlays: _budget, ...inherited } = policy;\n const hasPolicy = Object.values(inherited).some((value) => value !== undefined);\n return {\n type: 'audio',\n url: slow.url,\n alt: slow.alt ?? label,\n ...(hasPolicy ? { playback: inherited } : {}),\n };\n}\n\n/**\n * The transcript's words as a learner would read them: raw, in order, split at\n * whitespace. A token with nothing in it that is spelled — a dash, an ellipsis\n * — is joined to the word before it (or to the first word, when it leads), so\n * every reveal shows a word. A script written without spaces is one token, so\n * one reveal shows the whole sentence.\n */\nfunction hintWordsOf(transcript: string): string[] {\n const words: string[] = [];\n let leading = '';\n for (const token of transcript.split(HINT_SPACE_RE)) {\n if (token === '') {\n continue;\n }\n const last = words.length - 1;\n // Composed first: the Greek varia and oxia decompose to the grave and acute\n // accents, which the scorer folds into an apostrophe and removes.\n if (!UNSPELLED_RE.test(token.normalize('NFC'))) {\n words.push(leading === '' ? token : `${leading} ${token}`);\n leading = '';\n } else if (last >= 0) {\n words[last] = `${words[last]} ${token}`;\n } else {\n leading = leading === '' ? token : `${leading} ${token}`;\n }\n }\n return words;\n}\n\n/**\n * Word marks rebuilt from stored scoring details — a server-recorded mark\n * shown after an exam, with no transcript on the client. Extras are not in the\n * details (the scorer writes one per transcript word), so none appear.\n */\nfunction wordsFromDetails(details: readonly ScoringDetail[]): DictationWordAlignment[] | null {\n if (details.length === 0 || !details.every((detail) => /^w\\d+$/.test(detail.itemId))) {\n return null;\n }\n return details.map((detail) => {\n const attempt = Array.isArray(detail.learnerResponse)\n ? detail.learnerResponse.join(' ')\n : detail.learnerResponse;\n const reference = Array.isArray(detail.correctResponse)\n ? detail.correctResponse.join(' ')\n : detail.correctResponse;\n const correct = detail.outcome !== undefined ? detail.outcome === 'correct' : detail.correct;\n const missing = detail.outcome === 'incorrect-omission' || (!correct && attempt === '');\n return {\n itemId: detail.itemId,\n reference,\n attempt,\n similarity: detail.score ?? (correct ? 1 : 0),\n status: correct ? 'correct' : missing ? 'missing' : 'incorrect',\n };\n });\n}\n\n/** A run of character operations drawn as one glyph cluster, and how it is marked. */\ninterface MarkedCluster {\n op: DictationCharOp['op'];\n text: string;\n}\n\n/**\n * Whether `shown`, the character an operation displays, is drawn into the\n * cluster whose characters so far are `before`: a mark, a joiner or a skin tone\n * onto its base, a letter stacked under a virama, an alef fused with the lam\n * before it, the second half of a flag.\n */\nfunction extendsCluster(before: readonly string[], shown: string): boolean {\n const previous = before.at(-1) as string;\n if (EXTENDING_RE.test(shown)) {\n return true;\n }\n if (LETTER_RE.test(shown)) {\n if (\n STACKING_VIRAMAS.has(previous) ||\n PREPENDED.has(previous) ||\n SUBJOINED_AFTER.get(shown)?.(before) === true\n ) {\n return true;\n }\n if (previous === ZERO_WIDTH_JOINER && JOINABLE_VIRAMAS.has(before.at(-2) as string)) {\n return true;\n }\n }\n if (previous === ARABIC_LAM && LIGATING_ALEFS.has(shown)) {\n return true;\n }\n return (\n REGIONAL_INDICATOR_RE.test(shown) &&\n before.length % 2 === 1 &&\n before.every((character) => REGIONAL_INDICATOR_RE.test(character))\n );\n}\n\n/** The character an operation stands for: what should have been typed, or what was typed in excess. */\nfunction characterOf(op: DictationCharOp): string {\n return op.reference === '' ? op.attempt : op.reference;\n}\n\nfunction isArabicLetter(character: string | undefined): boolean {\n const point = character?.codePointAt(0);\n return (\n point !== undefined &&\n LETTER_RE.test(character as string) &&\n ARABIC_LETTER_BLOCKS.some(([first, last]) => point >= first && point <= last)\n );\n}\n\n/**\n * The directions missing characters' bullets are laid out in, by operation\n * index, worked out in two passes over the operations rather than a search\n * per bullet: a word of 2,000 emoji missing its letters took seconds to draw.\n *\n * A bullet takes the character's own direction when it has one — a digit runs\n * left to right, unless it is a digit of a right-to-left script. A currency,\n * degree or per-mille sign beside a digit is laid out with that number, left to\n * right, unless an Arabic letter comes before it, after which the number is not\n * a European one. Otherwise, for a space or a punctuation mark, it takes the\n * direction of the characters on both sides when they agree, and the content's\n * when they do not. A bare bullet is a neutral, which the bidirectional\n * algorithm would otherwise attach to the wrong side of a number or a word of\n * the other direction.\n */\nexport function bulletDirections(\n ops: readonly DictationCharOp[],\n contentDir: 'ltr' | 'rtl' | undefined,\n): Map<number, 'ltr' | 'rtl' | undefined> {\n const own = ops.map((op) => characterDirectionOf(characterOf(op)));\n const before: ('ltr' | 'rtl' | undefined)[] = [];\n const arabicBefore: boolean[] = [];\n let lastDirection: 'ltr' | 'rtl' | undefined;\n let lastLetterArabic = false;\n ops.forEach((op, index) => {\n before[index] = lastDirection;\n arabicBefore[index] = lastLetterArabic;\n lastDirection = own[index] ?? lastDirection;\n if (LETTER_RE.test(characterOf(op))) {\n lastLetterArabic = isArabicLetter(characterOf(op));\n }\n });\n const after: ('ltr' | 'rtl' | undefined)[] = [];\n let nextDirection: 'ltr' | 'rtl' | undefined;\n for (let index = ops.length - 1; index >= 0; index -= 1) {\n after[index] = nextDirection;\n nextDirection = own[index] ?? nextDirection;\n }\n const directions = new Map<number, 'ltr' | 'rtl' | undefined>();\n ops.forEach((op, index) => {\n if (op.op !== 'missing') {\n return;\n }\n const neighbourIsDigit = [ops[index - 1], ops[index + 1]].some(\n (neighbour) => neighbour !== undefined && DIGIT_RE.test(characterOf(neighbour)),\n );\n directions.set(\n index,\n own[index] ??\n (NUMBER_SIGN_RE.test(characterOf(op)) && neighbourIsDigit && !arabicBefore[index]\n ? 'ltr'\n : before[index] !== undefined && before[index] === after[index]\n ? before[index]\n : (contentDir ?? before[index] ?? after[index])),\n );\n });\n return directions;\n}\n\n/**\n * How a cluster is marked. One led by a missing character's bullet is missing,\n * whatever marks sit on the bullet: the character they belong to is not there.\n * Otherwise a cluster is marked as its wrong operations are when they agree —\n * a correct letter with an extra mark is `extra` — and as a substitution when\n * they do not.\n */\nfunction clusterOpOf(ops: readonly DictationCharOp[]): DictationCharOp['op'] {\n if ((ops[0] as DictationCharOp).op === 'missing') {\n return 'missing';\n }\n const wrong = new Set(ops.map((op) => op.op).filter((kind) => kind !== 'equal'));\n if (wrong.size === 0) {\n return 'equal';\n }\n return wrong.size === 1 ? ([...wrong][0] as DictationCharOp['op']) : 'substitute';\n}\n\n/**\n * Character operations grouped into what is drawn as one cluster, so a wrong\n * combining mark, joiner or stacked letter marks the glyph it is drawn into —\n * alone, its span has no width, and its underline or strike would not be\n * painted at all. A missing character's bullet carries only the marks drawn\n * onto it, never a letter that is drawn apart. A missing character's bullet is\n * wrapped in the direction mark of the character it stands for. Consecutive\n * correct clusters are one run: an engine that shapes each element on its own\n * would otherwise draw a correctly typed conjunct or a joined Mongolian word\n * broken apart.\n */\nfunction clustersOf(\n ops: readonly DictationCharOp[],\n contentDir: 'ltr' | 'rtl' | undefined,\n): MarkedCluster[] {\n const bullets = bulletDirections(ops, contentDir);\n const groups: { ops: DictationCharOp[]; shown: string[]; bullet?: 'ltr' | 'rtl' }[] = [];\n ops.forEach((op, index) => {\n const shown = op.op === 'missing' ? BULLET : op.attempt;\n const current = groups.at(-1);\n if (\n current !== undefined &&\n op.op !== 'missing' &&\n ((current.ops[0] as DictationCharOp).op === 'missing'\n ? EXTENDING_RE.test(shown)\n : extendsCluster(current.shown, shown))\n ) {\n current.ops.push(op);\n current.shown.push(shown);\n return;\n }\n const direction = bullets.get(index);\n groups.push({\n ops: [op],\n shown: [shown],\n ...(direction !== undefined ? { bullet: direction } : {}),\n });\n });\n const clusters: MarkedCluster[] = [];\n for (const group of groups) {\n const mark =\n group.bullet === undefined\n ? ''\n : group.bullet === 'rtl'\n ? RIGHT_TO_LEFT_MARK\n : LEFT_TO_RIGHT_MARK;\n const cluster = { op: clusterOpOf(group.ops), text: `${mark}${group.shown.join('')}${mark}` };\n const previous = clusters.at(-1);\n if (previous !== undefined && previous.op === 'equal' && cluster.op === 'equal') {\n previous.text += cluster.text;\n } else {\n clusters.push(cluster);\n }\n }\n return clusters;\n}\n\n/**\n * Character-level edit operations as decoration. Hidden from assistive\n * technology as a whole: a screen reader must never verbalise surrogate or\n * combining fragments one by one, and the word list already carries the\n * meaning. Sighted colour-blind and forced-colors users still get one\n * decoration per state, which the skin draws and the legend explains.\n */\nfunction CharOps({\n ops,\n contentDir,\n}: {\n ops: readonly DictationCharOp[];\n contentDir: 'ltr' | 'rtl' | undefined;\n}) {\n return clustersOf(ops, contentDir).map((cluster, index) => (\n <span\n // biome-ignore lint/suspicious/noArrayIndexKey: clusters are positional and recomputed whole\n key={index}\n className=\"lk-dc-op\"\n data-op={cluster.op}\n // A run with no letter whose script needs its common ligatures: the skin\n // turns them off there, so a font's \"fi\" cannot draw a wrong \"i\" into one glyph.\n {...(LETTER_WITH_REQUIRED_LIGATURES_RE.test(cluster.text)\n ? {}\n : { 'data-ligatures': 'decorative' })}\n aria-hidden=\"true\"\n >\n {cluster.text}\n </span>\n ));\n}\n\n/** What the live region says: the SDK's own sentences, then any authored feedback. */\ninterface Announcement {\n text: string;\n feedback: string | null;\n}\n\n/**\n * The announcement, with authored feedback in its own isolated span: its\n * direction is its own, so an English \"Well heard.\" in an Arabic interface\n * keeps its full stop at its end. Its language is not guessed — feedback may be\n * written in the interface's language or the dictation's.\n */\nfunction AnnouncementText({ announcement }: { announcement: Announcement | null }) {\n if (announcement === null) {\n return null;\n }\n return (\n <>\n {announcement.text}\n {announcement.feedback ? (\n <>\n {' '}\n <span dir=\"auto\">{announcement.feedback}</span>\n </>\n ) : null}\n </>\n );\n}\n\nexport function Dictation({\n data,\n onComplete,\n onSubmit,\n onChange,\n onInteraction,\n value,\n defaultValue,\n defaultSubmitted,\n renderMode = 'practice',\n outcome,\n mediaBudget,\n mediaStrings,\n strings,\n theme,\n locale,\n disabled,\n ai: aiProp,\n delivery,\n scoring,\n}: DictationProps) {\n const isExam = renderMode === 'exam';\n const isReview = renderMode === 'review';\n const s = useLkStrings(strings);\n const ai = useLearnerAi(aiProp);\n const policy = useDeliveryPolicy(delivery);\n const scoringPolicy = useItemScoringPolicy(scoring);\n\n // Whether the payload carries the transcript at all — the KEY, not the\n // `redacted` marker. `redact(data, { reveal: 'after-submit' })` stamps\n // `redacted: true` on a projection that still has its transcript, and that\n // projection is exactly what a review screen hands this component. A type\n // test only: no content is read here, and none is read in `exam` below.\n const hasKey = typeof (data as { transcript?: unknown }).transcript === 'string';\n\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n if (data.redacted === true) {\n if (hasKey) {\n // An after-submit projection: the strict redacted schema has no\n // `transcript`, so `assertRedacted` would throw on the one payload\n // this component exists to render in review. Check the public subset.\n const shape = data as { type?: unknown; id?: unknown; title?: unknown };\n return shape.type === 'dictation' &&\n typeof shape.id === 'string' &&\n typeof shape.title === 'string'\n ? null\n : new Error(\n 'Dictation received a revealed projection (redacted: true with a transcript) that ' +\n 'is not a dictation: `type`, `id` and `title` are required.',\n );\n }\n try {\n assertRedacted(data);\n return null;\n } catch (error) {\n return error instanceof Error ? error : new Error(String(error));\n }\n }\n const result = validateActivity('dictation', data);\n return result.success ? null : new ActivitySchemaError('dictation', result.errors);\n }, [data, hasKey]);\n\n const { state, start, complete, getTimeSpent, reset } = useActivityState(\n defaultSubmitted === true ? 'completed' : 'idle',\n );\n // Before anything that resets them: a new question starts with no tries.\n const tries = useTries({\n policy: scoringPolicy,\n graded: renderMode === 'practice' && policy.feedback,\n submitted: state === 'completed',\n disabled: disabled === true,\n });\n const resetTries = tries.reset;\n const isControlled = value !== undefined;\n const [internalText, setInternalText] = useState<string>(() => textOf(defaultValue));\n const [internalRevealed, setInternalRevealed] = useState<number>(() => hintsOf(defaultValue));\n const [result, setResult] = useState<ScoringResult | null>(null);\n const [summary, setSummary] = useState<Announcement | null>(null);\n const [solutionShown, setSolutionShown] = useState(false);\n const formRef = useRef<HTMLFormElement>(null);\n const feedbackRef = useRef<HTMLDivElement>(null);\n const focusAfterRef = useRef<'answer' | 'feedback' | null>(null);\n\n const text = isControlled ? textOf(value) : internalText;\n const revealed = isControlled ? hintsOf(value) : internalRevealed;\n\n const defaultValueRef = useRef(defaultValue);\n useEffect(() => {\n defaultValueRef.current = defaultValue;\n }, [defaultValue]);\n\n const defaultSubmittedRef = useRef(defaultSubmitted);\n useEffect(() => {\n defaultSubmittedRef.current = defaultSubmitted;\n }, [defaultSubmitted]);\n\n // Identity-guarded so the mount run is a no-op: without it this fires after\n // the first paint and undoes every seed it was just given (Req 3.7).\n const lastDataRef = useRef(data);\n useEffect(() => {\n if (lastDataRef.current === data) {\n return;\n }\n lastDataRef.current = data;\n setInternalText(textOf(defaultValueRef.current));\n setInternalRevealed(hintsOf(defaultValueRef.current));\n setResult(null);\n setSummary(null);\n setSolutionShown(false);\n resetTries();\n reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [data, reset, resetTries]);\n\n // `text-changed` is debounced so an autosaving host is not told about every\n // keystroke; only the length travels, never the text.\n const textChangedRef = useRef<{ timer: ReturnType<typeof setTimeout>; length: number } | null>(\n null,\n );\n useEffect(\n () => () => {\n if (textChangedRef.current !== null) {\n clearTimeout(textChangedRef.current.timer);\n }\n },\n [],\n );\n\n const revealButtonRef = useRef<HTMLButtonElement>(null);\n const ids = useId();\n const hintId = `${ids}-hint`;\n const titleId = `${ids}-title`;\n const solutionId = `${ids}-solution`;\n // Per mount, not per activity id: two renderings of one item on a page (a\n // review list of several attempts, a preview beside a sequence) are\n // separate players, and neither should pause the other's recordings.\n const [recordingsGroup] = useState(() => {\n recordingsGroupCount += 1;\n return `lk-dc-recordings-${recordingsGroupCount}`;\n });\n\n const slowMedia = useMemo(\n () => slowMediaFor(data, s.dictationSlowRecording),\n [data, s.dictationSlowRecording],\n );\n\n useEffect(() => {\n const target = focusAfterRef.current;\n if (target === null) {\n return;\n }\n focusAfterRef.current = null;\n if (target === 'answer') {\n formRef.current?.querySelector<HTMLElement>('textarea')?.focus();\n } else {\n feedbackRef.current?.focus();\n }\n });\n\n // All hooks are called before these throws, so hook order stays stable.\n if (devError) {\n throw devError;\n }\n // Practice grades locally, and a redacted projection has no answer key to\n // grade against — `score()` would throw RedactedScoringError from the submit\n // handler, where no error boundary can reach it and after the learner has\n // typed. Fail at render instead, in production too, as every built-in does.\n if (data.redacted === true && renderMode === 'practice') {\n throw new Error(\n `Dictation \"${data.id}\" received redacted activity data in renderMode \"practice\", ` +\n 'which grades locally and has no answer key to grade against. ' +\n 'Render redacted data with renderMode=\"exam\" (server grades) or \"review\" (pass `outcome`).',\n );\n }\n\n const submitted = state === 'completed';\n const inactive = disabled === true || submitted || isReview;\n\n /**\n * Whether answer-key-derived information may be shown. Never in `exam`; in\n * `practice` only once this component has scored; in `review` only once the\n * caller supplies a SCORED outcome.\n */\n const revealing = isReview ? outcome?.status === 'scored' : !isExam && submitted;\n // What the delivery policy lets that reveal show: the marks, the score and\n // the author's feedback — and, only where solutions show too, the \"Show\n // solution\" panel, since the transcript is the whole answer.\n const marking = revealing && policy.feedback;\n // Whether the right answer may show: not where the paper withholds it, and\n // not while the learner is offered another try, which is for finding it.\n // Without it the marks still say which words are right, wrong, missing or\n // extra — but never what a wrong or missing word should have been.\n const solutionsShown = policy.solutions && !tries.open;\n\n // The transcript, read only outside `exam`. `Renderable` says it is a\n // string; a plain redacted projection has none, so it is read defensively\n // and every consumer of it goes through `alignDictation`'s own guards.\n const transcript: string | undefined =\n isExam || !hasKey ? undefined : (data as { transcript: string }).transcript;\n\n // Hints exist only where the transcript does and nothing has been graded:\n // practice, before submit. A plain redacted projection has no words to give.\n const hintWords = useMemo(\n () => (transcript === undefined ? [] : hintWordsOf(transcript)),\n [transcript],\n );\n const hintsOffered =\n policy.hints &&\n data.hints?.mode === 'progressive-words' &&\n !isExam &&\n !isReview &&\n transcript !== undefined;\n const hintsEnabled = hintsOffered && !submitted;\n const hintTotal = hintWords.length;\n const hintsShown = Math.min(revealed, hintTotal);\n // What a response reports: the words actually shown, and none where no hint\n // can be — whatever count a seeded or controlled value carries.\n const reportedHints = hintsOffered ? hintsShown : 0;\n\n const fireInteraction = (\n type: 'text-changed' | 'hint-requested' | 'submitted',\n payload: Record<string, unknown>,\n ): void => {\n onInteraction?.({ type, activityId: data.id, timestamp: Date.now(), payload });\n };\n\n const flushTextChanged = (): void => {\n const pending = textChangedRef.current;\n if (pending === null) {\n return;\n }\n clearTimeout(pending.timer);\n textChangedRef.current = null;\n fireInteraction('text-changed', { length: pending.length });\n };\n\n const handleTextChange = (next: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n if (!isControlled) {\n setInternalText(next);\n }\n onChange?.(responseOf(next, reportedHints));\n if (onInteraction === undefined) {\n return;\n }\n if (textChangedRef.current !== null) {\n clearTimeout(textChangedRef.current.timer);\n }\n textChangedRef.current = {\n length: next.length,\n timer: setTimeout(() => {\n const pending = textChangedRef.current;\n textChangedRef.current = null;\n if (pending !== null) {\n fireInteraction('text-changed', { length: pending.length });\n }\n }, TEXT_CHANGED_DEBOUNCE_MS),\n };\n };\n\n const setRevealed = (next: number): void => {\n if (!isControlled) {\n setInternalRevealed(next);\n }\n onChange?.(responseOf(text, next));\n };\n\n const revealNextHint = (): void => {\n if (inactive || hintsShown >= hintTotal) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n const next = hintsShown + 1;\n setRevealed(next);\n fireInteraction('hint-requested', { revealed: next, total: hintTotal });\n };\n\n const resetHints = (): void => {\n // Under a hint cost the words shown stay shown: hiding them would not\n // unsee them, and the count they are charged by would say they were not.\n if (inactive || hintsShown === 0 || scoringPolicy.hintPenalty > 0) {\n return;\n }\n // The reset button leaves the page once nothing is revealed; hand focus to\n // the reveal button first, or the keyboard user lands on the page body.\n revealButtonRef.current?.focus();\n setRevealed(0);\n };\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive) {\n return;\n }\n const response = responseOf(text, reportedHints);\n onSubmit?.(response);\n flushTextChanged();\n\n if (isExam) {\n // The client neither grades nor reveals: no score(), no read of the\n // transcript, no onComplete, and no xAPI statement — its\n // correctResponsesPattern IS the answer key.\n complete();\n setSummary({ text: s.answerSubmitted, feedback: null });\n fireInteraction('submitted', { length: text.length, hintsRevealed: reportedHints });\n return;\n }\n\n const answer = score('dictation', data as DictationData, response);\n complete();\n const timeSpent = getTimeSpent();\n // The question's score under the policy — see MultipleChoice.\n const counted = tries.record({\n score: answer.score,\n maxScore: answer.maxScore,\n hintsRevealed: reportedHints,\n });\n const mine = counted.tries[counted.tries.length - 1];\n const costed = mine === undefined ? answer.score : mine.scored;\n const scoringResult =\n costed === answer.score\n ? answer\n : { ...answer, score: costed, passed: computePassThreshold(data as DictationData, costed) };\n const passed =\n counted.score === scoringResult.score\n ? scoringResult.passed\n : computePassThreshold(data as DictationData, counted.score);\n const xapiStatement = xAPIBuilder.buildAnsweredStatement({\n actor: ANONYMOUS_ACTOR,\n object: {\n id: objectIdFor(data.id),\n name: { [data.locale ?? 'en-US']: data.title },\n ...xapiDefinitionFor(data),\n },\n scoringResult,\n timeSpentMs: timeSpent,\n response: response.text,\n });\n setResult(answer);\n onComplete?.(\n stampTake(\n { score: counted.score, maxScore: counted.maxScore, passed, timeSpent, xapiStatement },\n mintTake(),\n ),\n );\n const correctWords = answer.details.filter(\n (detail) => (detail.outcome ?? (detail.correct ? 'correct' : 'incorrect')) === 'correct',\n ).length;\n setSolutionShown(false);\n setSummary({\n text: `${s.answerSubmitted} ${s.scoreAnnouncement(\n Math.round(counted.score * 100),\n passed,\n )} ${s.dictationWordsSummary(correctWords, answer.details.length)}`,\n feedback: answer.feedback,\n });\n fireInteraction('submitted', {\n length: text.length,\n hintsRevealed: reportedHints,\n score: scoringResult.score,\n });\n };\n\n /** \"Try again\": the text stays, to be corrected; the marks go. Hints shown stay shown. */\n const retry = (): void => {\n reset('idle');\n setSummary(null);\n focusAfterRef.current = 'answer';\n };\n\n /** \"Show answer\": no more tries; the question shows what a finished one shows. */\n const closeTries = (): void => {\n tries.close();\n focusAfterRef.current = 'feedback';\n };\n\n // A review may hold the key but not the learner's text — an outcome recorded\n // without its response. Recomputing from an empty box would contradict the\n // stored score, so the stored details are the record then.\n const given = isControlled ? value : defaultValue;\n const hasResponse = given !== undefined && given.type === 'dictation';\n // The dictation's language, and the direction its tag names when it names\n // one. Read defensively: stale data may carry a locale that is not a string.\n // A tag written with underscores reads as its hyphenated form, the only one `lang` accepts.\n const contentLang =\n typeof data.locale === 'string' ? data.locale.replaceAll('_', '-') : undefined;\n const localeDir = localeDirectionOf(data.locale);\n\n // The same comparison the scorer made — computed only when it may be shown.\n // The character diff is UI-only and needs memory proportional to the product\n // of the two lengths, so it lives here and nowhere near `score()`.\n const alignment = useMemo<DictationAlignment | null>(\n () =>\n marking && transcript !== undefined && (!isReview || hasResponse)\n ? alignDictation(data, text)\n : null,\n [marking, transcript, isReview, hasResponse, data, text],\n );\n // The character diff draws the transcript's letters beside the learner's, so\n // it is the right answer: only where that may show.\n const sentenceDiff = useMemo<DictationCharOp[] | null>(\n () =>\n alignment !== null && alignment.attempt !== '' && solutionsShown\n ? diffDictationChars(alignment.reference, alignment.attempt)\n : null,\n [alignment, solutionsShown],\n );\n\n // Review with no transcript on the client, or no response to recompute from:\n // a server-recorded mark still renders its word list from the stored details.\n const detailWords = useMemo<DictationWordAlignment[] | null>(\n () =>\n isReview && alignment === null && outcome?.status === 'scored'\n ? wordsFromDetails(outcome.details)\n : null,\n [isReview, alignment, outcome],\n );\n\n const words: DictationWordAlignment[] | null = alignment?.words ?? detailWords;\n const marksShown = marking && words !== null;\n // The authored content's direction: from its language, or else from the first\n // letter of the transcript (or of the stored words, when only they are here).\n // Never `auto` on a container: its first strong character could be a hidden\n // interface sentence, or whatever the learner typed. `undefined` — no letter\n // anywhere — inherits the interface's.\n const contentDir =\n localeDir ??\n textDirectionOf(transcript ?? words?.map((word) => word.reference).join(' ') ?? '');\n // Nothing survived normalisation: from the alignment when there is one, and\n // from the stored details otherwise (every transcript word unanswered).\n const nothingTyped =\n alignment !== null\n ? alignment.attempt === ''\n : (words?.every((word) => word.attempt === '') ?? false);\n\n const passed = isReview\n ? outcome?.status === 'scored' || outcome?.status === 'graded'\n ? outcome.passed\n : undefined\n : result?.passed;\n\n const reviewSummary = useMemo<Announcement | null>(() => {\n if (!isReview || outcome === undefined) {\n return null;\n }\n if (outcome.status === 'scored') {\n return {\n text: s.scoreAnnouncement(Math.round(outcome.score * 100), outcome.passed),\n feedback: outcome.feedback,\n };\n }\n if (outcome.status === 'deferred') {\n return { text: s.notGradedYet, feedback: null };\n }\n if (outcome.status === 'graded') {\n const percent =\n outcome.maxScore > 0\n ? Math.round((outcome.score / outcome.maxScore) * 100)\n : Math.round(outcome.score * 100);\n return {\n text: s.scoreAnnouncement(percent, outcome.passed),\n feedback: outcome.feedback ?? null,\n };\n }\n return { text: s.noGradeAvailable, feedback: null };\n }, [isReview, outcome, s]);\n\n const sentenceFor = (word: DictationWordAlignment): string => {\n switch (word.status) {\n case 'correct':\n return s.dictationWordCorrect(word.attempt);\n case 'incorrect':\n return solutionsShown\n ? s.dictationWordWrong(word.attempt, word.reference)\n : s.dictationWordWrongUnnamed(word.attempt);\n case 'missing':\n return solutionsShown\n ? s.dictationWordMissing(word.reference)\n : s.dictationWordMissingUnnamed;\n default:\n return s.dictationWordExtra(word.attempt);\n }\n };\n\n const acceptedTranscripts: readonly string[] | undefined =\n transcript === undefined || !Array.isArray(data.acceptedTranscripts)\n ? undefined\n : data.acceptedTranscripts.filter((entry): entry is string => typeof entry === 'string');\n\n const extra =\n !isReview && policy.feedback && submitted && tries.counted !== null\n ? triesSummary(s, scoringPolicy, tries.counted, tries.open)\n : '';\n\n return (\n <form\n ref={formRef}\n className=\"lk-dc\"\n // A title with nothing to say would name the form with silence.\n aria-labelledby={VISIBLE_TEXT_RE.test(data.title) ? titleId : undefined}\n lang={locale}\n data-render-mode={renderMode}\n style={theme as CSSProperties | undefined}\n onSubmit={handleSubmit}\n >\n {/*\n The form's name: read from here, it keeps the content's language. A\n paragraph of its own, so without a language to go by its own first\n letter decides its direction.\n */}\n <p id={titleId} className=\"lk-dc-title\" dir={localeDir ?? 'auto'} lang={contentLang}>\n {data.title}\n </p>\n\n {data.media !== undefined || slowMedia !== undefined ? (\n <div className=\"lk-dc-recordings\">\n {/*\n Each recording is a named group (a fieldset with a legend, which is\n what `role=\"group\"` plus a label spells natively), so the two\n transports are told apart by name and not only by position.\n */}\n {data.media !== undefined ? (\n <fieldset className=\"lk-dc-recording\">\n <legend className=\"lk-dc-recording-label\">{s.dictationRecording}</legend>\n {/* The ONLY budgeted recording: the binding is whatever the pager or the caller passes. */}\n <ActivityMedia\n media={data.media}\n renderMode={renderMode}\n playbackGroup={recordingsGroup}\n {...(mediaBudget !== undefined ? { mediaBudget } : {})}\n {...(mediaStrings !== undefined ? { mediaStrings } : {})}\n {...(strings !== undefined ? { strings } : {})}\n {...(onInteraction !== undefined ? { onInteraction } : {})}\n {...(locale !== undefined ? { locale } : {})}\n {...(disabled !== undefined ? { disabled } : {})}\n />\n </fieldset>\n ) : null}\n {slowMedia !== undefined ? (\n <fieldset className=\"lk-dc-recording\" data-slow=\"true\">\n <legend className=\"lk-dc-recording-label\">{s.dictationSlowRecording}</legend>\n {/* No budget of its own, by contract — so no binding is passed and none is demanded. */}\n <ActivityMedia\n media={slowMedia}\n renderMode={renderMode}\n playbackGroup={recordingsGroup}\n {...(mediaStrings !== undefined ? { mediaStrings } : {})}\n {...(strings !== undefined ? { strings } : {})}\n {...(locale !== undefined ? { locale } : {})}\n {...(disabled !== undefined ? { disabled } : {})}\n />\n </fieldset>\n ) : null}\n </div>\n ) : null}\n\n {hintsEnabled ? (\n <div className=\"lk-dc-hints\">\n {/*\n aria-disabled, never disabled, once every word is shown: a natively\n disabled button drops keyboard focus to the page body.\n */}\n <button\n ref={revealButtonRef}\n type=\"button\"\n className=\"lk-dc-hint-btn\"\n disabled={inactive}\n aria-disabled={hintsShown >= hintTotal || undefined}\n onClick={revealNextHint}\n >\n {s.dictationRevealNextWord(hintsShown, hintTotal)}\n </button>\n <HintCost policy={scoringPolicy} strings={s} />\n {hintsShown > 0 && scoringPolicy.hintPenalty <= 0 ? (\n <button\n type=\"button\"\n className=\"lk-dc-hint-reset\"\n disabled={inactive}\n onClick={resetHints}\n >\n {s.dictationResetHints}\n </button>\n ) : null}\n {/* Rendered before it has content, so the live region exists when the first word lands. */}\n <p id={hintId} className=\"lk-dc-hint\" role=\"status\" dir={contentDir} lang={contentLang}>\n {hintsShown > 0\n ? `${hintWords.slice(0, hintsShown).join(' ')}${hintsShown < hintTotal ? ' …' : ''}`\n : ''}\n </p>\n </div>\n ) : null}\n\n <fieldset disabled={inactive}>\n <textarea\n className=\"lk-dc-input\"\n aria-label={s.dictationInputLabel}\n aria-describedby={hintsEnabled && hintsShown > 0 ? hintId : undefined}\n // No content lang here: the element's accessible name is interface\n // text, and a screen reader voices a name in its element's language.\n dir=\"auto\"\n rows={3}\n maxLength={DICTATION_MAX_TEXT_LENGTH}\n // Assessment integrity, not style: a corrected or completed word is\n // not the learner's spelling.\n autoComplete=\"off\"\n autoCapitalize=\"none\"\n autoCorrect=\"off\"\n spellCheck={false}\n value={text}\n disabled={inactive}\n aria-disabled={inactive || undefined}\n data-correct={\n marking && passed !== undefined && (!isReview || hasResponse)\n ? String(passed)\n : undefined\n }\n onChange={(event) => handleTextChange(event.target.value)}\n />\n {isReview ? null : (\n // Enabled on empty text: an omission is a recorded answer.\n <button type=\"submit\" disabled={inactive}>\n {isExam ? s.submitAnswers : s.checkAnswers}\n </button>\n )}\n </fieldset>\n\n {marksShown ? (\n <div className=\"lk-dc-result\">\n {nothingTyped ? (\n <p className=\"lk-dc-nothing\" role=\"note\">\n {s.dictationNothingTyped}\n </p>\n ) : (\n <>\n {/*\n The word list is the assistive-technology channel: one hidden\n sentence per word says what happened to it. The visible token,\n its glyph and its character marks are decoration.\n */}\n {/*\n The list's name and the hidden sentences are interface text, so\n they keep the interface language; only the typed tokens carry\n the dictation's. Its direction is the dictation's, never\n resolved from the list's own text: the first strong character\n there would be a hidden interface sentence, or whatever the\n learner typed. Each word's own text may resolve from itself.\n */}\n <ol className=\"lk-dc-words\" aria-label={s.dictationMarksLabel} dir={contentDir}>\n {words?.map((word, index) => {\n const sentence = sentenceFor(word);\n return (\n <li\n // biome-ignore lint/suspicious/noArrayIndexKey: alignment order is the identity; words repeat\n key={index}\n className=\"lk-dc-word\"\n data-state={word.status}\n title={sentence}\n >\n <span className=\"lk-visually-hidden\" style={VISUALLY_HIDDEN}>\n {sentence}\n </span>\n <span\n className=\"lk-dc-word-text\"\n aria-hidden=\"true\"\n dir={contentDir ?? 'auto'}\n lang={contentLang}\n // A wrong word drawn as its character marks: the marks\n // carry the decoration, so the wrong characters stand\n // out from the right ones by shape, not colour alone.\n {...(word.status === 'incorrect' && alignment !== null && solutionsShown\n ? { 'data-marks': 'characters' }\n : {})}\n >\n {word.status === 'incorrect' && alignment !== null && solutionsShown ? (\n <CharOps\n ops={diffDictationChars(word.reference, word.attempt)}\n contentDir={contentDir}\n />\n ) : word.status === 'missing' ? (\n // Where the answer may not show, a gap the size of\n // nothing in particular: the word is missing, not named.\n solutionsShown ? (\n word.reference\n ) : (\n '…'\n )\n ) : (\n word.attempt\n )}\n </span>\n </li>\n );\n })}\n </ol>\n {sentenceDiff !== null ? (\n <p className=\"lk-dc-diff\" aria-hidden=\"true\" dir={contentDir} lang={contentLang}>\n <CharOps ops={sentenceDiff} contentDir={contentDir} />\n </p>\n ) : null}\n <ul className=\"lk-dc-legend\">\n <li data-state=\"correct\">{s.dictationLegendCorrect}</li>\n <li data-state=\"incorrect\">{s.dictationLegendWrong}</li>\n <li data-state=\"missing\">{s.dictationLegendMissing}</li>\n {alignment !== null ? <li data-state=\"extra\">{s.dictationLegendExtra}</li> : null}\n </ul>\n <p className=\"lk-dc-diff-note\">{s.dictationDiffNote}</p>\n </>\n )}\n {/*\n Offered after an empty answer too: a learner who could make out\n nothing is the one who most needs to read the sentence, and a\n single typed letter would reveal it anyway.\n */}\n {transcript !== undefined && solutionsShown ? (\n <>\n <button\n type=\"button\"\n className=\"lk-dc-solution-toggle\"\n aria-expanded={solutionShown}\n aria-controls={solutionId}\n onClick={() => setSolutionShown((shown) => !shown)}\n >\n {solutionShown ? s.hideSolution : s.showSolution}\n </button>\n {solutionShown ? (\n <blockquote\n id={solutionId}\n className=\"lk-dc-solution\"\n aria-label={s.dictationSolutionLabel}\n dir={contentDir}\n >\n <p lang={contentLang}>{transcript}</p>\n {acceptedTranscripts !== undefined && acceptedTranscripts.length > 0 ? (\n <ul className=\"lk-dc-solution-alternatives\" lang={contentLang}>\n {acceptedTranscripts.map((alternative) => (\n <li key={alternative}>{alternative}</li>\n ))}\n </ul>\n ) : null}\n </blockquote>\n ) : null}\n </>\n ) : null}\n </div>\n ) : null}\n\n <FeedbackRegion\n id={`${data.id}-feedback`}\n {...(scoringPolicy.retries > 0 ? { ref: feedbackRef } : {})}\n >\n <AnnouncementText\n announcement={\n isReview\n ? policy.feedback || !outcomeShowsMarks(outcome)\n ? reviewSummary\n : null\n : policy.feedback || summary === null\n ? summary === null || extra === ''\n ? summary\n : { ...summary, text: `${summary.text} ${extra}` }\n : { text: s.answerSubmitted, feedback: null }\n }\n />\n </FeedbackRegion>\n <TryActions\n tries={tries}\n solutions={policy.solutions && transcript !== undefined}\n strings={s}\n onRetry={retry}\n onClose={closeTries}\n />\n <AiExplanation\n ai={ai}\n data={data}\n renderMode={renderMode}\n submitted={submitted}\n response={responseOf(text, reportedHints)}\n outcome={outcome}\n locale={locale}\n onInteraction={onInteraction}\n strings={s}\n // An explanation all but always names the answer: not while another\n // try is on offer.\n delivery={tries.open ? { ...policy, solutions: false } : policy}\n />\n </form>\n );\n}\n","'use client';\n\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport { Dictation as DictationCore, type DictationProps } from './Dictation.js';\n\nexport type { DictationProps } from './Dictation.js';\n\n/**\n * Public Dictation activity: the core wrapped in `ActivityErrorBoundary` so a\n * render failure (including the dev schema-validation throw and the\n * redacted-in-`practice` guard) degrades to an accessible fallback instead of\n * crashing the host.\n */\nexport function Dictation(props: DictationProps) {\n const strings = useLkStrings(props.strings);\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title} strings={strings}>\n <DictationCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}