@intellectif/lk-react 19.0.0 → 20.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 (137) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +1 -0
  3. package/dist/{WrittenResponse-CeHY5u3W.d.ts → WrittenResponse-D2YI8xOK.d.ts} +8 -2
  4. package/dist/{WrittenResponse-DT_Yqbq1.d.cts → WrittenResponse-obIDDYDH.d.cts} +8 -2
  5. package/dist/ai/useAiHelp.cjs +4 -4
  6. package/dist/ai/useAiHelp.d.cts +8 -2
  7. package/dist/ai/useAiHelp.d.ts +8 -2
  8. package/dist/ai/useAiHelp.js +2 -2
  9. package/dist/{chunk-SEQTGOOU.js → chunk-3XP6VO4G.js} +22 -15
  10. package/dist/chunk-3XP6VO4G.js.map +1 -0
  11. package/dist/{chunk-5QCZZBOX.js → chunk-5GI53C6T.js} +22 -13
  12. package/dist/chunk-5GI53C6T.js.map +1 -0
  13. package/dist/{chunk-XUQ4KRTT.cjs → chunk-6S2CKZ4H.cjs} +24 -17
  14. package/dist/chunk-6S2CKZ4H.cjs.map +1 -0
  15. package/dist/{chunk-XHF376ES.js → chunk-AG7MWZYZ.js} +25 -11
  16. package/dist/chunk-AG7MWZYZ.js.map +1 -0
  17. package/dist/{chunk-YIYMBCPV.js → chunk-AQIAQHJC.js} +18 -9
  18. package/dist/chunk-AQIAQHJC.js.map +1 -0
  19. package/dist/{chunk-4NERCBOW.js → chunk-BGA2IVQY.js} +7 -5
  20. package/dist/chunk-BGA2IVQY.js.map +1 -0
  21. package/dist/{chunk-JVWLXVYG.cjs → chunk-BJLQKJZX.cjs} +2 -2
  22. package/dist/chunk-BJLQKJZX.cjs.map +1 -0
  23. package/dist/{chunk-XAIJNGTU.cjs → chunk-CEUEXISZ.cjs} +18 -13
  24. package/dist/chunk-CEUEXISZ.cjs.map +1 -0
  25. package/dist/{chunk-WKA6FUR4.cjs → chunk-DZVF3IGB.cjs} +24 -18
  26. package/dist/chunk-DZVF3IGB.cjs.map +1 -0
  27. package/dist/{chunk-KOFA54C7.js → chunk-HI6BUUIJ.js} +18 -12
  28. package/dist/chunk-HI6BUUIJ.js.map +1 -0
  29. package/dist/{chunk-NGFTSRKO.cjs → chunk-HREA5SMB.cjs} +23 -14
  30. package/dist/chunk-HREA5SMB.cjs.map +1 -0
  31. package/dist/{chunk-NLTU5JFD.js → chunk-IQ55QNBI.js} +16 -11
  32. package/dist/chunk-IQ55QNBI.js.map +1 -0
  33. package/dist/chunk-IXMT7X3E.cjs +44 -0
  34. package/dist/chunk-IXMT7X3E.cjs.map +1 -0
  35. package/dist/{chunk-ZME5FMED.cjs → chunk-JLDSCHY5.cjs} +27 -13
  36. package/dist/chunk-JLDSCHY5.cjs.map +1 -0
  37. package/dist/{chunk-55W2472T.cjs → chunk-LK5VXNMR.cjs} +14 -14
  38. package/dist/{chunk-55W2472T.cjs.map → chunk-LK5VXNMR.cjs.map} +1 -1
  39. package/dist/{chunk-KGDGYRBL.cjs → chunk-NOC2M6LU.cjs} +7 -6
  40. package/dist/chunk-NOC2M6LU.cjs.map +1 -0
  41. package/dist/{chunk-UXV3V5NH.js → chunk-NWNQ2PHT.js} +23 -11
  42. package/dist/chunk-NWNQ2PHT.js.map +1 -0
  43. package/dist/chunk-ORBU3XKH.js +40 -0
  44. package/dist/chunk-ORBU3XKH.js.map +1 -0
  45. package/dist/{chunk-2X2NZF46.cjs → chunk-PWLJDP3X.cjs} +29 -20
  46. package/dist/chunk-PWLJDP3X.cjs.map +1 -0
  47. package/dist/{chunk-OWV7VRMQ.js → chunk-QKW2GGTV.js} +20 -11
  48. package/dist/chunk-QKW2GGTV.js.map +1 -0
  49. package/dist/{chunk-NZM3M5IR.cjs → chunk-RT2PP4SO.cjs} +27 -15
  50. package/dist/chunk-RT2PP4SO.cjs.map +1 -0
  51. package/dist/{chunk-QRT2W5N5.js → chunk-RUS42NMN.js} +5 -4
  52. package/dist/chunk-RUS42NMN.js.map +1 -0
  53. package/dist/{chunk-SXU26SFO.cjs → chunk-TRMWBJWT.cjs} +17 -8
  54. package/dist/chunk-TRMWBJWT.cjs.map +1 -0
  55. package/dist/{chunk-CQNKZ435.cjs → chunk-UHIOVOFK.cjs} +7 -5
  56. package/dist/chunk-UHIOVOFK.cjs.map +1 -0
  57. package/dist/{chunk-7NNMDZUU.js → chunk-W6DMHN33.js} +8 -8
  58. package/dist/{chunk-7NNMDZUU.js.map → chunk-W6DMHN33.js.map} +1 -1
  59. package/dist/{chunk-TEAVPUMX.js → chunk-WBLYD5QA.js} +2 -2
  60. package/dist/chunk-WBLYD5QA.js.map +1 -0
  61. package/dist/components/ActivityPreview.cjs +12 -12
  62. package/dist/components/ActivityPreview.d.cts +2 -2
  63. package/dist/components/ActivityPreview.d.ts +2 -2
  64. package/dist/components/ActivityPreview.js +11 -11
  65. package/dist/components/ActivitySequence.cjs +12 -12
  66. package/dist/components/ActivitySequence.d.cts +16 -4
  67. package/dist/components/ActivitySequence.d.ts +16 -4
  68. package/dist/components/ActivitySequence.js +11 -11
  69. package/dist/components/Dictation.cjs +6 -6
  70. package/dist/components/Dictation.d.cts +1 -1
  71. package/dist/components/Dictation.d.ts +1 -1
  72. package/dist/components/Dictation.js +5 -5
  73. package/dist/components/FillInTheBlanks.cjs +6 -6
  74. package/dist/components/FillInTheBlanks.d.cts +1 -1
  75. package/dist/components/FillInTheBlanks.d.ts +1 -1
  76. package/dist/components/FillInTheBlanks.js +5 -5
  77. package/dist/components/GapSelect.cjs +6 -6
  78. package/dist/components/GapSelect.d.cts +1 -1
  79. package/dist/components/GapSelect.d.ts +1 -1
  80. package/dist/components/GapSelect.js +5 -5
  81. package/dist/components/InteractiveVideo.cjs +12 -12
  82. package/dist/components/InteractiveVideo.d.cts +16 -3
  83. package/dist/components/InteractiveVideo.d.ts +16 -3
  84. package/dist/components/InteractiveVideo.js +10 -10
  85. package/dist/components/MultipleChoice.cjs +6 -6
  86. package/dist/components/MultipleChoice.d.cts +1 -1
  87. package/dist/components/MultipleChoice.d.ts +1 -1
  88. package/dist/components/MultipleChoice.js +5 -5
  89. package/dist/components/PronunciationFeedback.d.cts +1 -1
  90. package/dist/components/PronunciationFeedback.d.ts +1 -1
  91. package/dist/components/ReadAloud.cjs +4 -4
  92. package/dist/components/ReadAloud.d.cts +2 -2
  93. package/dist/components/ReadAloud.d.ts +2 -2
  94. package/dist/components/ReadAloud.js +3 -3
  95. package/dist/components/StimulusPanel.d.cts +1 -1
  96. package/dist/components/StimulusPanel.d.ts +1 -1
  97. package/dist/components/WrittenResponse.cjs +4 -3
  98. package/dist/components/WrittenResponse.d.cts +3 -3
  99. package/dist/components/WrittenResponse.d.ts +3 -3
  100. package/dist/components/WrittenResponse.js +3 -2
  101. package/dist/i18n/LkIntlProvider.d.cts +2 -2
  102. package/dist/i18n/LkIntlProvider.d.ts +2 -2
  103. package/dist/index.cjs +25 -25
  104. package/dist/index.cjs.map +1 -1
  105. package/dist/index.d.cts +2 -2
  106. package/dist/index.d.ts +2 -2
  107. package/dist/index.js +13 -13
  108. package/dist/index.js.map +1 -1
  109. package/dist/{types-DtPYG1hh.d.cts → types-B_PCcqO-.d.cts} +9 -1
  110. package/dist/{types-BY-4S5Rd.d.ts → types-C-GMCJ0g.d.ts} +9 -1
  111. package/package.json +3 -3
  112. package/dist/chunk-2X2NZF46.cjs.map +0 -1
  113. package/dist/chunk-4NERCBOW.js.map +0 -1
  114. package/dist/chunk-5QCZZBOX.js.map +0 -1
  115. package/dist/chunk-BRPEB43C.cjs +0 -13
  116. package/dist/chunk-BRPEB43C.cjs.map +0 -1
  117. package/dist/chunk-CQNKZ435.cjs.map +0 -1
  118. package/dist/chunk-JVWLXVYG.cjs.map +0 -1
  119. package/dist/chunk-KGDGYRBL.cjs.map +0 -1
  120. package/dist/chunk-KOFA54C7.js.map +0 -1
  121. package/dist/chunk-NGFTSRKO.cjs.map +0 -1
  122. package/dist/chunk-NLTU5JFD.js.map +0 -1
  123. package/dist/chunk-NZM3M5IR.cjs.map +0 -1
  124. package/dist/chunk-OWV7VRMQ.js.map +0 -1
  125. package/dist/chunk-QRT2W5N5.js.map +0 -1
  126. package/dist/chunk-RCMYEWBE.js +0 -11
  127. package/dist/chunk-RCMYEWBE.js.map +0 -1
  128. package/dist/chunk-SEQTGOOU.js.map +0 -1
  129. package/dist/chunk-SXU26SFO.cjs.map +0 -1
  130. package/dist/chunk-TEAVPUMX.js.map +0 -1
  131. package/dist/chunk-UXV3V5NH.js.map +0 -1
  132. package/dist/chunk-WKA6FUR4.cjs.map +0 -1
  133. package/dist/chunk-XAIJNGTU.cjs.map +0 -1
  134. package/dist/chunk-XHF376ES.js.map +0 -1
  135. package/dist/chunk-XUQ4KRTT.cjs.map +0 -1
  136. package/dist/chunk-YIYMBCPV.js.map +0 -1
  137. package/dist/chunk-ZME5FMED.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/ActivitySequence/ActivitySequence.tsx"],"names":["useRef","useEffect","stopCaptureGroup","jsx","SequenceSlotContext","useLkStrings","useState","isItemGroup","randomSessionId","useMemo","flattenSequence","takeOf","mintTake","next","resolvePlaybackPolicy","slotMediaKey","stimulusMediaKey","jsxs","Fragment","MultipleChoice","FillInTheBlanks","GapSelect","Dictation","ReadAloud","WrittenResponse","StimulusPanel"],"mappings":"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","file":"chunk-2X2NZF46.cjs","sourcesContent":["'use client';\n\nimport {\n type ActivityMedia,\n type ActivityResult,\n flattenSequence,\n type InteractionEvent,\n type InteractionKind,\n type ItemOutcome,\n isItemGroup,\n type LearnerResponse,\n type RecordingRef,\n resolvePlaybackPolicy,\n type SequenceEntry,\n type SequenceSlot,\n type SequenceSlotGroup,\n type SpeechAssessment,\n slotMediaKey,\n stimulusMediaKey,\n type ThemeTokens,\n} from '@intellectif/lk-core';\nimport { type ComponentType, useEffect, useMemo, useRef, useState } from 'react';\nimport type { LearnerAi } from '../../ai/LkAiProvider.js';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport type { LkStringsOverride } from '../../i18n/strings.js';\nimport { randomSessionId } from '../_internal.js';\nimport { Dictation } from '../Dictation/index.js';\nimport { FillInTheBlanks } from '../FillInTheBlanks/index.js';\nimport { GapSelect } from '../GapSelect/index.js';\nimport { MultipleChoice } from '../MultipleChoice/index.js';\nimport { ReadAloud } from '../ReadAloud/index.js';\nimport type { RecordingBinding } from '../ReadAloud/ReadAloud.js';\nimport { StimulusPanel } from '../StimulusPanel/index.js';\nimport { stopCaptureGroup } from '../shared/capture-registry.js';\nimport {\n mintTake,\n type SequenceSlotChannel,\n SequenceSlotContext,\n type TakeState,\n takeOf,\n} from '../shared/sequence-slot.js';\nimport type {\n ActivityProps,\n HtmlSanitizer,\n MediaBudgetBinding,\n RenderableActivity,\n RenderMode,\n SequenceMediaBudget,\n SequenceRecordingBinding,\n SequenceRecordingSlot,\n} from '../types.js';\nimport { WrittenResponse, type WrittenResponseSubmission } from '../WrittenResponse/index.js';\n\n/**\n * What one finished slot in a sequence produced. Two kinds, because two kinds\n * of activity exist: those the SDK scores at submit time, and those a grader\n * scores later. Collapsing them would mean inventing a score for ungraded\n * work — the exact defect the deferred outcome exists to prevent.\n *\n * **A read-aloud hand-in adds no arm here, deliberately.** The obvious home for\n * a handed-in take is `submitted`, and it is the wrong one: that arm's payload\n * is typed `WrittenResponseSubmission`, this union is published from both the\n * barrel and the `components/ActivitySequence` subpath, and a consumer narrows\n * on `kind === 'submitted'` and reads `.text`. Widening the payload to a union\n * would break every one of those reads — a breaking change to a published\n * discriminated union, bought for a type that `responded` already describes: a\n * read-aloud hand-in IS a raw `LearnerResponse` with no grade, which is exactly\n * what `responded` carries.\n *\n * What a read-aloud does add is `unsubmitted`, for the one thing no existing\n * arm can say honestly: a take that never reached storage. Any response would\n * be a lie about it — a `recording` of `null` is the blank a learner chooses,\n * and grades as 0.\n */\nexport type SequenceItemOutcome =\n | {\n kind: 'scored';\n /** Presented position. */\n index: number;\n /** Slot identity from `flattenSequence` — stable under shuffling; the `slotId` to score with. */\n slotId: string;\n activityId: string;\n result: ActivityResult;\n /**\n * The answer this score grades, for a slot whose grade arrives after its\n * answer was handed in and whose learner may hand in another: a\n * `practice` read-aloud, whose `response.recording` names the take the\n * score belongs to. Absent for every other slot, whose score and answer\n * arrive together.\n */\n response?: LearnerResponse;\n }\n | {\n kind: 'submitted';\n index: number;\n slotId: string;\n activityId: string;\n /** Ungraded submission; the grade arrives asynchronously. */\n submission: WrittenResponseSubmission;\n }\n | {\n kind: 'restored';\n index: number;\n slotId: string;\n activityId: string;\n /**\n * The answer as restored, when one was. A slot can be submitted with no\n * stored response (a blank the learner committed), so this is optional —\n * inventing one would be worse than admitting it is absent.\n */\n response?: LearnerResponse;\n }\n | {\n kind: 'responded';\n index: number;\n slotId: string;\n activityId: string;\n /**\n * The raw answer, with no grade of any kind. This is what an `exam` slot\n * produces: the client is forbidden to score, so a response is all there\n * is until the server grades it.\n *\n * A read-aloud `recording` of `null` here is always a blank the learner\n * chose to hand in, never a take that failed to upload — that is\n * `unsubmitted`.\n */\n response: LearnerResponse;\n }\n | {\n /**\n * A question the learner tried to answer and could not hand in, and then\n * left: a `practice` read-aloud whose take never reached your storage. It\n * carries no response, because there is none.\n *\n * Deliberately not `responded` with a `recording` of `null`: that is the\n * blank a learner chooses, and a grader marks it 0 with every word\n * omitted — which would turn a storage outage into a zero. Grade nothing\n * for this slot; the learner can still return to it, and a take stored\n * then reaches you through `onSubmit`.\n */\n kind: 'unsubmitted';\n index: number;\n slotId: string;\n activityId: string;\n };\n\n/**\n * Where a question sits in the set, and the calls that tell the pager what the\n * learner did with it — handed to a `renderers` override as its `question`\n * prop, beside the shared {@link ActivityProps}.\n *\n * The SDK's own components make the same calls, so a question a host draws\n * counts exactly as one the SDK draws: in `onFinished`, in whether the set is\n * complete, and in what a resumed attempt reopens. Every call here keeps ONE\n * identity for the life of the set, so it is safe in an effect's dependencies,\n * and does nothing once the set has changed or the pager has gone.\n */\nexport interface SequenceQuestion {\n /**\n * The identity to store this question's answer against — the same object\n * `onSubmit` and `onActivityComplete` are handed, for the life of the set.\n * `slotId`, never `index`: the position moves under shuffling.\n */\n slot: SequenceRecordingSlot;\n /**\n * `true` while this is the question on screen. `false` from the moment the\n * learner pages away, and `true` again when they come back.\n *\n * WHEN IT TURNS `false`, STOP THE MICROPHONE, ANY TIMER AND ANY SPEECH. The\n * pager pauses the `<audio>` and `<video>` elements inside the question and\n * stops the recorders the SDK started, but it cannot reach a recorder it did\n * not create.\n */\n active: boolean;\n /**\n * An element inside this question's own pane, for popovers, tooltips and\n * dialogs. Portal into it rather than into `document.body`: a pane the\n * learner has paged away from is hidden, and anything portalled out of it\n * stays on screen over the next question — and in fullscreen, only the\n * subtree being painted is visible at all.\n *\n * It is the same element for as long as the set is on screen, and is `null`\n * only before the first commit.\n */\n portalContainer: HTMLElement | null;\n /**\n * The learner withdrew their answer — a \"Try again\". The question counts as\n * unanswered again and its outcome is forgotten, so the set is not complete\n * until it is answered again. Calls no host callback: the host already knows.\n */\n clear(): void;\n /**\n * Work is on its way for this question — an upload, an assessment, a save.\n * While any question is pending, the set is not reported: `onFinished` and\n * `onComplete` wait for it. Paging does not wait.\n *\n * Call `setPending(false)` however the work ends, a failure included.\n */\n setPending(pending: boolean): void;\n /** Forwards to `onInteraction`, with this question's `activityId` and the time filled in. */\n emit(type: InteractionKind, payload?: Record<string, unknown>): void;\n}\n\n/**\n * A component that can render one activity inside a sequence. Register one per\n * activity `type` to put a consumer-defined type on screen — the React half of\n * the activity-type registry, matching `registerActivityType` in lk-core.\n *\n * It receives the shared {@link ActivityProps} and one prop beyond them:\n * `question`, the pager's half of the contract ({@link SequenceQuestion}). A\n * renderer written as `ComponentType<ActivityProps>` still fits and simply\n * ignores it.\n */\nexport type ActivityRenderer = ComponentType<ActivityProps & { question?: SequenceQuestion }>;\n\n/** A slot as its own calls know it: its identity, plus the type that decides how an answer is recorded. */\ninterface SlotIdentity extends SequenceRecordingSlot {\n type: string;\n}\n\n/** One slot's calls, made once per slot per set. */\ninterface SlotCalls {\n slot: SequenceRecordingSlot;\n onSubmit: (response: LearnerResponse) => void;\n onComplete: (result: ActivityResult) => void;\n clear: () => void;\n setPending: (pending: boolean) => void;\n emit: (type: InteractionKind, payload?: Record<string, unknown>) => void;\n}\n\nexport interface ActivitySequenceProps {\n /**\n * The ordered set of entries to present, one question at a time: loose\n * activities, and item groups — a shared passage, recording or image with\n * the questions that refer to it. A group's stimulus stays on screen\n * alongside each of its questions.\n *\n * Accepts `redact()` projections. For data straight off a server, pass it\n * through `asRenderableSequence()` — see that helper for why a cast is\n * unavoidable there — and set `renderMode=\"exam\"`.\n */\n activities: readonly SequenceEntry<RenderableActivity>[];\n /**\n * Renderers for activity types beyond the built-ins, keyed by `data.type`.\n * A key matching a built-in overrides it, so a consumer can replace the\n * bundled renderer without forking the sequencer.\n *\n * A registered renderer is an {@link ActivityRenderer}: it receives the\n * shared prop contract, plus `question` — the pager's half of the contract\n * ({@link SequenceQuestion}), which tells it when its question is on screen,\n * holds the set while its work is in flight, and gives it somewhere to\n * portal a popover. `shuffleSeed`, `recordingBinding`, `assessment` and\n * `workletUrl` are outside both and reach no override: a replacement for\n * `read-aloud` has to be given its own binding by whoever wrote it.\n */\n renderers?: Readonly<Record<string, ActivityRenderer>>;\n /**\n * Called whenever an individual activity is scored at submit time — the\n * incremental persist hook.\n *\n * `slotId` is the identity to store the result against, NOT `index`.\n * `index` is where the question was PRESENTED, which is not stable: it moves\n * under shuffling, and it already differs from the slot identity whenever a\n * group is present (the second entry of a sequence is presented at index 1\n * but is slot `\"1.0\"` if it is a group's first question). `composeAssessmentScore`\n * scores by `slotId`, so persisting `index` cannot be reconciled with it.\n */\n onActivityComplete?: (result: ActivityResult, index: number, slotId: string) => void;\n /**\n * Called on every submit with the learner's raw response and the identity of\n * the slot that produced it — before any grading, in every render mode.\n *\n * **This is the only response channel an `exam` sequence has.** In `exam`\n * mode the components never grade, so `onActivityComplete` cannot fire and\n * `onComplete` never will; without this prop nothing a learner submitted\n * would reach the consumer at all. Persist `response` against `slotId`.\n */\n onSubmit?: (\n response: LearnerResponse,\n slot: { slotId: string; index: number; activityId: string },\n ) => void;\n /**\n * Called once every activity has been completed, with the scored results.\n *\n * Fires only when EVERY item produced a score. A set containing a\n * deferred-graded activity (a written response) can never satisfy that, so\n * use `onFinished` for mixed sets — it is the general completion signal and\n * reports both kinds of outcome. It also carries `slotId` per item, which\n * this callback's bare array cannot.\n *\n * It fires straight after `onFinished`, with the same results, or never: a\n * set `onFinished` reported with an ungraded slot is not reported here later,\n * when a re-recorded take is graded.\n */\n onComplete?: (results: ActivityResult[]) => void;\n /**\n * Called once every activity has been completed, whether it was scored at\n * submit time or submitted for later grading. Use this for any set that\n * mixes graded and deferred-graded activities.\n *\n * **Once per set**, and that matters for a `practice` read-aloud, whose grade\n * arrives after the submit that completed the slot. When the last slot is\n * filled while a read-aloud's take is still being stored or assessed, this\n * waits for it and reports the set then — with the grade in place, or with\n * the slot's raw `responded` outcome when the take came back with no grade.\n * A take that never settles is a question still being answered, so the set\n * is not reported while it runs.\n *\n * **A failure the learner can still retry is in flight too.** While a take\n * that failed to upload, or whose assessment failed, is offered \"Try again\"\n * on the question the learner is looking at, the set is not reported: the\n * retry may yet store and grade it. It completes once the learner moves to\n * another question — as the last take the slot stored, or as `unsubmitted`\n * when it never stored one, which is never a blank.\n *\n * **A score and a response always describe the same take.** A read-aloud\n * recorded again replaces the earlier take's grade with the new take's\n * `responded`, and a result that arrives for a take the learner has since\n * replaced, or for a paper no longer on screen, is not recorded at all.\n *\n * `onComplete` fires in the same report or not at all, so the two never\n * describe one set differently. What arrives after the set was reported is\n * not reported here again, because a consumer who persists on this callback\n * must not write one attempt twice: a later take reaches you through\n * `onSubmit`, and its grade through `onActivityComplete`, the per-item persist\n * hook keyed by `slotId` — which only ever grades the answer `onSubmit` last\n * reported for that slot.\n */\n onFinished?: (items: SequenceItemOutcome[]) => void;\n /** Forwarded to each activity. */\n onInteraction?: (event: InteractionEvent) => void;\n /** Forwarded to each activity. `exam` and `review` disable local scoring. */\n renderMode?: RenderMode;\n /**\n * Binds every budgeted recording in this sequence to a play ledger the\n * consumer persists.\n *\n * The SDK refuses a play; it does not remember one. `plays` seeds the counts\n * at mount (pass `restoreMediaPlayLedger(plan, stored).entries`), and\n * `onPlayConsumed` is how a play becomes durable — see\n * {@link SequenceMediaBudget}.\n */\n mediaBudget?: SequenceMediaBudget;\n /**\n * Where every read-aloud take in this sequence is stored, and how a\n * judgement comes back. Required by any `read-aloud` slot outside `review`:\n * the component refuses to render a recorder whose take is stored nowhere.\n *\n * Named `recordingBinding` rather than `recording` because both\n * `ReadAloudData.recording` (the bounds an item declares) and\n * `ReadAloudLearnerResponse.recording` (the take a learner handed in) already\n * exist — the plain name would read as one of those. Same name at both\n * levels, different types, as `mediaBudget` has.\n */\n recordingBinding?: SequenceRecordingBinding;\n /**\n * Speech assessments keyed by `slotId`, forwarded to the matching read-aloud\n * slot so `review` can show the per-word marks behind a stored grade.\n *\n * Read LIVE, like `outcomes` and unlike `responses`: an assessment is fetched\n * beside the attempt it explains and lands after the first paint, and a\n * mount-only read would show the grade with no marks under it for ever.\n */\n assessments?: Readonly<Record<string, SpeechAssessment>>;\n /**\n * The URL of a self-hosted copy of `CAPTURE_PROCESSOR_SOURCE`, forwarded to\n * every read-aloud slot's recorder, for a Content-Security-Policy whose\n * `script-src` does not allow `blob:`. See `ReadAloudProps.workletUrl`. Like\n * `recordingBinding`, it does not reach a `renderers` override.\n */\n workletUrl?: string;\n /** Overrides the SDK's chrome text for this sequence. See {@link LkIntlProvider}. */\n strings?: LkStringsOverride;\n /**\n * The host's AI ports, for every question this renders: explanations after\n * grading, hints before submit. The same as `LkAiProvider`, for this one\n * sequence, and a `renderers` override receives them as its own `ai` prop —\n * except in `exam`, where nothing AI appears and no question is given them.\n * See {@link LearnerAi}.\n */\n ai?: LearnerAi;\n /**\n * `entries` shuffles the top-level entries; a group moves as one block, and\n * the order INSIDE a group follows the group's own `shuffle` setting.\n * Default `none`: authored order.\n */\n shuffle?: 'none' | 'entries';\n /**\n * Seed for every shuffle in this sequence (`shuffle: 'entries'` and any\n * group with `shuffle: 'within-group'`). Supply the attempt id, so the\n * server's `flattenSequence(entries, { seed })` derives the same order this\n * pager shows.\n *\n * **Required whenever this sequence shuffles** — `shuffle: 'entries'`, or any\n * group with `shuffle: 'within-group'` — in `exam` or `review` mode; omitting\n * it then throws. An order nobody can reproduce cannot be reconciled with a\n * recorded attempt, and failing at render is the only way that mistake\n * surfaces before a learner sits the paper. A sequence that does not shuffle\n * needs no seed in any mode.\n *\n * The guard covers an activity's own `data.shuffle` too, a group's items\n * included: `<MultipleChoice>` would otherwise fall back to a per-mount seed,\n * in every mode.\n *\n * In `practice` mode it stays optional: a random per-mount seed is used,\n * stable within the mount and deliberately not reproducible. That fallback is\n * also not SSR-safe (server and client would invent different orders and\n * hydration would mismatch), so supply a seed for any server-rendered\n * sequence regardless of mode.\n */\n shuffleSeed?: string;\n /**\n * Where to open. Defaults to the first question; pass a stored\n * `AttemptState.index` to reopen an interrupted attempt where it was left.\n * Read at mount only, like any `default*` prop.\n */\n defaultIndex?: number;\n /**\n * Fires whenever the learner moves. Persist it and the next resume reopens\n * on the right question — without it, the pager's position is the one piece\n * of an attempt a consumer cannot recover.\n */\n onIndexChange?: (index: number) => void;\n /**\n * Answers to restore, keyed by `slotId`, seeded into each slot as its\n * `defaultValue`. This is the other half of resume: `AttemptState.responses`\n * goes straight in.\n *\n * Read at mount only. To restore a different attempt, remount with a `key`.\n */\n responses?: Readonly<Record<string, LearnerResponse>>;\n /**\n * Slots the learner had already submitted, from `AttemptState.submittedSlotIds`.\n * Each is mounted already submitted, so a resumed paper does not reopen a\n * locked question as answerable — without this a learner can change and\n * re-submit work they had already committed.\n *\n * Read at mount only, alongside `responses`.\n */\n submittedSlotIds?: readonly string[];\n /**\n * Server-computed outcomes keyed by `slotId`, forwarded to each slot. In\n * `review` mode this is what marks correctness — the client never scores, so\n * without it a review render has nothing to show.\n */\n outcomes?: Readonly<Record<string, ItemOutcome>>;\n /** Renders author-supplied rich text; forwarded to the stimulus panel and every activity. */\n sanitizeHtml?: HtmlSanitizer;\n theme?: Partial<ThemeTokens>;\n /**\n * BCP 47 tag stamped as `lang` on this component's root. This is the\n * INTERFACE language — the SDK's own chrome renders inside that element — so\n * it should carry the same value you give `<LkIntlProvider locale>`. Passing\n * a different one re-declares the language of every SDK string in this\n * subtree without changing the words.\n *\n * It is NOT `data.locale`, which labels xAPI statements — and, on a\n * dictation, places the dictation's own words: `<Dictation>` puts it, and\n * the direction it names, on its title, hints, marks and solution. Other\n * authored content in another language belongs on `stimulus.locale`, which\n * `<StimulusPanel>` puts on the passage alone.\n */\n locale?: string;\n disabled?: boolean;\n}\n\n/** One stimulus panel to keep mounted: the group, and the presented span of its questions. */\ninterface StimulusMount {\n /** Authored entry position — unique per group in the sequence even if two groups share an id. */\n entryKey: string;\n group: SequenceSlotGroup;\n first: number;\n last: number;\n}\n\n/**\n * One slot's key for the life of a set: its pane's React key, and the name its\n * portal node is held under.\n */\nfunction slotKeyOf(setKey: string, slot: SequenceSlot<RenderableActivity>): string {\n return `${setKey}::slot-${slot.slotId}-${slot.activity.id}`;\n}\n\n/** The authored-entry prefix of a slot id: `\"3\"` for `\"3\"` and for `\"3.1\"`. */\nfunction entryKeyOf(slot: SequenceSlot<RenderableActivity>): string {\n return slot.slotId.split('.')[0] ?? slot.slotId;\n}\n\n/** The latest take a slot has been told about, and how far it has got. */\ninterface SlotTake {\n /** A number from `mintTake`: unique on the page, larger for a later take. */\n take: number;\n state: TakeState;\n /** The answer the take was handed in as, once it was stored. */\n response?: LearnerResponse;\n}\n\n/**\n * Names the capture groups of one pager, so a slot's microphone belongs to that\n * pager alone. Per mount, for the reason the playback groups are: slot ids are\n * short and repeat (\"0\", \"1.0\"), so two pagers on one page — a review beside a\n * live attempt — would share a group, and hiding one's first question would\n * stop the other's recorder. The name never reaches the DOM, so a server and\n * the client that hydrates it may mint different ones.\n */\nlet captureScopeCount = 0;\n\n/**\n * A pane that is kept MOUNTED but shown only when it is the current one, and\n * that stops any media it contains on the way out.\n *\n * `hidden` alone is not enough. It resolves to `display: none`, and CSS does\n * not touch playback: an `<audio>` or `<video>` inside a hidden subtree keeps\n * playing to the end. (The HTML spec pauses a media element when it is\n * REMOVED from the document — a different thing, and the thing this pager\n * deliberately does not do, because unmounting is what used to destroy the\n * learner's answers.) So a recording started for one question would carry on\n * underneath the next, which for a listening paper means the passage plays\n * while the learner is somewhere else entirely.\n *\n * Pausing preserves `currentTime`: coming back to a group resumes where the\n * learner left off. Nothing auto-plays on the way in — starting audio the\n * learner did not ask for is its own defect.\n *\n * `embed` media (a provider iframe) cannot be paused this way: controlling a\n * third-party player needs its own JS API, which the SDK has no reliable\n * access to because the author supplies the embed URL. Use `audio`/`video`\n * media for anything that must stop when the learner navigates.\n *\n * A microphone is the other way round: it is not an element in this subtree, so\n * no query can find it. A slot pane hands its content a `channel` through\n * `SequenceSlotContext` — never a prop, so nothing on a public component's\n * signature can name it — whose `captureGroup` the pane's recorders join, and\n * hiding the pane stops them: a learner who navigates away mid-take must not\n * leave the microphone live under the next question.\n */\nfunction SequencePane({\n className,\n hidden,\n channel,\n children,\n}: {\n className: string;\n hidden: boolean;\n channel?: SequenceSlotChannel;\n children: React.ReactNode;\n}): React.JSX.Element {\n const ref = useRef<HTMLDivElement>(null);\n const captureGroup = channel?.captureGroup;\n\n useEffect(() => {\n if (!hidden) {\n return;\n }\n if (captureGroup !== undefined) {\n stopCaptureGroup(captureGroup);\n }\n const node = ref.current;\n if (node === null) {\n return;\n }\n for (const media of node.querySelectorAll('audio, video')) {\n const element = media as HTMLMediaElement;\n if (!element.paused) {\n element.pause();\n }\n }\n }, [hidden, captureGroup]);\n\n return (\n <div className={className} hidden={hidden} ref={ref}>\n {channel === undefined ? (\n children\n ) : (\n <SequenceSlotContext.Provider value={channel}>{children}</SequenceSlotContext.Provider>\n )}\n </div>\n );\n}\n\n/**\n * Presents a set of activities as an in-place pager: one question visible at a\n * time, navigated with Prev/Next (no page scrolling). Linear free navigation —\n * the learner may move back and forth; activities keep their own submit and\n * scoring. On navigation, focus moves to the question region and the position\n * is announced via an aria-live region.\n *\n * Item groups are flattened into consecutive questions; the group's stimulus\n * is mounted ONCE, beside the question region rather than inside it, and shown\n * alongside every question in the group — so a passage stays put, and a\n * recording keeps its position, as the learner moves between its questions.\n */\n/**\n * Whether an activity shuffles its own options, and therefore needs a seed of\n * its own. Read structurally: `shuffle` is a per-type content field, not part\n * of the sequence entry contract.\n */\nfunction shufflesItsOwnOptions(entry: unknown): boolean {\n const item = entry as { shuffle?: unknown; shuffleChoices?: unknown } | null;\n // Two content fields, one question: does this item deal an order of its own?\n // `shuffle` is MultipleChoice's; `shuffleChoices` is Gap Select's. A type\n // added without being named here shuffles unseeded in an exam — and one named\n // here but not in the seed guard's message sends its author looking for a\n // shuffle they never wrote.\n return item?.shuffle === true || item?.shuffleChoices === true;\n}\n\nexport function ActivitySequence({\n activities,\n renderers,\n onActivityComplete,\n onComplete,\n onFinished,\n onSubmit,\n onInteraction,\n renderMode = 'practice',\n shuffle,\n shuffleSeed,\n defaultIndex,\n onIndexChange,\n responses,\n submittedSlotIds,\n outcomes,\n sanitizeHtml,\n theme,\n locale,\n disabled,\n mediaBudget,\n recordingBinding,\n assessments,\n workletUrl,\n strings,\n ai,\n}: ActivitySequenceProps): React.JSX.Element {\n const sessionIdRef = useRef<string | null>(null);\n const s = useLkStrings(strings);\n const mediaPlays = mediaBudget?.plays;\n const [captureScope] = useState(() => {\n captureScopeCount += 1;\n return `lk-seq-capture-${captureScopeCount}`;\n });\n\n // The presented order comes from lk-core, never computed here: the server\n // that records an attempt calls the same function with the same seed.\n const shuffleEntries = shuffle === 'entries';\n // Every shuffle this sequence can produce, not just the ones it performs\n // itself. An ACTIVITY's own `data.shuffle` is the third source: MultipleChoice\n // falls back to a per-mount seed when none reaches it, so an unseeded item\n // shuffle used to render happily under `exam` and hand the learner a\n // different option order on every mount — exactly the unreproducible\n // arrangement this guard exists to refuse, arriving through the one door it\n // did not watch.\n const needsSeed =\n shuffleEntries ||\n activities.some((entry) =>\n isItemGroup(entry)\n ? entry.shuffle === 'within-group' || entry.items.some(shufflesItsOwnOptions)\n : shufflesItsOwnOptions(entry),\n );\n\n // An unreproducible order is a practice-only affordance. Under `exam` or\n // `review` the server has to be able to rebuild exactly what the learner\n // saw, so a missing seed is an error rather than something to paper over —\n // the same reason `flattenSequence` refuses to invent one.\n if (needsSeed && shuffleSeed === undefined && renderMode !== 'practice') {\n // Every door `needsSeed` watches, named: a reader whose only shuffle is an\n // item's own field must find that field here. And no single function\n // rebuilds all of these orders — `flattenSequence` deals entries and group\n // items, while an item deals its own options from the same seed — so the\n // message promises the seed, not a function.\n throw new Error(\n `ActivitySequence: renderMode \"${renderMode}\" requires a \\`shuffleSeed\\` when anything ` +\n 'in the sequence shuffles: shuffle=\"entries\", a group with shuffle: \"within-group\", or ' +\n \"an activity's own `data.shuffle` (multiple choice) or `data.shuffleChoices` (gap \" +\n 'select), including an activity inside a group. Pass the attempt id, so the server can ' +\n 'rebuild every order the learner saw from that one seed.',\n );\n }\n\n // Lazily created only when shuffling in practice without a caller seed (same\n // contract as MultipleChoice.shuffleSeed): a per-mount session seed, stable\n // across re-renders, never reproducible.\n const seed = needsSeed\n ? // biome-ignore lint/suspicious/noAssignInExpressions: sanctioned lazy ref initialization\n (shuffleSeed ?? (sessionIdRef.current ??= randomSessionId()))\n : undefined;\n const slots = useMemo(\n () => flattenSequence(activities, { shuffleEntries, ...(seed !== undefined ? { seed } : {}) }),\n [activities, shuffleEntries, seed],\n );\n\n // Clamped rather than trusted: a stored position from a paper that has since\n // lost its last question would open the pager on a slot that is not there,\n // which renders as an empty shell with no way forward.\n const [index, setIndex] = useState(() => {\n // NaN passes through Math.min/Math.max unchanged, so it survived every\n // clamp and indexed the slots with NaN — the pager then rendered the empty\n // shell this clamp exists to prevent. `NaN` is a `number`, so the prop type\n // gives no protection, and `Number(row.last_index)` on a NULL column\n // produces exactly that.\n const requested = Math.trunc(defaultIndex ?? 0);\n if (!Number.isFinite(requested)) {\n return 0;\n }\n return Math.min(Math.max(0, requested), Math.max(0, slots.length - 1));\n });\n // Outcomes live in a ref, not state: nothing renders from them, and a ref is\n // written SYNCHRONOUSLY. Reading them from a `useState` closure meant two\n // slots completing in the same tick both saw the pre-update array — the\n // second overwrote the first, so one answer vanished and completion never\n // fired. Every slot is mounted at once here, so same-tick completions are\n // reachable (a custom renderer that completes from a mount effect).\n const outcomesRef = useRef<(SequenceItemOutcome | null)[] | null>(null);\n // The slot each position of `outcomesRef` belongs to, assigned wherever that\n // array is created so the two can never describe different papers. A ref,\n // not the `slots` a closure saw: an outcome that settles late arrives through\n // a closure from the render that started it, and only a ref can tell it what\n // is at its position NOW — see `record`.\n const outcomeSlotsRef = useRef<readonly SequenceSlot<RenderableActivity>[] | null>(null);\n // The latest take each `practice` read-aloud slot has been told about, keyed\n // by position — see `record`. A ref for the reason the outcomes are one: a\n // submit, the grade after it and the report all read it inside one tick.\n const takesRef = useRef<Map<number, SlotTake>>(new Map());\n // Which set the outcomes describe: bumped on every set change, and captured\n // by every callback a render hands a slot. A result that arrives through a\n // callback from an earlier set is refused, whatever slot and activity ids the\n // new set reuses — ids a host chooses, and a loading blip or a paper that\n // shares an item repeats them exactly.\n const setGenerationRef = useRef(0);\n // The question on screen, as of the last commit: leaving a question is what\n // completes a failure on it that was being offered a retry, and a report made\n // on a position the screen does not show yet would describe a question the\n // learner is still looking at. Written by the effect below `go`.\n const indexRef = useRef(index);\n // Whether this set has been reported. One flag for both whole-set callbacks,\n // because they are one report: a set can be filled again after it reported —\n // a practice read-aloud recorded again replaces its own outcome — and a\n // consumer who persists on either callback must not be handed one attempt\n // twice, nor be told by `onComplete` later what `onFinished` reported\n // differently.\n const reportedRef = useRef(false);\n // The channel each slot pane hands its content, one per slot per set, so it\n // keeps one identity for as long as its set is on screen.\n const channelsRef = useRef<Map<string, SequenceSlotChannel>>(new Map());\n // The calls each slot makes, on the same terms and for the same reason: a\n // host renderer may hold them in an effect, and a new identity each render\n // would re-run it.\n const callsRef = useRef<Map<string, SlotCalls>>(new Map());\n // Questions whose host says work is on its way, by presented position. The\n // set is not reported while any of them is pending — the pager's own\n // read-aloud takes are tracked separately, by take, in `takesRef`.\n const hostPendingRef = useRef<Set<number>>(new Set());\n // The element each host-drawn question portals into, and the ref callbacks\n // that collect them. The callbacks are made once per slot: a new one each\n // render would detach and re-attach on every commit.\n const [portals, setPortals] = useState<ReadonlyMap<string, HTMLElement>>(() => new Map());\n const portalRefsRef = useRef<Map<string, (node: HTMLDivElement | null) => void>>(new Map());\n // The mode this pager is delivering in, as of the latest render: a channel\n // reads it through this ref rather than holding a copy, so a set switched\n // from practice to exam in place is an exam at once.\n const modeRef = useRef<RenderMode>(renderMode);\n modeRef.current = renderMode;\n // What a channel calls, as of the latest render — assigned below `updateTake`.\n const latestRef = useRef<{\n updateTake: (generation: number, at: number, take: number, state: TakeState) => void;\n reportIfFinished: () => void;\n submitSlot: (generation: number, at: SlotIdentity, response: LearnerResponse) => void;\n completeSlot: (generation: number, at: SlotIdentity, result: ActivityResult) => void;\n clearSlot: (generation: number, at: SlotIdentity) => void;\n hostPending: (generation: number, at: SlotIdentity, pending: boolean) => void;\n emit: (\n generation: number,\n at: SlotIdentity,\n type: InteractionKind,\n payload: Record<string, unknown> | undefined,\n ) => void;\n } | null>(null);\n // Cleared as this pager unmounts. A slot's own teardown tells the pager its\n // take is over, and a pager on its way out must not report a set on the\n // strength of that.\n const aliveRef = useRef(true);\n useEffect(() => {\n aliveRef.current = true;\n return () => {\n aliveRef.current = false;\n };\n }, []);\n if (outcomesRef.current === null) {\n // Slots the learner had already submitted are seeded as `restored`. They\n // will not submit again — they mount locked — so leaving them null meant\n // `onFinished` waited forever on outcomes that could never arrive, and a\n // resumed attempt could never signal completion however many of the\n // remaining questions the learner answered. Seeding fires no callback: the\n // attempt was already this far along before this mount existed.\n const submittedAtMount = new Set(submittedSlotIds ?? []);\n outcomesRef.current = slots.map((slot) =>\n submittedAtMount.has(slot.slotId)\n ? {\n kind: 'restored' as const,\n index: slot.index,\n slotId: slot.slotId,\n activityId: slot.activity.id,\n ...(responses !== undefined && Object.hasOwn(responses, slot.slotId)\n ? { response: responses[slot.slotId] as LearnerResponse }\n : {}),\n }\n : null,\n );\n outcomeSlotsRef.current = slots;\n }\n const regionRef = useRef<HTMLDivElement>(null);\n const mountedRef = useRef(false);\n\n // Reset pager position and outcomes when the presented SET changes (B8):\n // without this, results from the previous set leak into the new one and can\n // fire completion with a mixed old/new array. Keyed on the set's CONTENT\n // identity (ordered slot and activity ids), not the array reference — a\n // parent that re-creates a structurally identical array on every render\n // (inline literals, .map() in render) must not wipe in-progress answers.\n // JSON.stringify (not join) so ids containing the separator cannot collide.\n const setKey = JSON.stringify(slots.map((slot) => [slot.slotId, slot.activity.id]));\n const [prevSetKey, setPrevSetKey] = useState(setKey);\n // The set this component mounted with. `responses` seeds slots BY slotId,\n // and slot ids are short and repeat across papers (\"0\", \"1.0\"), so applying\n // them after the set changed would drop one paper's answers under another\n // paper's questions — a review modal stepping to the next attempt did\n // exactly that. Seeding stops at the first set change; to show a different\n // attempt, remount with a `key`, which is what \"read at mount only\" means.\n // `mediaBudget.resumeKey` participates so an invigilator can hand a play\n // back — re-seeding the budgets in place — without remounting the pager and\n // costing the learner their focus, their scroll position and an unsaved\n // answer.\n const seedKey = `${setKey}::${mediaBudget?.resumeKey ?? ''}`;\n const mountSetKeyRef = useRef(setKey);\n const mountSeedKeyRef = useRef(seedKey);\n const seedsApply = mountSetKeyRef.current === setKey;\n const mediaSeedsApply = mountSeedKeyRef.current === seedKey || seedsApply;\n if (mountSeedKeyRef.current !== seedKey) {\n mountSeedKeyRef.current = seedKey;\n }\n const submitted = useMemo(() => new Set(submittedSlotIds ?? []), [submittedSlotIds]);\n if (prevSetKey !== setKey) {\n setPrevSetKey(setKey);\n setIndex(0);\n // At once, not in the effect: a new set has no question the learner was on,\n // and the effect does not run at all when the position was already 0.\n indexRef.current = 0;\n outcomesRef.current = slots.map(() => null);\n outcomeSlotsRef.current = slots;\n takesRef.current = new Map();\n setGenerationRef.current += 1;\n channelsRef.current = new Map();\n callsRef.current = new Map();\n hostPendingRef.current = new Set();\n portalRefsRef.current = new Map();\n reportedRef.current = false;\n }\n const setGeneration = setGenerationRef.current;\n\n // After a navigation (not the initial mount) move focus to the question\n // region so screen-reader / keyboard users land on the new question.\n // biome-ignore lint/correctness/useExhaustiveDependencies: index is the intended trigger\n useEffect(() => {\n if (mountedRef.current) {\n regionRef.current?.focus();\n } else {\n mountedRef.current = true;\n }\n }, [index]);\n\n // Report where the pager actually IS, from an effect rather than from the\n // navigation handler. Two positions the learner never chose are still\n // positions a consumer has to persist: a `defaultIndex` that was clamped\n // (stored 99, showing 2) and the reset a set change performs. Reporting only\n // clicks left storage disagreeing with the screen, and the next autosave\n // then wrote a position the plan does not have.\n const reportedIndexRef = useRef<number | null>(null);\n useEffect(() => {\n if (reportedIndexRef.current === index) {\n return;\n }\n const first = reportedIndexRef.current === null;\n reportedIndexRef.current = index;\n // On mount, stay silent unless the requested position was corrected.\n if (first && index === Math.trunc(defaultIndex ?? 0)) {\n return;\n }\n onIndexChange?.(index);\n }, [index, defaultIndex, onIndexChange]);\n\n // The learner has moved, and the screen shows where to. Leaving a question\n // can complete a failure on it that was being offered a retry — see\n // `reportIfFinished` — so the set may be reportable now.\n useEffect(() => {\n if (indexRef.current === index) {\n return;\n }\n indexRef.current = index;\n latestRef.current?.reportIfFinished();\n }, [index]);\n\n // One panel per group, spanning the presented positions of its questions.\n const stimulusMounts = useMemo<StimulusMount[]>(() => {\n const mounts = new Map<string, StimulusMount>();\n for (const slot of slots) {\n if (slot.group === undefined) {\n continue;\n }\n const entryKey = entryKeyOf(slot);\n const existing = mounts.get(entryKey);\n if (existing === undefined) {\n mounts.set(entryKey, { entryKey, group: slot.group, first: slot.index, last: slot.index });\n } else {\n existing.last = slot.index;\n }\n }\n return [...mounts.values()];\n }, [slots]);\n\n const total = slots.length;\n const current = slots[index];\n\n const go = (next: number): void => {\n setIndex(next);\n };\n\n /**\n * Fires the whole-set callbacks — once, together — when every slot is done\n * and no take is still on its way.\n *\n * **A take on its way holds the report.** A practice read-aloud records its\n * `responded` outcome the moment its take is stored, and its grade can land\n * after the last slot was filled; reporting then handed `onFinished` the raw\n * outcome, and the grade a moment later could never reach it. So a slot\n * whose latest take is being stored or judged is not done. The take states\n * come from the component itself, through the slot's channel, and every way a\n * take can end reports an end — a grade, a take with no grade, a failure\n * nobody can retry, an unmount — so a take that settles never holds a set.\n *\n * **So does a failure the learner can still retry, on the question in front\n * of them.** Reporting there reported a set whose answer \"Try again\" was about\n * to change: the retry stored and graded the take, and `onComplete` then\n * described a different set from the `onFinished` before it. Once the learner\n * moves to another question the failure no longer holds the report: the slot\n * completes as whatever it last stored, or as `unsubmitted` when that is\n * nothing — never as a `recording` of `null`, which is the blank a learner\n * chooses and a grader marks 0. `unsubmitted` is a `practice` outcome only:\n * an exam question with no stored take is unanswered, and the learner has a\n * blank to hand in if that is their answer.\n */\n const reportIfFinished = (): void => {\n const held = outcomesRef.current;\n const presented = outcomeSlotsRef.current;\n if (held === null || presented === null || reportedRef.current) {\n return;\n }\n // A host's question says work is on its way for it: an upload, a save, a\n // judgement. Reporting now would describe the set as the learner left it a\n // moment before it settled.\n if (hostPendingRef.current.size > 0) {\n return;\n }\n const items: SequenceItemOutcome[] = [];\n for (const [at, outcome] of held.entries()) {\n const take = takesRef.current.get(at);\n if (take?.state === 'in-flight' || (take?.state === 'retryable' && at === indexRef.current)) {\n return;\n }\n const slot = presented[at];\n if (outcome !== null) {\n items.push(outcome);\n } else if (take?.state === 'retryable' && renderMode === 'practice' && slot !== undefined) {\n items.push({\n kind: 'unsubmitted',\n index: at,\n slotId: slot.slotId,\n activityId: slot.activity.id,\n });\n } else {\n return;\n }\n }\n reportedRef.current = true;\n onFinished?.(items);\n\n // `onComplete` predates deferred grading and promises ActivityResult[].\n // Fire it only when every slot really was scored, rather than inventing a\n // result for work nobody has graded — and only here, beside `onFinished`.\n const scored: ActivityResult[] = [];\n for (const item of items) {\n if (item.kind !== 'scored') {\n return;\n }\n scored.push(item.result);\n }\n onComplete?.(scored);\n };\n\n /**\n * Records a finished slot and reports the set when it is done — the one place\n * a slot's outcome is decided.\n *\n * **An outcome must come from the set on screen.** A grade can settle after\n * the paper changed — an assessment held across a swap of `activities`, or a\n * consumer's own renderer grading asynchronously — and it arrives through a\n * callback from the render that started it. Accepted, it landed in whatever\n * slot of the NEW paper had its index, and could report that paper fully\n * scored though nobody had answered it. Ids cannot tell the papers apart: a\n * new paper can reuse both the slot id and the activity id, and a loading\n * blip reuses every id there is. `generation` can: it is the set the callback\n * was handed out for, and it is refused unless that set is still current.\n *\n * **A slot that already holds an outcome is FINAL**: the first outcome wins,\n * so a renderer that reports twice keeps what it reported first. There is one\n * exception.\n *\n * **The exception is a `practice` read-aloud, and it is decided by `take`.**\n * That slot records `responded` when a take is stored, because a take that\n * comes back unscorable produces no grade at all and the slot would otherwise\n * stay `null` for ever. Each take has a number (`mintTake`), unique on the page\n * and larger for a later take, carried by everything the component reports\n * about it. The invariant:\n *\n * **The score and the response a slot holds always describe the same take.**\n *\n * - A `responded` for a newer take replaces whatever the slot holds, a grade\n * included: `onSubmit` has already told the host about that take, so a\n * grade kept for the older one would sit beside a response it does not\n * grade.\n * - A `scored` lands only for the slot's latest take, once, and replaces that\n * take's `responded`. A grade for any other take — one the learner has\n * since replaced, even one still in flight when they did — is dropped\n * rather than filed against a take it did not grade.\n * - A `restored` slot is never replaced: it was submitted before this mount,\n * and a resumed attempt must not be re-reported as if it had just been sat.\n *\n * A grade therefore lands once per take, and `onActivityComplete` — the\n * per-item hook, keyed by `slotId` — only ever grades the answer `onSubmit`\n * last reported for that slot. The whole-set callbacks stay once per set\n * whatever is replaced — see `reportedRef`.\n */\n const record = (outcome: SequenceItemOutcome, generation: number, take?: number): void => {\n const held = outcomesRef.current;\n if (held === null || generation !== setGenerationRef.current) {\n return;\n }\n const at = outcome.index;\n const holding = held[at];\n let accepted = outcome;\n if (take === undefined) {\n if (holding != null) {\n return;\n }\n } else {\n if (holding?.kind === 'restored') {\n return;\n }\n const latest = takesRef.current.get(at);\n if (outcome.kind === 'responded') {\n // Older than the latest take, or a second answer for the same one.\n if (\n latest !== undefined &&\n (take < latest.take || (take === latest.take && latest.response !== undefined))\n ) {\n return;\n }\n takesRef.current.set(at, {\n take,\n // A take this pager hears of first through its answer — a consumer's\n // renderer has no channel — has nothing on its way.\n state: latest?.take === take ? latest.state : 'settled',\n response: outcome.response,\n });\n } else if (outcome.kind === 'scored') {\n // Only the latest take's grade, only once it was stored, and only once.\n if (latest?.take !== take || latest.response === undefined || holding?.kind === 'scored') {\n return;\n }\n accepted = { ...outcome, response: latest.response };\n } else {\n return;\n }\n }\n const next = held.slice();\n next[at] = accepted;\n // Commit before firing anything: a second record() in the same tick must\n // see this one.\n outcomesRef.current = next;\n\n if (accepted.kind === 'scored') {\n onActivityComplete?.(accepted.result, accepted.index, accepted.slotId);\n }\n reportIfFinished();\n };\n\n /**\n * The learner committed an answer to a slot: the body of the `onSubmit` every\n * question is handed, the SDK's own components and a host's renderer alike.\n */\n const submitSlot = (generation: number, at: SlotIdentity, response: LearnerResponse): void => {\n // A submit through a callback handed out for an earlier set is a\n // consumer renderer's late one, for a question no longer on screen: the\n // host would file it against whatever slot reuses these ids now.\n if (generation !== setGenerationRef.current) {\n return;\n }\n onSubmit?.(response, { slotId: at.slotId, index: at.index, activityId: at.activityId });\n // Written responses are excluded in every mode because they report\n // something richer through `onSubmitted` a moment later, and the first\n // outcome wins.\n if (at.type === 'written-response') {\n return;\n }\n // Outside `practice` the components do not grade, so a raw response is\n // the ONLY outcome this slot will ever produce — record it, or the set\n // could never complete and `onFinished` would be dead in exam mode.\n if (renderMode !== 'practice') {\n record(\n {\n kind: 'responded',\n index: at.index,\n slotId: at.slotId,\n activityId: at.activityId,\n response,\n },\n generation,\n );\n return;\n }\n // A `practice` read-aloud is the one type that can submit and then\n // produce no score: an unscorable take, an assessor that failed, or a\n // binding that stores without judging all end the attempt with nothing\n // to grade, and `onComplete` fires only on a grade. Recorded for its\n // take, so the grade that may still arrive for that take replaces it —\n // and a take recorded again replaces the one before, grade and all.\n if (at.type === 'read-aloud') {\n record(\n {\n kind: 'responded',\n index: at.index,\n slotId: at.slotId,\n activityId: at.activityId,\n response,\n },\n generation,\n takeOf(response) ?? mintTake(),\n );\n }\n };\n\n /** A slot produced a grade: the body of the `onComplete` every question is handed. */\n const completeSlot = (generation: number, at: SlotIdentity, result: ActivityResult): void => {\n const takesAnswers = renderMode === 'practice' && at.type === 'read-aloud';\n record(\n { kind: 'scored', index: at.index, slotId: at.slotId, activityId: at.activityId, result },\n generation,\n // The take the grade is for: stamped by `<ReadAloud>`, and for a\n // consumer's renderer, which cannot stamp one, the slot's latest.\n takesAnswers ? (takeOf(result) ?? takesRef.current.get(at.index)?.take) : undefined,\n );\n };\n\n /**\n * A host's question withdrew its answer. The slot holds nothing again — its\n * outcome and, with it, the take any grade would be filed against — so the\n * set is unfinished until it is answered again.\n *\n * A set already reported stays reported: the whole-set callbacks fire once,\n * and a consumer who persists on them must not be handed one attempt twice.\n */\n const clearSlot = (generation: number, at: SlotIdentity): void => {\n const held = outcomesRef.current;\n if (!aliveRef.current || held === null || generation !== setGenerationRef.current) {\n return;\n }\n takesRef.current.delete(at.index);\n if (held[at.index] == null) {\n return;\n }\n const next = held.slice();\n next[at.index] = null;\n outcomesRef.current = next;\n };\n\n /** A host's question is waiting on work of its own, or has stopped waiting. */\n const hostPending = (generation: number, at: SlotIdentity, pending: boolean): void => {\n if (!aliveRef.current || generation !== setGenerationRef.current) {\n return;\n }\n const waiting = hostPendingRef.current;\n if (pending) {\n waiting.add(at.index);\n return;\n }\n if (!waiting.delete(at.index)) {\n return;\n }\n // The last thing the set was waiting for may just have arrived.\n reportIfFinished();\n };\n\n /** A host's question reported an interaction: its activity and the moment, filled in. */\n const emitFor = (\n generation: number,\n at: SlotIdentity,\n type: InteractionKind,\n payload: Record<string, unknown> | undefined,\n ): void => {\n if (!aliveRef.current || generation !== setGenerationRef.current) {\n return;\n }\n onInteraction?.({\n type,\n activityId: at.activityId,\n timestamp: Date.now(),\n payload: payload ?? {},\n });\n };\n\n /**\n * What a slot's channel reports: the state of one of its takes. A take older\n * than the slot's latest has been replaced, and a report from a set that is\n * no longer current, or reaching a pager that is unmounting, describes\n * nothing on screen.\n */\n const updateTake = (generation: number, at: number, take: number, state: TakeState): void => {\n if (!aliveRef.current || generation !== setGenerationRef.current) {\n return;\n }\n const latest = takesRef.current.get(at);\n if (latest !== undefined && take < latest.take) {\n return;\n }\n // A newer take starts with no answer of its own: the response the slot\n // holds belongs to the take before it until this one is stored.\n takesRef.current.set(at, latest?.take === take ? { ...latest, state } : { take, state });\n reportIfFinished();\n };\n // Channels and slot calls keep one identity for a whole set, so they call\n // through this ref and always reach the latest render's props.\n latestRef.current = {\n updateTake,\n reportIfFinished,\n submitSlot,\n completeSlot,\n clearSlot,\n hostPending,\n emit: emitFor,\n };\n\n /**\n * The calls one slot makes, made once per slot per set. They reach the latest\n * render through `latestRef`, so they can keep one identity while the props\n * they report to change — what a host renderer needs to hold them in an\n * effect, and what the pager needs so a host that re-renders on every\n * keystroke does not re-run one.\n */\n const callsFor = (slot: SequenceSlot<RenderableActivity>): SlotCalls => {\n const key = `${slot.index}::${slot.slotId}`;\n let calls = callsRef.current.get(key);\n if (calls === undefined) {\n const generation = setGeneration;\n const at: SlotIdentity = {\n slotId: slot.slotId,\n index: slot.index,\n activityId: slot.activity.id,\n type: slot.activity.type,\n };\n calls = {\n slot: { slotId: at.slotId, index: at.index, activityId: at.activityId },\n onSubmit: (response) => latestRef.current?.submitSlot(generation, at, response),\n onComplete: (result) => latestRef.current?.completeSlot(generation, at, result),\n clear: () => latestRef.current?.clearSlot(generation, at),\n setPending: (pending) => latestRef.current?.hostPending(generation, at, pending === true),\n emit: (type, payload) => latestRef.current?.emit(generation, at, type, payload),\n };\n callsRef.current.set(key, calls);\n }\n return calls;\n };\n\n /**\n * Where a host-drawn question portals: a node inside its own pane, so it is\n * hidden with the question it belongs to and painted with it in fullscreen.\n * One ref callback per slot, kept, because a new one each render detaches and\n * re-attaches the node on every commit.\n */\n const portalRefFor = (key: string): ((node: HTMLDivElement | null) => void) => {\n let callback = portalRefsRef.current.get(key);\n if (callback === undefined) {\n callback = (node: HTMLDivElement | null) => {\n setPortals((held) => {\n if (node === null) {\n if (!held.has(key)) {\n return held;\n }\n const next = new Map(held);\n next.delete(key);\n return next;\n }\n if (held.get(key) === node) {\n return held;\n }\n const next = new Map(held);\n next.set(key, node);\n return next;\n });\n };\n portalRefsRef.current.set(key, callback);\n }\n return callback;\n };\n\n /** The channel a slot's pane hands its content: its capture group and its takes. */\n const channelFor = (slot: SequenceSlot<RenderableActivity>): SequenceSlotChannel => {\n const key = `${slot.index}::${slot.slotId}`;\n let channel = channelsRef.current.get(key);\n if (channel === undefined) {\n const generation = setGeneration;\n const at = slot.index;\n channel = {\n captureGroup: `${captureScope}::${slot.slotId}`,\n // A getter, so the channel keeps the one identity a recorder joined\n // while still answering for the mode in force now.\n get renderMode() {\n return modeRef.current;\n },\n takeState: (take, state) => {\n latestRef.current?.updateTake(generation, at, take, state);\n },\n };\n channelsRef.current.set(key, channel);\n }\n return channel;\n };\n\n if (!current) {\n return <div className=\"lk-seq\" />;\n }\n\n // Every budgeted recording this paper presents, and the key it is budgeted\n // under. Derived from the resolved policy, so the pager, the plan and the\n // schema cannot disagree about what is budgeted.\n const budgetedMedia = slots.flatMap((slot) => {\n const found: { key: string; url: string }[] = [];\n const own = (slot.activity as { media?: ActivityMedia }).media;\n if (own !== undefined && resolvePlaybackPolicy(own).maxPlays !== null) {\n found.push({ key: slotMediaKey(slot.slotId), url: own.url });\n }\n const stimulus = slot.group?.stimulus.media;\n if (stimulus !== undefined && resolvePlaybackPolicy(stimulus).maxPlays !== null) {\n found.push({ key: stimulusMediaKey(slot.slotId), url: stimulus.url });\n }\n return found;\n });\n\n const budgetEnforced = mediaBudget?.enforced ?? renderMode !== 'review';\n\n if (budgetedMedia.length > 0 && renderMode === 'exam' && budgetEnforced) {\n if (mediaBudget?.onPlayConsumed === undefined) {\n throw new Error(\n 'ActivitySequence: renderMode \"exam\" with media that declares `maxPlays` requires ' +\n '`mediaBudget.onPlayConsumed`. The SDK persists nothing, so without it the count lives ' +\n 'only in this mount: a refresh silently restores the full budget, and a paper that ' +\n 'grants unlimited plays while showing \"2 plays remaining\" is indistinguishable — to the ' +\n 'learner and to an appeal — from one that works. Budgeted media: ' +\n `${[...new Set(budgetedMedia.map((m) => m.key))].join(', ')}.`,\n );\n }\n const resuming =\n (responses !== undefined && Object.keys(responses).length > 0) ||\n (submittedSlotIds !== undefined && submittedSlotIds.length > 0);\n if (resuming && mediaBudget.plays === undefined) {\n throw new Error(\n 'ActivitySequence: this is a resumed attempt (`responses`/`submittedSlotIds` were ' +\n 'supplied) but `mediaBudget.plays` is absent. A resume that omits the ledger hands the ' +\n 'learner a fresh budget at exactly the moment it matters. Pass ' +\n '`restoreMediaPlayLedger(plan, stored).entries`, or `{}` to state explicitly that ' +\n 'nothing was spent.',\n );\n }\n }\n\n // One recording must not carry two budgets, in any mode — six questions each\n // holding the same clip at maxPlays 2 is twelve plays of one recording.\n const keysByUrl = new Map<string, Set<string>>();\n for (const { key, url } of budgetedMedia) {\n const keys = keysByUrl.get(url) ?? new Set<string>();\n keys.add(key);\n keysByUrl.set(url, keys);\n }\n for (const [url, keys] of keysByUrl) {\n if (keys.size > 1) {\n throw new Error(\n `ActivitySequence: media ${JSON.stringify(url)} is budgeted under ${keys.size} separate ` +\n `keys (${[...keys].join(', ')}), so one recording grants ${keys.size} × maxPlays. Put ` +\n \"the questions that share a recording in an item group — a group's stimulus is one \" +\n 'recording with one budget.',\n );\n }\n }\n\n /** The per-media binding handed to a component or to the stimulus panel. */\n const bindingFor = (\n key: string,\n slot: { slotId: string; index: number; activity: { id: string } },\n ): MediaBudgetBinding | undefined => {\n if (mediaBudget === undefined) {\n return undefined;\n }\n const entry =\n mediaSeedsApply && mediaPlays !== undefined && Object.hasOwn(mediaPlays, key)\n ? mediaPlays[key]\n : undefined;\n return {\n key,\n slotId: slot.slotId,\n index: slot.index,\n activityId: slot.activity.id,\n ...(entry !== undefined ? { entry } : {}),\n ...(mediaBudget.enforced !== undefined ? { enforced: mediaBudget.enforced } : {}),\n ...(mediaBudget.onPlayConsumed !== undefined\n ? { onPlayConsumed: mediaBudget.onPlayConsumed }\n : {}),\n ...(mediaBudget.onPlayRefunded !== undefined\n ? { onPlayRefunded: mediaBudget.onPlayRefunded }\n : {}),\n ...(mediaBudget.onPosition !== undefined ? { onPosition: mediaBudget.onPosition } : {}),\n ...(mediaBudget.strings !== undefined ? { strings: mediaBudget.strings } : {}),\n };\n };\n\n /**\n * The per-slot recording binding handed to a read-aloud, modelled on\n * `bindingFor`: the sequence-level methods are called with the slot they\n * belong to, and `assess` / `playbackUrl` are copied only when the consumer\n * supplied them — a binding carrying `assess: undefined` is a binding the\n * component would try to call.\n *\n * It watches nothing. The pager used to watch the promises it handed out to\n * decide when a set was finished, and a promise knows nothing of the take\n * that asked for it: a late failure from a paper no longer on screen landed\n * in a new paper that reused the slot's ids, as a blank. What a take came to\n * is reported by the component, numbered by take, through the slot's channel\n * — see `record` and `reportIfFinished`.\n *\n * A consumer's own renderer is handed no binding.\n */\n const recordingBindingFor = (slot: {\n slotId: string;\n index: number;\n activity: { id: string };\n }): RecordingBinding | undefined => {\n if (recordingBinding === undefined) {\n return undefined;\n }\n const at: SequenceRecordingSlot = {\n slotId: slot.slotId,\n index: slot.index,\n activityId: slot.activity.id,\n };\n const { assess, playbackUrl } = recordingBinding;\n // Called on the binding, so a consumer's binding written as a class keeps\n // its `this`.\n return {\n upload: (take) => recordingBinding.upload(take, at),\n ...(assess !== undefined\n ? { assess: (ref: RecordingRef) => assess.call(recordingBinding, ref, at) }\n : {}),\n ...(playbackUrl !== undefined\n ? { playbackUrl: (ref: RecordingRef) => playbackUrl.call(recordingBinding, ref, at) }\n : {}),\n };\n };\n\n const forwarded = {\n ...(onInteraction ? { onInteraction } : {}),\n renderMode,\n ...(sanitizeHtml ? { sanitizeHtml } : {}),\n ...(strings !== undefined ? { strings } : {}),\n // Handed to no question in `exam`, where none gives AI help: a\n // `renderers` override is not given ports it must then know to ignore.\n ...(ai !== undefined && renderMode !== 'exam' ? { ai } : {}),\n ...(theme ? { theme } : {}),\n ...(locale ? { locale } : {}),\n ...(disabled ? { disabled } : {}),\n };\n\n const renderSlot = (slot: SequenceSlot<RenderableActivity>): React.JSX.Element => {\n const activity = slot.activity;\n const { slotId, index: slotIndex } = slot;\n const activityId = activity.id;\n const restored =\n seedsApply && responses !== undefined && Object.hasOwn(responses, slotId)\n ? responses[slotId]\n : undefined;\n const slotOutcome =\n outcomes !== undefined && Object.hasOwn(outcomes, slotId) ? outcomes[slotId] : undefined;\n const slotBinding = bindingFor(slotMediaKey(slotId), slot);\n const calls = callsFor(slot);\n const childProps = {\n // `defaultValue`, not `value`: the learner must be able to keep editing\n // a restored answer. A controlled `value` would freeze it unless the\n // consumer also threaded state back, which is not what resume means.\n ...(restored !== undefined ? { defaultValue: restored } : {}),\n ...(seedsApply && submitted.has(slotId) ? { defaultSubmitted: true } : {}),\n ...(slotOutcome !== undefined ? { outcome: slotOutcome } : {}),\n // One identity per slot for the life of the set — see `callsFor`. Their\n // bodies are `submitSlot` and `completeSlot`, the same ones a host's own\n // renderer calls, so a question the host draws is recorded exactly as one\n // the SDK draws.\n onComplete: calls.onComplete,\n onSubmit: calls.onSubmit,\n ...forwarded,\n ...(slotBinding !== undefined ? { mediaBudget: slotBinding } : {}),\n };\n\n const CustomRenderer = renderers?.[activity.type];\n if (CustomRenderer) {\n const portalKey = slotKeyOf(setKey, slot);\n return (\n <>\n <CustomRenderer\n data={activity}\n {...childProps}\n question={{\n slot: calls.slot,\n active: slotIndex === index,\n portalContainer: portals.get(portalKey) ?? null,\n clear: calls.clear,\n setPending: calls.setPending,\n emit: calls.emit,\n }}\n />\n {/*\n After the question, so a popover portalled into it paints above.\n Inside the pane, so it is hidden with the question and painted with\n it in fullscreen — the two things portalling into `document.body`\n gets wrong here.\n */}\n <div className=\"lk-seq-portal\" ref={portalRefFor(portalKey)} />\n </>\n );\n }\n if (activity.type === 'multiple-choice') {\n return (\n <MultipleChoice\n data={activity}\n // The sequence seed reaches the OPTION shuffle too. Without it a\n // resumed or reviewed item invented a fresh per-mount order, so the\n // learner saw their answers against a different arrangement than the\n // one they sat — the exact reproducibility the attempt id is for.\n {...(shuffleSeed !== undefined ? { shuffleSeed } : {})}\n {...childProps}\n />\n );\n }\n if (activity.type === 'fill-in-the-blanks') {\n return <FillInTheBlanks data={activity} {...childProps} />;\n }\n if (activity.type === 'gap-select') {\n return (\n <GapSelect\n data={activity}\n // The sequence seed reaches the per-gap choice shuffle too, for the\n // same reason it reaches MultipleChoice's options: a resumed or\n // reviewed item that dealt a fresh order would show the learner\n // their answers against an arrangement they never sat.\n {...(shuffleSeed !== undefined ? { shuffleSeed } : {})}\n {...childProps}\n />\n );\n }\n if (activity.type === 'dictation') {\n // Nothing to seed: a dictation shuffles nothing. Outside `practice` its\n // raw response is recorded through `onSubmit` above, like MC/FIB/GS.\n return <Dictation data={activity} {...childProps} />;\n }\n if (activity.type === 'read-aloud') {\n const slotRecordingBinding = recordingBindingFor(slot);\n const slotAssessment =\n assessments !== undefined && Object.hasOwn(assessments, slotId)\n ? assessments[slotId]\n : undefined;\n return (\n <ReadAloud\n data={activity}\n // The whole bag, nothing subtracted — unlike the written-response\n // branch below, which drops `onComplete` because it never grades. A\n // read-aloud needs it in `practice`, where a returned grade is a\n // score like any other, and simply never calls it in `exam`.\n {...childProps}\n // No capture group here: the slot's pane hands it down through\n // `SequenceSlotContext`, so it is no prop of the public component.\n {...(slotRecordingBinding !== undefined\n ? { recordingBinding: slotRecordingBinding }\n : {})}\n {...(slotAssessment !== undefined ? { assessment: slotAssessment } : {})}\n {...(workletUrl !== undefined ? { workletUrl } : {})}\n />\n );\n }\n if (activity.type === 'written-response') {\n // Derived from the SAME bag the other two get, minus the one prop this\n // component genuinely does not have. Listing the props by hand is what\n // made this the branch left behind three separate times —\n // `defaultSubmitted`, the redacted-in-`practice` guard and the media\n // budget were each added to `childProps` and each silently missed here.\n // Subtracting from the shared bag cannot forget a prop that is added to\n // it; listing them can.\n const { onComplete: _neverGrades, ...writtenResponseProps } = childProps;\n return (\n <WrittenResponse\n data={activity}\n {...writtenResponseProps}\n onSubmitted={(submission) =>\n record(\n { kind: 'submitted', index: slotIndex, slotId, activityId, submission },\n setGeneration,\n )\n }\n />\n );\n }\n return (\n <div className=\"lk-seq-unsupported\" role=\"note\">\n {s.unsupportedActivity}\n </div>\n );\n };\n\n const currentEntryKey = entryKeyOf(current);\n\n return (\n <div className=\"lk-seq\" lang={locale}>\n <div className=\"lk-seq-progress\" aria-live=\"polite\" role=\"status\">\n {s.questionProgress(index + 1, total)}\n </div>\n\n {/*\n Each group's stimulus is mounted ONCE and only hidden while the current\n question is outside its group. That — not re-rendering it per slot — is\n what makes the passage the same DOM node throughout, and what lets a\n recording keep its position between questions of one group.\n\n It sits BESIDE the question region, not inside it. Inside, every\n navigation dropped focus onto a container whose first content was the\n whole passage: the region promised \"Question 3 of 5\" and then began\n with the passage, keyboard users tabbed through the stimulus (audio\n controls included) before reaching the question, and the passage was a\n landmark nested inside another landmark. As a sibling it stays a\n landmark the learner can jump back to, while focus lands on the\n question that was actually navigated to.\n */}\n {stimulusMounts.map(({ entryKey, group, first, last }) => {\n // `setKey` leads for the same reason it does on a slot pane: entry keys\n // are short and repeat across papers, so a set change to a DIFFERENT\n // paper that happens to reuse an entry key would not remount this\n // panel — and a mount-only budget seed would leak into the next paper.\n const firstSlot = slots[first];\n const stimulusBinding =\n firstSlot === undefined\n ? undefined\n : bindingFor(stimulusMediaKey(firstSlot.slotId), firstSlot);\n return (\n <SequencePane\n className=\"lk-seq-stimulus\"\n hidden={entryKey !== currentEntryKey}\n key={`${setKey}::group-${entryKey}`}\n >\n <StimulusPanel\n stimulus={group.stimulus}\n range={{ first: first + 1, last: last + 1 }}\n renderMode={renderMode}\n {...(sanitizeHtml ? { sanitizeHtml } : {})}\n {...(locale ? { locale } : {})}\n {...(onInteraction ? { onInteraction } : {})}\n {...(disabled ? { disabled } : {})}\n {...(strings !== undefined ? { strings } : {})}\n {...(stimulusBinding !== undefined ? { mediaBudget: stimulusBinding } : {})}\n />\n </SequencePane>\n );\n })}\n\n <section\n className=\"lk-seq-question\"\n ref={regionRef}\n tabIndex={-1}\n aria-label={s.questionProgress(index + 1, total)}\n >\n {/*\n Every slot stays MOUNTED; only the current one is visible. Rendering\n just the current activity unmounted the others, so navigating back to\n an answered question showed it blank and re-answerable — the learner\n silently lost their answer and a second submit fired completion\n again. `hidden` removes the inactive slots from the accessibility\n tree and from tab order, so only the current question is reachable.\n */}\n {slots.map((slot) => {\n // The slot id (authored position) plus the activity id: the same\n // activity may legitimately fill two slots, and each must keep its\n // own answer state; and a different activity in the same slot must\n // remount rather than inherit the previous one's state.\n //\n // `setKey` leads, so a set change remounts every child. Without it a\n // pure REORDER changed `setKey` — clearing the outcomes — while each\n // child kept its key and therefore its `completed` state. Those\n // slots could not be answered again and no outcome would ever refill\n // them, so the sequence could never finish. A reset has to reset\n // both halves or neither. A parent re-creating a structurally\n // identical array leaves `setKey` untouched, so answers still\n // survive that.\n const slotKey = slotKeyOf(setKey, slot);\n return (\n <SequencePane\n className=\"lk-seq-slot\"\n hidden={slot.index !== index}\n // A channel for every slot, not just the ones that record: its\n // capture group is empty until something joins it, and a pane\n // that asked whether its activity records would have to know the\n // type of a renderer the consumer supplied.\n channel={channelFor(slot)}\n key={slotKey}\n >\n {renderSlot(slot)}\n </SequencePane>\n );\n })}\n </section>\n\n <div className=\"lk-seq-nav\">\n <button\n type=\"button\"\n className=\"lk-seq-prev\"\n onClick={() => go(index - 1)}\n disabled={index === 0}\n >\n {s.previous}\n </button>\n <button\n type=\"button\"\n className=\"lk-seq-next\"\n onClick={() => go(index + 1)}\n disabled={index === total - 1}\n >\n {s.next}\n </button>\n </div>\n </div>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/WrittenResponse/WrittenResponse.tsx","../src/components/WrittenResponse/index.tsx"],"names":["WrittenResponse","jsx"],"mappings":"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","file":"chunk-4NERCBOW.js","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n type CriterionScore,\n countWords,\n evaluate,\n type GradeRecord,\n type InlineCorrection,\n type InteractionEvent,\n type ItemOutcome,\n type LearnerResponse,\n type ThemeTokens,\n validateActivity,\n type WrittenResponseData,\n type XAPIStatement,\n xAPIBuilder,\n} from '@intellectif/lk-core';\nimport { type CSSProperties, useEffect, useMemo, useRef, useState } from 'react';\nimport { useActivityState } from '../../hooks/useActivityState.js';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport type { LkStrings, LkStringsOverride } from '../../i18n/strings.js';\nimport { ANONYMOUS_ACTOR, isDevelopment, objectIdFor } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport type {\n HtmlSanitizer,\n MediaBudgetBinding,\n MediaTransportStrings,\n Renderable,\n RenderMode,\n} from '../types.js';\n\n/**\n * Payload delivered when the learner submits a written response. Grading is\n * DEFERRED (asynchronous AI/human grading), so there is no score here — and\n * deliberately no fake one: an ungraded submission must never be shown or\n * stored as a 0. The consumer persists `text` and grades it out-of-band.\n */\nexport interface WrittenResponseSubmission {\n /** The submitted text, verbatim. */\n text: string;\n /** Word count of `text`, recomputed with the canonical `countWords()`. */\n wordCount: number;\n /** Whether `wordCount` falls within the activity's `[minWords, maxWords]`. */\n withinWordBounds: boolean;\n /** Time in milliseconds from first interaction to submission. */\n timeSpent: number;\n /** The SUBMITTED-verb xAPI statement built for this submission (no score fields). */\n xapiStatement: XAPIStatement;\n}\n\n/**\n * Props for {@link WrittenResponse}. Mirrors `ActivityProps` except for the\n * completion callback: a deferred-grading activity completes with a\n * {@link WrittenResponseSubmission} (`onSubmitted`), not a scored\n * `ActivityResult` (`onComplete`) — deviation from Req 22.8 recorded in the\n * roadmap (§3.1): fabricating a score of 0 for ungraded work is the exact\n * bug this type exists to fix. Everything else — controlled value, render\n * mode, server outcome, rich text — follows the shared `ActivityProps`\n * contract verbatim.\n */\nexport interface WrittenResponseProps {\n /** Activity content. Accepts a `redact()` projection in `exam` mode. */\n data: Renderable<WrittenResponseData>;\n /**\n * Called on submit with the ungraded submission (text, recomputed word\n * count, bounds flag, timing, xAPI statement). Fires in `practice` and\n * `exam` mode, after `onSubmit`; never in `review`.\n *\n * Optional since lk-react 2.1.0 so a `review`-mode render (which has no submit\n * control) and an exam runner that only wants the raw response through\n * `onSubmit` do not have to pass a no-op. Supply it in `practice`/`exam`\n * unless `onSubmit` already captures everything you persist.\n */\n onSubmitted?: (submission: WrittenResponseSubmission) => void;\n /**\n * Called on submit with the raw learner response and no grade, BEFORE\n * `onSubmitted`. This is the only submit callback an exam runner needs:\n * the server grades the text and returns an {@link ItemOutcome} later.\n */\n onSubmit?: (response: LearnerResponse) => void;\n /**\n * Controlled value: the learner's current response. When present the\n * textarea renders from it (never from internal state) and every keystroke\n * goes out through `onChange` — restore an in-progress attempt, autosave a\n * delta, or drive a review render with it.\n */\n value?: LearnerResponse;\n /** Initial response for an uncontrolled component (ignored when `value` is set). */\n defaultValue?: LearnerResponse;\n /**\n * Mount as already submitted — read at mount only. Without it a resumed\n * attempt reopens an essay the learner had already submitted as editable\n * and re-submittable.\n */\n defaultSubmitted?: boolean;\n /** Fires on every change to the learner's response. Required for a controlled component. */\n onChange?: (response: LearnerResponse) => void;\n /**\n * Presentation mode, default `practice`. `practice` and `exam` are nearly\n * identical here — a written response is never graded client-side, so\n * there is no local score to withhold; `exam` additionally never calls\n * `evaluate()` and renders happily from a `redact()` projection.\n * `review` is read-only and shows the server's `outcome`.\n */\n renderMode?: RenderMode;\n /**\n * Server-computed outcome, rendered by `review` mode: `deferred` shows a\n * \"not graded yet\" affordance, `scored` shows the returned grade and\n * feedback. Ignored in other modes — this component never manufactures an\n * outcome of its own.\n */\n outcome?: ItemOutcome;\n /** Renders `data.promptHtml` when provided. See `HtmlSanitizer`. */\n sanitizeHtml?: HtmlSanitizer;\n /** Binds this activity's own `data.media` to a play budget. See {@link MediaBudgetBinding}. */\n mediaBudget?: MediaBudgetBinding;\n /** Translations for the audio transport chrome. See {@link MediaTransportStrings}. */\n mediaStrings?: Partial<MediaTransportStrings>;\n /** Overrides the SDK's chrome text for this activity. See {@link LkIntlProvider}. */\n strings?: LkStringsOverride;\n onInteraction?: (event: InteractionEvent) => void;\n /** Per-instance token overrides, applied as inline CSS vars on the root. */\n theme?: Partial<ThemeTokens>;\n locale?: string;\n disabled?: boolean;\n}\n\n/**\n * Reads the text out of a learner response. Another activity type's response\n * shape (the `value`/`defaultValue` props take the `LearnerResponse` union,\n * so a generic runner can hand any of them over) reads as empty rather than\n * throwing mid-exam.\n */\nfunction textOf(response: LearnerResponse | undefined): string {\n return response !== undefined && response.type === 'written-response' ? response.text : '';\n}\n\n/**\n * One criterion's score as a percentage of its OWN maximum.\n *\n * `CriterionScore.maxScore` defaults to 1 — the SDK's scaled convention — but\n * a grader may work out of 100, or out of 9 for a CEFR band, and\n * `gradeFromRubric` deliberately stores those judgements verbatim so a grade\n * stays auditable in the units the grader actually used. It normalises when it\n * computes the total; anything DISPLAYING a criterion has to normalise too, or\n * a perfectly ordinary 82/100 renders as \"8200%\". This is the same guard the\n * overall score already applies against `outcome.maxScore`.\n */\nfunction criterionPercent(criterion: CriterionScore): string {\n const max = criterion.maxScore !== undefined && criterion.maxScore > 0 ? criterion.maxScore : 1;\n return `${Math.round(((criterion.score as number) / max) * 100)}%`;\n}\n\n/**\n * Pairs each correction with a list key that is unique by construction. The\n * same mistake made twice carries the same `original`/`corrected` pair, so\n * the grader's `range` is what tells the occurrences apart. `range` is\n * optional and nothing validates it, so a correction without one — or one\n * whose range the grader already used — is keyed by its position instead.\n * Only position keys start with `#`, so the two kinds never collide.\n */\nfunction keyedCorrections(\n corrections: readonly InlineCorrection[],\n): { key: string; correction: InlineCorrection }[] {\n const used = new Set<string>();\n return corrections.map((correction, index) => {\n const anchored = correction.range\n ? `${correction.range.start}-${correction.range.end}:${correction.corrected}`\n : undefined;\n const key = anchored !== undefined && !used.has(anchored) ? anchored : `#${index}`;\n used.add(key);\n return { key, correction };\n });\n}\n\n/**\n * Renders the server-computed outcome in `review` mode. The grade shown here\n * always came back from the asynchronous grader — nothing on this path scores,\n * infers, or defaults a grade. A `written-response` outcome carries no\n * per-item `details` (there are no options or blanks to mark), so the render\n * is the scaled score, the pass state, and the grader's feedback.\n */\nfunction GradeBody({ grade, s }: { grade: GradeRecord; s: LkStrings }) {\n return (\n <>\n {grade.feedback ? <p className=\"lk-wr-grade-feedback\">{grade.feedback}</p> : null}\n {grade.criteria && grade.criteria.length > 0 ? (\n <ul className=\"lk-wr-criteria\">\n {grade.criteria.map((criterion) => (\n <li\n className=\"lk-wr-criterion\"\n key={criterion.name}\n data-na={String(criterion.notApplicable === true)}\n >\n <span className=\"lk-wr-criterion-name\">{criterion.name}</span>\n {criterion.notApplicable === true ? (\n <span className=\"lk-wr-criterion-score\">{s.notApplicable}</span>\n ) : (\n <span className=\"lk-wr-criterion-score\">\n {criterion.band ??\n (typeof criterion.score === 'number' ? criterionPercent(criterion) : '')}\n </span>\n )}\n {criterion.comment ? (\n <span className=\"lk-wr-criterion-comment\">{criterion.comment}</span>\n ) : null}\n </li>\n ))}\n </ul>\n ) : null}\n {grade.corrections && grade.corrections.length > 0 ? (\n <ul className=\"lk-wr-corrections\">\n {keyedCorrections(grade.corrections).map(({ key, correction }) => (\n <li className=\"lk-wr-correction\" key={key}>\n <del className=\"lk-wr-correction-original\">{correction.original}</del>{' '}\n <ins className=\"lk-wr-correction-corrected\">{correction.corrected}</ins>\n {correction.explanation ? (\n <span className=\"lk-wr-correction-explanation\">{correction.explanation}</span>\n ) : null}\n </li>\n ))}\n </ul>\n ) : null}\n {grade.requiresHumanReview === true ? (\n <p className=\"lk-wr-review-flag\">{s.awaitingHumanReview}</p>\n ) : null}\n </>\n );\n}\n\n/**\n * Renders the server-computed outcome in `review` mode. The grade shown here\n * always came back from the grader — nothing on this path scores, infers, or\n * defaults a grade.\n */\nfunction OutcomeSummary({ outcome, s }: { outcome: ItemOutcome; s: LkStrings }) {\n if (outcome.status === 'scored' || outcome.status === 'graded') {\n // `score` is scaled [0–1] against `maxScore`; normalising by `maxScore`\n // is a no-op for the canonical maxScore of 1 and keeps an unscaled\n // grader payload (8.5 / 10) from rendering as 850%.\n const percent =\n outcome.maxScore > 0\n ? Math.round((outcome.score / outcome.maxScore) * 100)\n : Math.round(outcome.score * 100);\n return (\n <div\n className=\"lk-wr-outcome\"\n data-status={outcome.status}\n data-passed={String(outcome.passed)}\n >\n <p className=\"lk-wr-grade\">{s.scoreAnnouncement(percent, outcome.passed)}</p>\n {outcome.status === 'graded' ? (\n <GradeBody grade={outcome.grade} s={s} />\n ) : outcome.feedback ? (\n <p className=\"lk-wr-grade-feedback\">{outcome.feedback}</p>\n ) : null}\n </div>\n );\n }\n if (outcome.status === 'deferred') {\n return (\n <div className=\"lk-wr-outcome\" data-status=\"deferred\">\n <p className=\"lk-wr-grade\">{s.awaitingGrade}</p>\n </div>\n );\n }\n // `unscorable` reasons are developer-facing strings; surface the state, not\n // the reason, and leave it on the element for diagnostics.\n return (\n <div className=\"lk-wr-outcome\" data-status=\"unscorable\" data-reason={outcome.reason}>\n <p className=\"lk-wr-grade\">{s.couldNotBeGraded}</p>\n </div>\n );\n}\n\nexport function WrittenResponse({\n data,\n onSubmitted,\n onSubmit,\n value,\n defaultValue,\n defaultSubmitted,\n onChange,\n renderMode = 'practice',\n outcome,\n sanitizeHtml,\n mediaBudget,\n mediaStrings,\n strings,\n onInteraction,\n theme,\n locale,\n disabled,\n}: WrittenResponseProps) {\n // Dev-only boundary validation (Req 2.3), same convention as MC/FIB. The\n // content schema is loose, so a `redact()` projection validates too.\n const s = useLkStrings(strings);\n\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n const result = validateActivity('written-response', data);\n return result.success ? null : new ActivitySchemaError('written-response', result.errors);\n }, [data]);\n\n const { state, start, complete, getTimeSpent, reset } = useActivityState(\n defaultSubmitted === true ? 'completed' : 'idle',\n );\n const isControlled = value !== undefined;\n const [innerText, setInnerText] = useState<string>(() => textOf(defaultValue));\n const [summary, setSummary] = useState<string | null>(null);\n\n /**\n * Rich text is rendered ONLY through a caller-supplied sanitiser, and only\n * when the author actually shipped the HTML sidecar. Without both, the\n * plain-text prompt is rendered as an escaped React child.\n */\n const promptHtml = useMemo(() => {\n if (sanitizeHtml === undefined || typeof data.promptHtml !== 'string') {\n return null;\n }\n return sanitizeHtml(data.promptHtml);\n }, [data.promptHtml, sanitizeHtml]);\n\n // Latest seed text, kept in a ref so `defaultValue` is NOT a reset trigger:\n // callers pass an object literal, whose identity changes every render.\n const defaultTextRef = useRef<string>(textOf(defaultValue));\n useEffect(() => {\n defaultTextRef.current = textOf(defaultValue);\n }, [defaultValue]);\n\n // Reset on data-prop change (Req 3.7): back to the seed (empty when there\n // is none — the v1 behaviour), never the previous activity's draft.\n // Mirror `defaultSubmitted` so the reset below returns to the SEEDED state.\n // Resetting unconditionally to idle unlocked an item the learner had already\n // committed — which made `defaultSubmitted` a no-op here, since this effect\n // runs right after the first paint.\n const defaultSubmittedRef = useRef(defaultSubmitted);\n useEffect(() => {\n defaultSubmittedRef.current = defaultSubmitted;\n }, [defaultSubmitted]);\n\n // Identity guard so the MOUNT run is a no-op. Without it this effect fires\n // after the first paint and undoes every seed it was just given; with it,\n // only a real `data` change resets. A parent that rebuilds structurally\n // identical entries in render (`activities={raw.map(redact)}`) must not\n // unlock or revert a restored answer — the pager already remounts a slot\n // whose activity actually changed, via its key.\n const lastDataRef = useRef(data);\n useEffect(() => {\n if (lastDataRef.current === data) {\n return;\n }\n lastDataRef.current = data;\n setInnerText(defaultTextRef.current);\n setSummary(null);\n reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [data, reset]);\n\n if (devError) {\n throw devError;\n }\n\n const isExam = renderMode === 'exam';\n const isReview = renderMode === 'review';\n\n const text = isControlled ? textOf(value) : innerText;\n const submitted = state === 'completed';\n const inactive = disabled === true || submitted || isReview;\n const wordCount = countWords(text);\n const withinBounds = wordCount >= data.minWords && wordCount <= data.maxWords;\n\n const handleChange = (next: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n // A controlled component renders from `props.value` only; the caller's\n // state is the single source of truth for the text.\n if (!isControlled) {\n setInnerText(next);\n }\n const nextWordCount = countWords(next);\n onChange?.({ type: 'written-response', text: next, wordCount: nextWordCount });\n onInteraction?.({\n type: 'text-changed',\n activityId: data.id,\n timestamp: Date.now(),\n payload: { wordCount: nextWordCount },\n });\n };\n\n // Practice reveals nothing an essay could leak, but it DOES run the local\n // submit path and emit a practice xAPI statement for work the server is\n // supposed to grade. MultipleChoice and FillInTheBlanks have refused\n // redacted data in `practice` since 0.5.0; this component silently accepted\n // it, so an all-essay redacted paper mounted without `renderMode` looked\n // entirely healthy end to end. Fail at render, like its siblings.\n if (data.redacted === true && renderMode === 'practice') {\n throw new Error(\n `Written Response \"${data.id}\" received redacted activity data in renderMode \"practice\". ` +\n 'Practice runs the local submit path and emits a practice-mode xAPI statement, so an ' +\n 'exam item wired this way is graded nowhere and looks fine while it happens. ' +\n 'Render redacted data with renderMode=\"exam\" (server grades) or \"review\" (pass `outcome`).',\n );\n }\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive || text.trim().length === 0) {\n return;\n }\n let finalWordCount = wordCount;\n let finalWithinBounds = withinBounds;\n // `exam` mode must not call evaluate(): the component does not grade, and\n // a redacted projection carries no scoring input anyway. The two facts\n // evaluate() returns for a deferred type (recomputed word count, bounds)\n // are exactly what is computed above, so the payload is identical either\n // way; practice keeps going through core so the numbers stay canonical.\n if (!isExam) {\n const itemOutcome = evaluate(data, { type: 'written-response', text, wordCount });\n const partial = itemOutcome.status === 'deferred' ? itemOutcome.partial : undefined;\n if (typeof partial?.wordCount === 'number') {\n finalWordCount = partial.wordCount;\n }\n if (typeof partial?.withinWordBounds === 'boolean') {\n finalWithinBounds = partial.withinWordBounds;\n }\n }\n complete();\n const timeSpent = getTimeSpent();\n const xapiStatement = xAPIBuilder.buildSubmittedStatement({\n actor: ANONYMOUS_ACTOR,\n object: {\n id: objectIdFor(data.id),\n name: { [data.locale ?? 'en-US']: data.title },\n type: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n interactionType: 'long-fill-in',\n },\n timeSpentMs: timeSpent,\n response: text,\n resultExtensions: {\n 'urn:learning-kit:extension:word-count': finalWordCount,\n 'urn:learning-kit:extension:within-word-bounds': finalWithinBounds,\n },\n });\n // Raw response first (an exam runner persists this), then the fuller\n // ungraded submission — mirroring `onSubmit` before `onComplete`.\n onSubmit?.({ type: 'written-response', text, wordCount: finalWordCount });\n onSubmitted?.({\n text,\n wordCount: finalWordCount,\n withinWordBounds: finalWithinBounds,\n timeSpent,\n xapiStatement,\n });\n setSummary(s.responseSubmitted);\n onInteraction?.({\n type: 'submitted',\n activityId: data.id,\n timestamp: Date.now(),\n payload: { wordCount: finalWordCount, withinWordBounds: finalWithinBounds },\n });\n };\n\n const promptId = `${data.id}-prompt`;\n const counterId = `${data.id}-counter`;\n const boundsLabel = s.wordBounds(data.minWords, data.maxWords);\n\n return (\n <form\n className=\"lk-wr\"\n aria-label={data.title}\n lang={locale}\n data-render-mode={renderMode}\n style={theme as CSSProperties | undefined}\n onSubmit={handleSubmit}\n >\n {data.media ? (\n <ActivityMedia\n media={data.media}\n renderMode={renderMode}\n {...(mediaBudget !== undefined ? { mediaBudget } : {})}\n {...(mediaStrings !== undefined ? { mediaStrings } : {})}\n {...(strings !== undefined ? { strings } : {})}\n {...(onInteraction !== undefined ? { onInteraction } : {})}\n {...(locale !== undefined ? { locale } : {})}\n />\n ) : null}\n {promptHtml === null ? (\n <p className=\"lk-wr-prompt\" id={promptId}>\n {data.prompt}\n </p>\n ) : (\n // A div, not a p: sanitised author HTML may contain block content.\n <div\n className=\"lk-wr-prompt\"\n id={promptId}\n // biome-ignore lint/security/noDangerouslySetInnerHtml: rendered only through the caller-supplied HtmlSanitizer — the SDK never injects unsanitised markup\n dangerouslySetInnerHTML={{ __html: promptHtml }}\n />\n )}\n <textarea\n className=\"lk-wr-textarea\"\n aria-labelledby={promptId}\n aria-describedby={counterId}\n value={text}\n onChange={(event) => handleChange(event.target.value)}\n // `review` uses readOnly rather than disabled: the submitted essay\n // must stay focusable, selectable and scrollable for a screen reader\n // and a keyboard, while remaining uneditable.\n disabled={!isReview && inactive}\n readOnly={isReview}\n rows={8}\n />\n <div\n className=\"lk-wr-counter\"\n id={counterId}\n aria-live=\"polite\"\n data-within-bounds={withinBounds}\n >\n {s.wordCount(wordCount)} ({boundsLabel})\n </div>\n {isReview ? null : (\n <button\n type=\"submit\"\n className=\"lk-wr-submit\"\n disabled={inactive || text.trim().length === 0}\n >\n {s.submit}\n </button>\n )}\n <FeedbackRegion id={`${data.id}-feedback`}>\n {isReview ? outcome ? <OutcomeSummary outcome={outcome} s={s} /> : null : summary}\n </FeedbackRegion>\n </form>\n );\n}\n","'use client';\n\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport {\n WrittenResponse as WrittenResponseCore,\n type WrittenResponseProps,\n} from './WrittenResponse.js';\n\nexport type { HtmlSanitizer, Renderable, RenderMode } from '../types.js';\nexport type { WrittenResponseProps, WrittenResponseSubmission } from './WrittenResponse.js';\n\n/**\n * Public Written Response activity: a labelled textarea with a live word\n * counter for free-text writing graded asynchronously (AI or human). The core\n * is wrapped in `ActivityErrorBoundary` so a render failure (incl. the dev\n * schema-validation throw) degrades to an accessible fallback.\n */\nexport function WrittenResponse(props: WrittenResponseProps) {\n // Read here rather than in the boundary: a class component cannot call a\n // hook, and the fallback it renders is learner-facing text like any other.\n const strings = useLkStrings(props.strings);\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title} strings={strings}>\n <WrittenResponseCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/Dictation/Dictation.tsx","../src/components/Dictation/index.tsx"],"names":["result","Dictation","jsx"],"mappings":"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","file":"chunk-5QCZZBOX.js","sourcesContent":["'use client';\n\nimport {\n type ActivityMedia as ActivityMediaData,\n ActivitySchemaError,\n alignDictation,\n assertRedacted,\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 { FeedbackRegion } from '../shared/FeedbackRegion.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}: DictationProps) {\n const isExam = renderMode === 'exam';\n const isReview = renderMode === 'review';\n const s = useLkStrings(strings);\n const ai = useLearnerAi(aiProp);\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 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\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 reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [data, reset]);\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 // 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\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 data.hints?.mode === 'progressive-words' && !isExam && !isReview && 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 if (inactive || hintsShown === 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 scoringResult = score('dictation', data as DictationData, response);\n complete();\n const timeSpent = getTimeSpent();\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(scoringResult);\n onComplete?.({\n score: scoringResult.score,\n maxScore: scoringResult.maxScore,\n passed: scoringResult.passed,\n timeSpent,\n xapiStatement,\n });\n const correctWords = scoringResult.details.filter(\n (detail) => (detail.outcome ?? (detail.correct ? 'correct' : 'incorrect')) === 'correct',\n ).length;\n setSummary({\n text: `${s.answerSubmitted} ${s.scoreAnnouncement(\n Math.round(scoringResult.score * 100),\n scoringResult.passed,\n )} ${s.dictationWordsSummary(correctWords, scoringResult.details.length)}`,\n feedback: scoringResult.feedback,\n });\n fireInteraction('submitted', {\n length: text.length,\n hintsRevealed: reportedHints,\n score: scoringResult.score,\n });\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 revealing && transcript !== undefined && (!isReview || hasResponse)\n ? alignDictation(data, text)\n : null,\n [revealing, transcript, isReview, hasResponse, data, text],\n );\n const sentenceDiff = useMemo<DictationCharOp[] | null>(\n () =>\n alignment !== null && alignment.attempt !== ''\n ? diffDictationChars(alignment.reference, alignment.attempt)\n : null,\n [alignment],\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 = revealing && 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 s.dictationWordWrong(word.attempt, word.reference);\n case 'missing':\n return s.dictationWordMissing(word.reference);\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 return (\n <form\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 {hintsShown > 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 revealing && 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\n ? { 'data-marks': 'characters' }\n : {})}\n >\n {word.status === 'incorrect' && alignment !== null ? (\n <CharOps\n ops={diffDictationChars(word.reference, word.attempt)}\n contentDir={contentDir}\n />\n ) : word.status === 'missing' ? (\n word.reference\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 ? (\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 id={`${data.id}-feedback`}>\n <AnnouncementText announcement={isReview ? reviewSummary : summary} />\n </FeedbackRegion>\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 />\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"]}
@@ -1,13 +0,0 @@
1
- 'use client';
2
- 'use strict';
3
-
4
- var jsxRuntime = require('react/jsx-runtime');
5
-
6
- // src/components/shared/FeedbackRegion.tsx
7
- function FeedbackRegion({ id, className, children }) {
8
- return /* @__PURE__ */ jsxRuntime.jsx("div", { id, className, "aria-live": "polite", "aria-atomic": "true", children });
9
- }
10
-
11
- exports.FeedbackRegion = FeedbackRegion;
12
- //# sourceMappingURL=chunk-BRPEB43C.cjs.map
13
- //# sourceMappingURL=chunk-BRPEB43C.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/shared/FeedbackRegion.tsx"],"names":["jsx"],"mappings":";;;;;AAcO,SAAS,cAAA,CAAe,EAAE,EAAA,EAAI,SAAA,EAAW,UAAS,EAAwB;AAC/E,EAAA,uBACEA,cAAA,CAAC,SAAI,EAAA,EAAQ,SAAA,EAAsB,aAAU,QAAA,EAAS,aAAA,EAAY,QAC/D,QAAA,EACH,CAAA;AAEJ","file":"chunk-BRPEB43C.cjs","sourcesContent":["import type { ReactNode } from 'react';\n\nexport interface FeedbackRegionProps {\n /** Identifier so activity inputs can reference this region via aria. */\n id?: string;\n className?: string;\n children?: ReactNode;\n}\n\n/**\n * A polite ARIA live region. Screen readers announce content injected here\n * (score, feedback, state changes) without moving focus. Pure/presentational\n * — safe to render in any environment.\n */\nexport function FeedbackRegion({ id, className, children }: FeedbackRegionProps) {\n return (\n <div id={id} className={className} aria-live=\"polite\" aria-atomic=\"true\">\n {children}\n </div>\n );\n}\n"]}