@intellectif/lk-react 20.0.0 → 22.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 (174) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +33 -5
  3. package/dist/{WrittenResponse-D2YI8xOK.d.ts → WrittenResponse-CgRrjM4S.d.ts} +14 -4
  4. package/dist/{WrittenResponse-obIDDYDH.d.cts → WrittenResponse-D2K1VW_V.d.cts} +14 -4
  5. package/dist/ai/LkAiProvider.cjs +3 -3
  6. package/dist/ai/LkAiProvider.d.cts +17 -4
  7. package/dist/ai/LkAiProvider.d.ts +17 -4
  8. package/dist/ai/LkAiProvider.js +1 -1
  9. package/dist/ai/useAiHelp.cjs +9 -5
  10. package/dist/ai/useAiHelp.d.cts +58 -3
  11. package/dist/ai/useAiHelp.d.ts +58 -3
  12. package/dist/ai/useAiHelp.js +3 -3
  13. package/dist/{chunk-BGA2IVQY.js → chunk-3GLYK37F.js} +119 -26
  14. package/dist/chunk-3GLYK37F.js.map +1 -0
  15. package/dist/{chunk-LX2X4KC7.cjs → chunk-5YGVGMMV.cjs} +7 -7
  16. package/dist/{chunk-LX2X4KC7.cjs.map → chunk-5YGVGMMV.cjs.map} +1 -1
  17. package/dist/{chunk-3XP6VO4G.js → chunk-62K4Q565.js} +139 -45
  18. package/dist/chunk-62K4Q565.js.map +1 -0
  19. package/dist/{chunk-CEUEXISZ.cjs → chunk-6QY7GC55.cjs} +131 -50
  20. package/dist/chunk-6QY7GC55.cjs.map +1 -0
  21. package/dist/chunk-7EEKZWTQ.js +241 -0
  22. package/dist/chunk-7EEKZWTQ.js.map +1 -0
  23. package/dist/{chunk-3FBBP5QR.cjs → chunk-B4JRBV56.cjs} +6 -6
  24. package/dist/{chunk-3FBBP5QR.cjs.map → chunk-B4JRBV56.cjs.map} +1 -1
  25. package/dist/{chunk-RT2PP4SO.cjs → chunk-BL5SIBQA.cjs} +30 -30
  26. package/dist/{chunk-RT2PP4SO.cjs.map → chunk-BL5SIBQA.cjs.map} +1 -1
  27. package/dist/{chunk-YRNEOBXA.js → chunk-C4PSJ3ZC.js} +19 -2
  28. package/dist/chunk-C4PSJ3ZC.js.map +1 -0
  29. package/dist/{chunk-FEOFX4QW.js → chunk-FB47DQPS.js} +3 -3
  30. package/dist/{chunk-FEOFX4QW.js.map → chunk-FB47DQPS.js.map} +1 -1
  31. package/dist/chunk-FKNEHCNT.cjs +251 -0
  32. package/dist/chunk-FKNEHCNT.cjs.map +1 -0
  33. package/dist/{chunk-AG7MWZYZ.js → chunk-FPM2GQZY.js} +144 -55
  34. package/dist/chunk-FPM2GQZY.js.map +1 -0
  35. package/dist/{chunk-NWNQ2PHT.js → chunk-HNZ3JMAN.js} +8 -8
  36. package/dist/{chunk-NWNQ2PHT.js.map → chunk-HNZ3JMAN.js.map} +1 -1
  37. package/dist/{chunk-BY4UQH2Y.js → chunk-HYOUMTPW.js} +3 -3
  38. package/dist/{chunk-BY4UQH2Y.js.map → chunk-HYOUMTPW.js.map} +1 -1
  39. package/dist/{chunk-7K4B2EMU.js → chunk-HZIT24BK.js} +4 -4
  40. package/dist/{chunk-7K4B2EMU.js.map → chunk-HZIT24BK.js.map} +1 -1
  41. package/dist/{chunk-BJLQKJZX.cjs → chunk-J2QIHZ3H.cjs} +2 -2
  42. package/dist/chunk-J2QIHZ3H.cjs.map +1 -0
  43. package/dist/{chunk-DZVF3IGB.cjs → chunk-JII7XSCT.cjs} +59 -25
  44. package/dist/chunk-JII7XSCT.cjs.map +1 -0
  45. package/dist/{chunk-HI6BUUIJ.js → chunk-KFPCZ7N3.js} +48 -14
  46. package/dist/chunk-KFPCZ7N3.js.map +1 -0
  47. package/dist/{chunk-W6DMHN33.js → chunk-LRWFU3KS.js} +10 -10
  48. package/dist/{chunk-W6DMHN33.js.map → chunk-LRWFU3KS.js.map} +1 -1
  49. package/dist/{chunk-G7AGM2Y7.js → chunk-LX4JI262.js} +4 -4
  50. package/dist/{chunk-G7AGM2Y7.js.map → chunk-LX4JI262.js.map} +1 -1
  51. package/dist/{chunk-IXMT7X3E.cjs → chunk-M4UJBLVR.cjs} +17 -6
  52. package/dist/chunk-M4UJBLVR.cjs.map +1 -0
  53. package/dist/{chunk-AHGB5HH6.cjs → chunk-NFGUJ52G.cjs} +19 -2
  54. package/dist/chunk-NFGUJ52G.cjs.map +1 -0
  55. package/dist/{chunk-PWLJDP3X.cjs → chunk-NP5YC4NV.cjs} +95 -27
  56. package/dist/chunk-NP5YC4NV.cjs.map +1 -0
  57. package/dist/{chunk-ZDYNAWDZ.cjs → chunk-OJGTJOWI.cjs} +2 -2
  58. package/dist/chunk-OJGTJOWI.cjs.map +1 -0
  59. package/dist/{chunk-ORBU3XKH.js → chunk-PJMXHDY5.js} +16 -5
  60. package/dist/chunk-PJMXHDY5.js.map +1 -0
  61. package/dist/{chunk-TRMWBJWT.cjs → chunk-PZVRZ6OX.cjs} +141 -10
  62. package/dist/chunk-PZVRZ6OX.cjs.map +1 -0
  63. package/dist/{chunk-QKW2GGTV.js → chunk-R4ZNFDTK.js} +84 -16
  64. package/dist/chunk-R4ZNFDTK.js.map +1 -0
  65. package/dist/{chunk-YNDKAYW3.js → chunk-RX3A6KIY.js} +2 -2
  66. package/dist/chunk-RX3A6KIY.js.map +1 -0
  67. package/dist/{chunk-UHIOVOFK.cjs → chunk-TQN5CJO6.cjs} +125 -32
  68. package/dist/chunk-TQN5CJO6.cjs.map +1 -0
  69. package/dist/{chunk-AQIAQHJC.js → chunk-TRXUZSOY.js} +138 -8
  70. package/dist/chunk-TRXUZSOY.js.map +1 -0
  71. package/dist/{chunk-WBLYD5QA.js → chunk-U4KTOH7C.js} +2 -2
  72. package/dist/chunk-U4KTOH7C.js.map +1 -0
  73. package/dist/{chunk-L7WY5YFQ.cjs → chunk-UKUHVCZX.cjs} +5 -5
  74. package/dist/{chunk-L7WY5YFQ.cjs.map → chunk-UKUHVCZX.cjs.map} +1 -1
  75. package/dist/{chunk-HREA5SMB.cjs → chunk-UWFJGFB6.cjs} +116 -47
  76. package/dist/chunk-UWFJGFB6.cjs.map +1 -0
  77. package/dist/{chunk-JLDSCHY5.cjs → chunk-UXTYFEIC.cjs} +150 -61
  78. package/dist/chunk-UXTYFEIC.cjs.map +1 -0
  79. package/dist/{chunk-5GI53C6T.js → chunk-V2QZM2P2.js} +109 -40
  80. package/dist/chunk-V2QZM2P2.js.map +1 -0
  81. package/dist/{chunk-6S2CKZ4H.cjs → chunk-WFGDKBBP.cjs} +145 -51
  82. package/dist/chunk-WFGDKBBP.cjs.map +1 -0
  83. package/dist/{chunk-APZ6JFN6.cjs → chunk-Y5H4NOM6.cjs} +5 -5
  84. package/dist/{chunk-APZ6JFN6.cjs.map → chunk-Y5H4NOM6.cjs.map} +1 -1
  85. package/dist/{chunk-IQ55QNBI.js → chunk-YIXO6C4Z.js} +125 -44
  86. package/dist/chunk-YIXO6C4Z.js.map +1 -0
  87. package/dist/{chunk-LK5VXNMR.cjs → chunk-ZUE32Z67.cjs} +18 -18
  88. package/dist/{chunk-LK5VXNMR.cjs.map → chunk-ZUE32Z67.cjs.map} +1 -1
  89. package/dist/components/ActivityPreview.cjs +17 -17
  90. package/dist/components/ActivityPreview.d.cts +2 -2
  91. package/dist/components/ActivityPreview.d.ts +2 -2
  92. package/dist/components/ActivityPreview.js +16 -16
  93. package/dist/components/ActivitySequence.cjs +18 -18
  94. package/dist/components/ActivitySequence.d.cts +22 -4
  95. package/dist/components/ActivitySequence.d.ts +22 -4
  96. package/dist/components/ActivitySequence.js +17 -17
  97. package/dist/components/Dictation.cjs +10 -10
  98. package/dist/components/Dictation.d.cts +1 -1
  99. package/dist/components/Dictation.d.ts +1 -1
  100. package/dist/components/Dictation.js +9 -9
  101. package/dist/components/FillInTheBlanks.cjs +10 -10
  102. package/dist/components/FillInTheBlanks.d.cts +1 -1
  103. package/dist/components/FillInTheBlanks.d.ts +1 -1
  104. package/dist/components/FillInTheBlanks.js +9 -9
  105. package/dist/components/GapSelect.cjs +10 -10
  106. package/dist/components/GapSelect.d.cts +1 -1
  107. package/dist/components/GapSelect.d.ts +1 -1
  108. package/dist/components/GapSelect.js +9 -9
  109. package/dist/components/InteractiveVideo.cjs +17 -17
  110. package/dist/components/InteractiveVideo.d.cts +19 -3
  111. package/dist/components/InteractiveVideo.d.ts +19 -3
  112. package/dist/components/InteractiveVideo.js +15 -15
  113. package/dist/components/MultipleChoice.cjs +10 -10
  114. package/dist/components/MultipleChoice.d.cts +1 -1
  115. package/dist/components/MultipleChoice.d.ts +1 -1
  116. package/dist/components/MultipleChoice.js +9 -9
  117. package/dist/components/PronunciationFeedback.cjs +4 -4
  118. package/dist/components/PronunciationFeedback.d.cts +1 -1
  119. package/dist/components/PronunciationFeedback.d.ts +1 -1
  120. package/dist/components/PronunciationFeedback.js +3 -3
  121. package/dist/components/ReadAloud.cjs +8 -8
  122. package/dist/components/ReadAloud.d.cts +2 -2
  123. package/dist/components/ReadAloud.d.ts +2 -2
  124. package/dist/components/ReadAloud.js +7 -7
  125. package/dist/components/StimulusPanel.cjs +4 -4
  126. package/dist/components/StimulusPanel.d.cts +1 -1
  127. package/dist/components/StimulusPanel.d.ts +1 -1
  128. package/dist/components/StimulusPanel.js +3 -3
  129. package/dist/components/WrittenResponse.cjs +9 -7
  130. package/dist/components/WrittenResponse.d.cts +3 -3
  131. package/dist/components/WrittenResponse.d.ts +3 -3
  132. package/dist/components/WrittenResponse.js +8 -6
  133. package/dist/i18n/LkIntlProvider.cjs +7 -7
  134. package/dist/i18n/LkIntlProvider.d.cts +2 -2
  135. package/dist/i18n/LkIntlProvider.d.ts +2 -2
  136. package/dist/i18n/LkIntlProvider.js +1 -1
  137. package/dist/index.cjs +45 -41
  138. package/dist/index.cjs.map +1 -1
  139. package/dist/index.d.cts +3 -3
  140. package/dist/index.d.ts +3 -3
  141. package/dist/index.js +19 -19
  142. package/dist/index.js.map +1 -1
  143. package/dist/theme/skin.css +78 -0
  144. package/dist/{types-B_PCcqO-.d.cts → types-CO0XLEN6.d.cts} +67 -1
  145. package/dist/{types-C-GMCJ0g.d.ts → types-CrtIcRcK.d.ts} +67 -1
  146. package/package.json +3 -3
  147. package/dist/chunk-3XP6VO4G.js.map +0 -1
  148. package/dist/chunk-5GI53C6T.js.map +0 -1
  149. package/dist/chunk-6S2CKZ4H.cjs.map +0 -1
  150. package/dist/chunk-AG7MWZYZ.js.map +0 -1
  151. package/dist/chunk-AHGB5HH6.cjs.map +0 -1
  152. package/dist/chunk-AQIAQHJC.js.map +0 -1
  153. package/dist/chunk-BGA2IVQY.js.map +0 -1
  154. package/dist/chunk-BJLQKJZX.cjs.map +0 -1
  155. package/dist/chunk-CEUEXISZ.cjs.map +0 -1
  156. package/dist/chunk-DZVF3IGB.cjs.map +0 -1
  157. package/dist/chunk-HI6BUUIJ.js.map +0 -1
  158. package/dist/chunk-HREA5SMB.cjs.map +0 -1
  159. package/dist/chunk-IQ55QNBI.js.map +0 -1
  160. package/dist/chunk-IXMT7X3E.cjs.map +0 -1
  161. package/dist/chunk-JLDSCHY5.cjs.map +0 -1
  162. package/dist/chunk-NOC2M6LU.cjs +0 -118
  163. package/dist/chunk-NOC2M6LU.cjs.map +0 -1
  164. package/dist/chunk-ORBU3XKH.js.map +0 -1
  165. package/dist/chunk-PWLJDP3X.cjs.map +0 -1
  166. package/dist/chunk-QKW2GGTV.js.map +0 -1
  167. package/dist/chunk-RUS42NMN.js +0 -115
  168. package/dist/chunk-RUS42NMN.js.map +0 -1
  169. package/dist/chunk-TRMWBJWT.cjs.map +0 -1
  170. package/dist/chunk-UHIOVOFK.cjs.map +0 -1
  171. package/dist/chunk-WBLYD5QA.js.map +0 -1
  172. package/dist/chunk-YNDKAYW3.js.map +0 -1
  173. package/dist/chunk-YRNEOBXA.js.map +0 -1
  174. package/dist/chunk-ZDYNAWDZ.cjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
- var chunkAHGB5HH6_cjs = require('./chunk-AHGB5HH6.cjs');
4
+ var chunkNFGUJ52G_cjs = require('./chunk-NFGUJ52G.cjs');
5
5
  var lkCore = require('@intellectif/lk-core');
6
6
  var react = require('react');
7
7
  var jsxRuntime = require('react/jsx-runtime');
@@ -71,7 +71,7 @@ function AudioTransport({
71
71
  const statusId = react.useId();
72
72
  usePlaybackGroup(elementRef, playbackGroup);
73
73
  const suppliedStrings = mediaBudget?.strings ?? mediaStrings;
74
- const fromProvider = chunkAHGB5HH6_cjs.useLkStrings(strings);
74
+ const fromProvider = chunkNFGUJ52G_cjs.useLkStrings(strings);
75
75
  const s = {
76
76
  ...fromProvider.media,
77
77
  ...mediaStrings,
@@ -536,7 +536,7 @@ function AudioTransport({
536
536
  }
537
537
  function ActivityMedia(props) {
538
538
  const { media, renderMode = "practice", playbackGroup } = props;
539
- const s = chunkAHGB5HH6_cjs.useLkStrings(props.strings);
539
+ const s = chunkNFGUJ52G_cjs.useLkStrings(props.strings);
540
540
  const audioRef = react.useRef(null);
541
541
  usePlaybackGroup(audioRef, media.type === "audio" ? playbackGroup : void 0);
542
542
  if (media.type === "image") {
@@ -595,5 +595,5 @@ function ActivityMedia(props) {
595
595
  }
596
596
 
597
597
  exports.ActivityMedia = ActivityMedia;
598
- //# sourceMappingURL=chunk-L7WY5YFQ.cjs.map
599
- //# sourceMappingURL=chunk-L7WY5YFQ.cjs.map
598
+ //# sourceMappingURL=chunk-UKUHVCZX.cjs.map
599
+ //# sourceMappingURL=chunk-UKUHVCZX.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/shared/playback-group.ts","../src/components/shared/AudioTransport.tsx","../src/components/shared/ActivityMedia.tsx"],"names":["useEffect","useRef","useId","useLkStrings","useState","useCallback","jsxs","jsx","resolvePlaybackPolicy"],"mappings":"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","file":"chunk-L7WY5YFQ.cjs","sourcesContent":["'use client';\n\nimport { type RefObject, useEffect } from 'react';\n\n/**\n * Media elements that must not play at the same time, keyed by group name.\n *\n * Module-level on purpose: the two players of a dictation are two separate\n * `<ActivityMedia>` mounts with no common ancestor that owns playback, and a\n * context would have to be threaded through every activity component to reach\n * them. A map keyed by a group name is the smaller mechanism, and it costs\n * nothing to the components that do not name a group. The caller mints the\n * name per mount — never from the activity id, which two renderings of one\n * item share, and never from `useId`, which two separately hydrated roots\n * share.\n */\nconst groups = new Map<string, Set<HTMLMediaElement>>();\n\n/** Adds an element to a group; returns the matching removal. */\nexport function joinPlaybackGroup(group: string, element: HTMLMediaElement): () => void {\n let members = groups.get(group);\n if (members === undefined) {\n members = new Set();\n groups.set(group, members);\n }\n members.add(element);\n return () => {\n const current = groups.get(group);\n if (current === undefined) {\n return;\n }\n current.delete(element);\n if (current.size === 0) {\n groups.delete(group);\n }\n };\n}\n\n/**\n * Pauses every OTHER member of the group. Called from a player's own `play`\n * event, so whichever recording the learner starts silences the rest.\n *\n * Pausing charges nothing: a budgeted play is charged inside the starting\n * player's own handler, and pausing another player neither refunds nor starts\n * anything — its transport records the position, so a later resume is a\n * resume and not a second play.\n */\nexport function pausePlaybackGroupOthers(group: string, element: HTMLMediaElement): void {\n const members = groups.get(group);\n if (members === undefined) {\n return;\n }\n for (const member of members) {\n if (member !== element && !member.paused) {\n member.pause();\n }\n }\n}\n\n/**\n * Keeps `ref`'s element registered in `group` for as long as both exist.\n *\n * Re-registers after every commit rather than on a dependency list: the\n * element behind a ref can change while the ref object does not — a player\n * that swaps the SDK transport for the native bar when `renderMode` turns to\n * `review` mounts a new `<audio>` — and a list of `[ref, group]` would leave\n * the new element out of the group. Leaving and joining is a set delete and a\n * set add.\n */\nexport function usePlaybackGroup(\n ref: RefObject<HTMLMediaElement | null>,\n group: string | undefined,\n): void {\n useEffect(() => {\n const element = ref.current;\n if (group === undefined || element === null) {\n return;\n }\n return joinPlaybackGroup(group, element);\n });\n}\n","'use client';\n\nimport type {\n ActivityMedia as ActivityMediaData,\n MediaPlayClaim,\n ResolvedPlaybackPolicy,\n} from '@intellectif/lk-core';\nimport { useCallback, useEffect, useId, useRef, useState } from 'react';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport type { LkStringsOverride } from '../../i18n/strings.js';\nimport type { MediaBudgetBinding, MediaTransportStrings } from '../types.js';\nimport { pausePlaybackGroupOthers, usePlaybackGroup } from './playback-group.js';\n\n/**\n * The SDK's own audio player, used whenever a policy has something to enforce.\n *\n * The browser's control bar cannot express a spent budget — its play button\n * stays enabled — and a control that looks operable and does nothing is a\n * WCAG 3.2.2 / 4.1.3 failure, so an enforcing policy replaces the bar rather\n * than lying with it. What replaces it is a SUPERSET minus what the policy\n * removes on purpose: play/pause, elapsed and total time, mute, volume, an\n * optional speed control, an optional scrubber, and a live plays-remaining\n * status. Volume and mute are never restricted by any policy — they affect no\n * assessment property, and a learner in a lab with a locked OS volume has no\n * other lever once the native bar is gone.\n */\n\n/** A resume is a start within a quarter-second of where playback last stopped. */\nconst RESUME_EPSILON = 0.25;\n/** Below this, a seek is the browser settling rather than the learner moving. */\nconst SEEK_EPSILON = 0.35;\n/** A charged play that errors before this much audio played produced nothing. */\nconst NO_PROGRESS = 0.25;\n\nconst formatTime = (seconds: number): string => {\n if (!Number.isFinite(seconds) || seconds < 0) {\n return '0:00';\n }\n const whole = Math.floor(seconds);\n return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, '0')}`;\n};\n\nexport interface AudioTransportProps {\n media: ActivityMediaData;\n policy: ResolvedPlaybackPolicy;\n renderMode?: 'practice' | 'exam' | 'review';\n disabled?: boolean;\n locale?: string;\n mediaBudget?: MediaBudgetBinding;\n mediaStrings?: Partial<MediaTransportStrings>;\n /** Overrides the SDK's chrome text. See {@link LkIntlProvider}. */\n strings?: LkStringsOverride;\n onInteraction?: (event: {\n type: string;\n activityId: string;\n timestamp: number;\n payload: Record<string, unknown>;\n }) => void;\n /** Starting this player pauses every other audio element in the named group. */\n playbackGroup?: string;\n}\n\nexport function AudioTransport({\n media,\n policy,\n renderMode = 'practice',\n disabled = false,\n locale,\n mediaBudget,\n mediaStrings,\n strings,\n onInteraction,\n playbackGroup,\n}: AudioTransportProps): React.JSX.Element {\n const elementRef = useRef<HTMLAudioElement>(null);\n const statusId = useId();\n usePlaybackGroup(elementRef, playbackGroup);\n\n // Provider first, then the narrower props that shipped in 0.8.0 — so an\n // existing `mediaStrings` call site still wins over a provider-wide default\n // and keeps behaving exactly as it did.\n const suppliedStrings = mediaBudget?.strings ?? mediaStrings;\n const fromProvider = useLkStrings(strings);\n const s: MediaTransportStrings = {\n ...fromProvider.media,\n ...mediaStrings,\n ...mediaBudget?.strings,\n };\n\n const enforced = mediaBudget?.enforced ?? renderMode !== 'review';\n const budgeted = enforced && policy.maxPlays !== null && mediaBudget !== undefined;\n const maxPlays = policy.maxPlays ?? 0;\n\n // Written synchronously inside handlers and mirrored into state for\n // rendering, for the same reason `ActivitySequence` keeps `outcomesRef` in a\n // ref: two events in one tick would otherwise both read the pre-update value.\n const usedRef = useRef(mediaBudget?.entry?.plays ?? 0);\n const pendingRef = useRef(false);\n const resumeAtRef = useRef<number | null>(null);\n const highWaterRef = useRef(0);\n const chargeRef = useRef<{ claim: MediaPlayClaim; at: number } | null>(null);\n const seededRef = useRef(false);\n const lastPositionRef = useRef(-1);\n // Marks a `play()` this component itself issued after charging. The element's\n // `play` event is where the budget is enforced — so that a media key or a\n // scripted `play()` is charged too — which means our OWN start re-enters it.\n // In the confirmed tier `pendingRef` covered that; in the optimistic tier\n // nothing did, and `remaining` is still the pre-charge value in that closure,\n // so a single press was charged twice.\n const selfStartRef = useRef(false);\n // Whether the play under way is one this transport let run: paid for, or\n // free by policy. Only a pause of such a play is a place to resume from. A\n // start the player refuses (its budget spent, the player disabled, a grant\n // still out) is paused before anything is paid, and a resume point left\n // behind one made the next press a play nobody paid for. Decided by what was\n // let run, never by the order `play` and `pause` arrive in, which differs\n // between browsers, and between a browser and a test double.\n const paidRef = useRef(false);\n\n const [used, setUsed] = useState(usedRef.current);\n const [playing, setPlaying] = useState(false);\n const [pending, setPending] = useState(false);\n const [elapsed, setElapsed] = useState(0);\n const [duration, setDuration] = useState(0);\n const [muted, setMuted] = useState(false);\n const [volume, setVolume] = useState(1);\n const [confirming, setConfirming] = useState(false);\n const [notice, setNotice] = useState('');\n // A repeated identical message must announce again, so the alert node is\n // REPLACED rather than mutated. Screen readers ignore an unchanged one.\n const [noticeNonce, setNoticeNonce] = useState(0);\n\n const remaining = Math.max(0, maxPlays - used);\n\n const announce = useCallback((message: string) => {\n setNotice(message);\n setNoticeNonce((n) => n + 1);\n }, []);\n\n const emit = useCallback(\n (type: string, playsUsed: number) => {\n if (!budgeted || mediaBudget === undefined || onInteraction === undefined) {\n return;\n }\n onInteraction({\n type,\n activityId: mediaBudget.activityId ?? mediaBudget.slotId,\n timestamp: Date.now(),\n payload: {\n mediaKey: mediaBudget.key,\n mediaType: 'audio',\n playsUsed,\n maxPlays,\n playsRemaining: Math.max(0, maxPlays - playsUsed),\n },\n });\n },\n [budgeted, mediaBudget, maxPlays, onInteraction],\n );\n\n const reportPosition = useCallback(\n (seconds: number) => {\n mediaBudget?.onPosition?.(mediaBudget.key, seconds);\n },\n [mediaBudget],\n );\n\n /** A start within a hair of where playback last stopped is a resume, not a play. */\n const isResume = useCallback((element: HTMLAudioElement): boolean => {\n const at = resumeAtRef.current;\n return at !== null && Math.abs(element.currentTime - at) < RESUME_EPSILON;\n }, []);\n\n const buildClaim = useCallback((): MediaPlayClaim | null => {\n if (mediaBudget === undefined) {\n return null;\n }\n return {\n key: mediaBudget.key,\n previousPlaysUsed: usedRef.current,\n playsUsed: usedRef.current + 1,\n maxPlays,\n playsRemaining: Math.max(0, maxPlays - (usedRef.current + 1)),\n slotId: mediaBudget.slotId,\n index: mediaBudget.index,\n ...(mediaBudget.activityId !== undefined ? { activityId: mediaBudget.activityId } : {}),\n };\n }, [mediaBudget, maxPlays]);\n\n const refuse = useCallback(() => {\n announce(s.noPlaysRemaining);\n emit('media-play-refused', usedRef.current);\n }, [announce, emit, s.noPlaysRemaining]);\n\n const startPlayback = useCallback(() => {\n const element = elementRef.current;\n if (element === null) {\n return;\n }\n highWaterRef.current = element.currentTime;\n selfStartRef.current = true;\n void element.play().catch(() => {\n selfStartRef.current = false;\n /* autoplay policies and races surface through the `error` handler */\n });\n }, []);\n\n const commit = useCallback(\n (claim: MediaPlayClaim) => {\n const element = elementRef.current;\n chargeRef.current = { claim, at: element?.currentTime ?? 0 };\n usedRef.current = claim.playsUsed;\n setUsed(claim.playsUsed);\n emit('media-play-consumed', claim.playsUsed);\n },\n [emit],\n );\n\n /**\n * Charges a play, then starts it.\n *\n * The confirmed tier holds playback until the consumer's atomic write\n * settles, which is the only construction that catches a second tab: two\n * mounts seeded at 0 both claim 1, and only the server can tell them apart.\n */\n const claimAndStart = useCallback(\n (element: HTMLAudioElement, alreadyPlaying: boolean) => {\n // The one place a play is charged, so the one place a disabled player\n // refuses one. The button and the element's own `play` event check too,\n // but the last-play confirmation calls straight in here: a confirmation\n // left open when the player was disabled — by a read-aloud starting a\n // take, say — would otherwise charge a play the next moment silences.\n if (disabled) {\n if (alreadyPlaying) {\n element.pause();\n }\n return;\n }\n const claim = buildClaim();\n if (claim === null) {\n return;\n }\n const result = mediaBudget?.onPlayConsumed?.(claim);\n\n if (result !== undefined && typeof (result as Promise<unknown>).then === 'function') {\n if (alreadyPlaying) {\n element.pause();\n }\n pendingRef.current = true;\n setPending(true);\n void (result as Promise<{ playsUsed: number } | undefined>)\n .then((grant) => {\n const granted =\n grant !== undefined && Number.isInteger(grant.playsUsed)\n ? grant.playsUsed\n : claim.playsUsed;\n if (granted > maxPlays) {\n usedRef.current = granted;\n setUsed(granted);\n refuse();\n return;\n }\n commit({ ...claim, playsUsed: granted });\n startPlayback();\n })\n .catch(() => {\n // Nothing charged: the learner may retry.\n announce(s.playFailed);\n emit('media-play-errored', usedRef.current);\n })\n .finally(() => {\n pendingRef.current = false;\n setPending(false);\n });\n return;\n }\n\n commit(claim);\n if (alreadyPlaying) {\n paidRef.current = true;\n } else {\n startPlayback();\n }\n },\n [\n disabled,\n buildClaim,\n mediaBudget,\n maxPlays,\n commit,\n startPlayback,\n refuse,\n announce,\n emit,\n s.playFailed,\n ],\n );\n\n const handleButton = useCallback(() => {\n const element = elementRef.current;\n if (element === null || pendingRef.current || disabled) {\n return;\n }\n if (!element.paused) {\n element.pause();\n return;\n }\n if (!budgeted || isResume(element)) {\n startPlayback();\n return;\n }\n if (remaining === 0) {\n refuse();\n return;\n }\n if (remaining === 1 && !confirming) {\n setConfirming(true);\n return;\n }\n setConfirming(false);\n claimAndStart(element, false);\n }, [disabled, budgeted, isResume, startPlayback, remaining, refuse, confirming, claimAndStart]);\n\n // Seed the restored position once metadata is known, BEFORE the seek guard\n // can see it — otherwise the guard reverts the very restore it was given.\n const handleLoadedMetadata = useCallback(() => {\n const element = elementRef.current;\n if (element === null) {\n return;\n }\n setDuration(Number.isFinite(element.duration) ? element.duration : 0);\n const at = mediaBudget?.entry?.at;\n if (!seededRef.current && typeof at === 'number' && Number.isFinite(at) && at > 0) {\n seededRef.current = true;\n highWaterRef.current = at;\n resumeAtRef.current = at;\n element.currentTime = at;\n setElapsed(at);\n }\n }, [mediaBudget]);\n\n /**\n * The budget lives on the ELEMENT's `play` event, not on the button, so a\n * hardware media key, a browser extension or a scripted `el.play()` is\n * charged too — and an exhausted budget stops playback inside the event,\n * before a sample is audible.\n */\n const handlePlay = useCallback(() => {\n const element = elementRef.current;\n if (element === null) {\n return;\n }\n if (disabled) {\n element.pause();\n return;\n }\n // Before the budget: whichever recording starts silences the others in\n // its group, and a refused start below still leaves them paused — two\n // recordings of one sentence must never sound at once.\n if (playbackGroup !== undefined) {\n pausePlaybackGroupOthers(playbackGroup, element);\n }\n if (selfStartRef.current) {\n selfStartRef.current = false;\n paidRef.current = true;\n setPlaying(true);\n return;\n }\n if (pendingRef.current) {\n setPlaying(true);\n return;\n }\n if (isResume(element)) {\n paidRef.current = true;\n setPlaying(true);\n resumeAtRef.current = null;\n return;\n }\n if (!budgeted) {\n paidRef.current = true;\n setPlaying(true);\n return;\n }\n if (remaining === 0) {\n element.pause();\n refuse();\n return;\n }\n setPlaying(true);\n claimAndStart(element, true);\n }, [disabled, playbackGroup, isResume, budgeted, remaining, refuse, claimAndStart]);\n\n const handlePause = useCallback(() => {\n const element = elementRef.current;\n setPlaying(false);\n if (element === null) {\n return;\n }\n // A play the transport never let run is no place to resume from: the\n // position a paid-for play last stopped at stands.\n if (!paidRef.current) {\n return;\n }\n paidRef.current = false;\n // Every pause records the position — learner, browser, or the pager's own\n // pause as a pane hides. Without this, paging through a six-question\n // listening group and back would exhaust the budget by navigation alone.\n resumeAtRef.current = element.currentTime;\n reportPosition(element.currentTime);\n }, [reportPosition]);\n\n const handleEnded = useCallback(() => {\n paidRef.current = false;\n setPlaying(false);\n resumeAtRef.current = null;\n highWaterRef.current = 0;\n chargeRef.current = null;\n setElapsed(0);\n reportPosition(0);\n }, [reportPosition]);\n\n const handleTimeUpdate = useCallback(() => {\n const element = elementRef.current;\n if (element === null) {\n return;\n }\n setElapsed(element.currentTime);\n highWaterRef.current = Math.max(highWaterRef.current, element.currentTime);\n const whole = Math.floor(element.currentTime);\n if (whole !== lastPositionRef.current) {\n lastPositionRef.current = whole;\n reportPosition(element.currentTime);\n }\n }, [reportPosition]);\n\n const handleSeeking = useCallback(() => {\n const element = elementRef.current;\n if (element === null || policy.seek !== 'none') {\n return;\n }\n if (Math.abs(element.currentTime - highWaterRef.current) > SEEK_EPSILON) {\n element.currentTime = highWaterRef.current;\n announce(s.seekBlocked);\n }\n }, [policy.seek, announce, s.seekBlocked]);\n\n const handleRateChange = useCallback(() => {\n const element = elementRef.current;\n if (element === null || policy.rate !== 'fixed' || element.playbackRate === 1) {\n return;\n }\n element.playbackRate = 1;\n announce(s.rateBlocked);\n }, [policy.rate, announce, s.rateBlocked]);\n\n const handleError = useCallback(() => {\n const charge = chargeRef.current;\n const element = elementRef.current;\n // Ordering matters: an element can error with nothing charged (a bad URL\n // the learner never pressed play on), so the charge must be proven to\n // exist before its position is read.\n const progressed =\n charge !== null && element !== null && element.currentTime - charge.at >= NO_PROGRESS;\n if (charge !== null && !progressed) {\n if (mediaBudget?.onPlayRefunded !== undefined) {\n usedRef.current = charge.claim.previousPlaysUsed;\n setUsed(charge.claim.previousPlaysUsed);\n mediaBudget.onPlayRefunded(charge.claim);\n emit('media-play-refunded', charge.claim.previousPlaysUsed);\n } else {\n emit('media-play-errored', usedRef.current);\n }\n }\n chargeRef.current = null;\n setPlaying(false);\n announce(s.playFailed);\n }, [mediaBudget, emit, announce, s.playFailed]);\n\n // Keep the element's volume in step with the controls.\n useEffect(() => {\n const element = elementRef.current;\n if (element !== null) {\n element.volume = volume;\n element.muted = muted;\n }\n }, [volume, muted]);\n\n const exhausted = budgeted && remaining === 0;\n const buttonLabel = pending ? s.preparing : playing ? s.pause : s.play;\n\n return (\n <figure className=\"lk-media\" data-controls=\"minimal\">\n {/* biome-ignore lint/a11y/useMediaCaption: captions are optional in the data contract — a <track> is rendered when captionsUrl is provided; absence is the author's documented choice (Req 14.5) */}\n <audio\n ref={elementRef}\n className=\"lk-media-el\"\n preload=\"metadata\"\n aria-label={media.alt || undefined}\n onLoadedMetadata={handleLoadedMetadata}\n onPlay={handlePlay}\n onPause={handlePause}\n onEnded={handleEnded}\n onTimeUpdate={handleTimeUpdate}\n onSeeking={handleSeeking}\n onRateChange={handleRateChange}\n onError={handleError}\n >\n <source src={media.url} />\n {media.captionsUrl ? <track kind=\"captions\" src={media.captionsUrl} default /> : null}\n </audio>\n\n {/* A timeline runs with the audio, not with the script. Forcing LTR here\n fixes seek direction for RTL locales by construction; the surrounding\n text keeps the document direction. */}\n <div\n className=\"lk-media-transport\"\n dir=\"ltr\"\n {...(suppliedStrings !== undefined && locale !== undefined ? { lang: locale } : {})}\n >\n <button\n type=\"button\"\n className=\"lk-media-play\"\n onClick={handleButton}\n // NEVER `disabled`: a natively disabled button leaves the focus\n // order, so a learner who tabs into nothing is told nothing — and\n // disabling it mid-attempt would drop focus to <body>. Kept\n // focusable, it re-announces the refusal when pressed.\n aria-disabled={exhausted || disabled || undefined}\n aria-busy={pending || undefined}\n aria-label={exhausted ? `${s.play} — ${s.noPlaysRemaining}` : undefined}\n aria-describedby={budgeted ? statusId : undefined}\n >\n {buttonLabel}\n </button>\n\n <span className=\"lk-media-time\">\n {formatTime(elapsed)} / {formatTime(duration)}\n </span>\n\n {policy.seek === 'allow' ? (\n <input\n type=\"range\"\n className=\"lk-media-scrub\"\n min={0}\n max={duration || 0}\n step={0.1}\n value={Math.min(elapsed, duration || 0)}\n aria-label={s.seek}\n aria-valuetext={s.timeValue(formatTime(elapsed), formatTime(duration))}\n onChange={(event) => {\n const element = elementRef.current;\n if (element !== null) {\n element.currentTime = Number(event.target.value);\n }\n }}\n />\n ) : null}\n\n <button\n type=\"button\"\n className=\"lk-media-mute\"\n aria-pressed={muted}\n onClick={() => setMuted((m) => !m)}\n >\n {muted ? s.unmute : s.mute}\n </button>\n\n <input\n type=\"range\"\n className=\"lk-media-volume\"\n min={0}\n max={1}\n step={0.05}\n value={volume}\n aria-label={s.volume}\n onChange={(event) => setVolume(Number(event.target.value))}\n />\n\n {policy.rate === 'allow' ? (\n <select\n className=\"lk-media-rate\"\n aria-label={s.speed}\n defaultValue=\"1\"\n onChange={(event) => {\n const element = elementRef.current;\n if (element !== null) {\n element.playbackRate = Number(event.target.value);\n }\n }}\n >\n {['0.75', '1', '1.25', '1.5'].map((rate) => (\n <option key={rate} value={rate}>\n {rate}×\n </option>\n ))}\n </select>\n ) : null}\n </div>\n\n {budgeted ? (\n <p className=\"lk-media-plays\" id={statusId} role=\"status\">\n {remaining > 0 ? s.playsRemaining(remaining, maxPlays) : s.noPlaysRemaining}\n </p>\n ) : null}\n\n {/* Separate from the polite status on purpose: a refusal is a direct\n response to a user action and must not be the update a screen reader\n drops when two polite regions compete. */}\n <p className=\"lk-media-notice\" role=\"alert\" key={noticeNonce}>\n {notice}\n </p>\n\n {confirming ? (\n <fieldset className=\"lk-media-confirm\">\n <legend>{s.lastPlayConfirm}</legend>\n <button\n type=\"button\"\n className=\"lk-media-confirm-start\"\n // biome-ignore lint/a11y/noAutofocus: focus must land on the confirm action the press opened, or a keyboard learner is stranded on a button whose meaning just changed\n autoFocus\n onClick={() => {\n const element = elementRef.current;\n setConfirming(false);\n if (element !== null) {\n claimAndStart(element, false);\n }\n }}\n >\n {s.lastPlayStart}\n </button>\n <button\n type=\"button\"\n className=\"lk-media-confirm-cancel\"\n onClick={() => setConfirming(false)}\n >\n {s.lastPlayCancel}\n </button>\n </fieldset>\n ) : null}\n </figure>\n );\n}\n","import type { ActivityMedia as ActivityMediaData, InteractionEvent } from '@intellectif/lk-core';\nimport { resolvePlaybackPolicy } from '@intellectif/lk-core';\nimport { useRef } from 'react';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport type { LkStringsOverride } from '../../i18n/strings.js';\nimport type { MediaBudgetBinding, MediaTransportStrings, RenderMode } from '../types.js';\nimport { AudioTransport } from './AudioTransport.js';\nimport { pausePlaybackGroupOthers, usePlaybackGroup } from './playback-group.js';\n\n/**\n * Presentational media block shown above a question or passage. URL-only:\n * the SDK does not host media (consumer responsibility). Accessibility:\n * images use `alt` (schema requires it non-empty); audio/video expose an\n * optional label and a captions `<track>` when `captionsUrl` is provided;\n * `embed` renders a responsive sandboxed iframe with `alt` as its required\n * accessible `title` (Req 14.5).\n *\n * Audio additionally carries a playback policy (`media.playback`). With no\n * policy the emitted element is character-identical to what every release\n * before 0.8.0 produced; with something to enforce, the browser's control bar\n * is replaced by {@link AudioTransport}.\n */\nexport interface ActivityMediaProps {\n media: ActivityMediaData;\n renderMode?: RenderMode;\n disabled?: boolean;\n locale?: string;\n mediaBudget?: MediaBudgetBinding;\n mediaStrings?: Partial<MediaTransportStrings>;\n /** Overrides the SDK's chrome text. See {@link LkIntlProvider}. */\n strings?: LkStringsOverride;\n onInteraction?: (event: InteractionEvent) => void;\n /**\n * Recordings that share a name never play together: starting this one\n * pauses every other audio element mounted under the same `playbackGroup`.\n * A dictation names one group per mount, so its normal and slow recordings\n * take turns. Pausing charges no play budget. Audio only.\n */\n playbackGroup?: string;\n}\n\nexport function ActivityMedia(props: ActivityMediaProps): React.JSX.Element {\n const { media, renderMode = 'practice', playbackGroup } = props;\n // Read before the branches: every return below is conditional, and the one\n // string this component owns itself (the embed's accessible name) is on the\n // last of them.\n const s = useLkStrings(props.strings);\n // The native audio bar's membership of a playback group. Registered from the\n // ref rather than from an id so two mounts of one recording stay distinct.\n const audioRef = useRef<HTMLAudioElement>(null);\n usePlaybackGroup(audioRef, media.type === 'audio' ? playbackGroup : undefined);\n\n if (media.type === 'image') {\n return (\n <figure className=\"lk-media\">\n <img className=\"lk-media-el\" src={media.url} alt={media.alt ?? ''} />\n </figure>\n );\n }\n\n if (media.type === 'audio') {\n const policy = resolvePlaybackPolicy(media);\n\n // BEFORE the branch below, not after it. A budgeted policy always resolves\n // to `controls: 'minimal'`, so a guard placed after that early return\n // could never fire for the one case it exists to catch — the transport\n // rendered happily with no binding, counting nothing, while the paper\n // believed it had a budget.\n if (policy.maxPlays !== null && renderMode === 'exam' && props.mediaBudget === undefined) {\n throw new Error(\n `ActivityMedia: audio ${JSON.stringify(media.url)} declares maxPlays and is rendered in ` +\n 'renderMode \"exam\" without a `mediaBudget` binding. Nothing would persist the count, so ' +\n 'a refresh restores the full budget while the page says plays remain. Render it through ' +\n '<ActivitySequence mediaBudget={…}> or pass `mediaBudget` yourself.',\n );\n }\n\n // REVIEW ALWAYS GETS THE NATIVE BAR. The paper is graded; a learner\n // reviewing it cannot change an answer by listening again, so taking away\n // their scrubber, their speed control and their browser-localized controls\n // buys nothing — and costs a learner with a processing disability the\n // ability to work out what they got wrong.\n if (policy.controls === 'minimal' && renderMode !== 'review') {\n return <AudioTransport {...props} media={media} policy={policy} />;\n }\n\n const hints = renderMode === 'review' ? [] : policy.nativeControlHints;\n return (\n <figure className=\"lk-media\">\n <audio\n ref={audioRef}\n className=\"lk-media-el\"\n controls\n aria-label={media.alt || undefined}\n {...(playbackGroup !== undefined\n ? {\n onPlay: (event: React.SyntheticEvent<HTMLAudioElement>) =>\n pausePlaybackGroupOthers(playbackGroup, event.currentTarget),\n }\n : {})}\n {...(hints.length > 0\n ? {\n controlsList: hints\n .map((hint) => (hint === 'hide-download' ? 'nodownload' : 'noplaybackrate'))\n .join(' '),\n }\n : {})}\n >\n <source src={media.url} />\n {media.captionsUrl ? <track kind=\"captions\" src={media.captionsUrl} default /> : null}\n </audio>\n </figure>\n );\n }\n\n if (media.type === 'embed') {\n return (\n <figure className=\"lk-media\">\n <div className=\"lk-media-embed\">\n <iframe\n className=\"lk-media-el\"\n src={media.url}\n title={media.alt ?? s.embeddedMedia}\n loading=\"lazy\"\n referrerPolicy=\"strict-origin-when-cross-origin\"\n // The docblock always promised a sandboxed iframe; now it is one.\n // allow-scripts + allow-same-origin are required by provider\n // players (YouTube/Vimeo); top-navigation, forms, downloads and\n // popups stay blocked. Scheme allow-listing in MediaSchema is the\n // primary defense; this is depth.\n sandbox=\"allow-scripts allow-same-origin allow-presentation\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n </div>\n </figure>\n );\n }\n\n return (\n <figure className=\"lk-media\">\n {/* biome-ignore lint/a11y/useMediaCaption: captions are optional in the data contract — a <track> is rendered when captionsUrl is provided; absence is the author's documented choice (Req 14.5) */}\n <video className=\"lk-media-el\" controls aria-label={media.alt || undefined}>\n <source src={media.url} />\n {media.captionsUrl ? <track kind=\"captions\" src={media.captionsUrl} default /> : null}\n </video>\n </figure>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/components/shared/playback-group.ts","../src/components/shared/AudioTransport.tsx","../src/components/shared/ActivityMedia.tsx"],"names":["useEffect","useRef","useId","useLkStrings","useState","useCallback","jsxs","jsx","resolvePlaybackPolicy"],"mappings":"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","file":"chunk-UKUHVCZX.cjs","sourcesContent":["'use client';\n\nimport { type RefObject, useEffect } from 'react';\n\n/**\n * Media elements that must not play at the same time, keyed by group name.\n *\n * Module-level on purpose: the two players of a dictation are two separate\n * `<ActivityMedia>` mounts with no common ancestor that owns playback, and a\n * context would have to be threaded through every activity component to reach\n * them. A map keyed by a group name is the smaller mechanism, and it costs\n * nothing to the components that do not name a group. The caller mints the\n * name per mount — never from the activity id, which two renderings of one\n * item share, and never from `useId`, which two separately hydrated roots\n * share.\n */\nconst groups = new Map<string, Set<HTMLMediaElement>>();\n\n/** Adds an element to a group; returns the matching removal. */\nexport function joinPlaybackGroup(group: string, element: HTMLMediaElement): () => void {\n let members = groups.get(group);\n if (members === undefined) {\n members = new Set();\n groups.set(group, members);\n }\n members.add(element);\n return () => {\n const current = groups.get(group);\n if (current === undefined) {\n return;\n }\n current.delete(element);\n if (current.size === 0) {\n groups.delete(group);\n }\n };\n}\n\n/**\n * Pauses every OTHER member of the group. Called from a player's own `play`\n * event, so whichever recording the learner starts silences the rest.\n *\n * Pausing charges nothing: a budgeted play is charged inside the starting\n * player's own handler, and pausing another player neither refunds nor starts\n * anything — its transport records the position, so a later resume is a\n * resume and not a second play.\n */\nexport function pausePlaybackGroupOthers(group: string, element: HTMLMediaElement): void {\n const members = groups.get(group);\n if (members === undefined) {\n return;\n }\n for (const member of members) {\n if (member !== element && !member.paused) {\n member.pause();\n }\n }\n}\n\n/**\n * Keeps `ref`'s element registered in `group` for as long as both exist.\n *\n * Re-registers after every commit rather than on a dependency list: the\n * element behind a ref can change while the ref object does not — a player\n * that swaps the SDK transport for the native bar when `renderMode` turns to\n * `review` mounts a new `<audio>` — and a list of `[ref, group]` would leave\n * the new element out of the group. Leaving and joining is a set delete and a\n * set add.\n */\nexport function usePlaybackGroup(\n ref: RefObject<HTMLMediaElement | null>,\n group: string | undefined,\n): void {\n useEffect(() => {\n const element = ref.current;\n if (group === undefined || element === null) {\n return;\n }\n return joinPlaybackGroup(group, element);\n });\n}\n","'use client';\n\nimport type {\n ActivityMedia as ActivityMediaData,\n MediaPlayClaim,\n ResolvedPlaybackPolicy,\n} from '@intellectif/lk-core';\nimport { useCallback, useEffect, useId, useRef, useState } from 'react';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport type { LkStringsOverride } from '../../i18n/strings.js';\nimport type { MediaBudgetBinding, MediaTransportStrings } from '../types.js';\nimport { pausePlaybackGroupOthers, usePlaybackGroup } from './playback-group.js';\n\n/**\n * The SDK's own audio player, used whenever a policy has something to enforce.\n *\n * The browser's control bar cannot express a spent budget — its play button\n * stays enabled — and a control that looks operable and does nothing is a\n * WCAG 3.2.2 / 4.1.3 failure, so an enforcing policy replaces the bar rather\n * than lying with it. What replaces it is a SUPERSET minus what the policy\n * removes on purpose: play/pause, elapsed and total time, mute, volume, an\n * optional speed control, an optional scrubber, and a live plays-remaining\n * status. Volume and mute are never restricted by any policy — they affect no\n * assessment property, and a learner in a lab with a locked OS volume has no\n * other lever once the native bar is gone.\n */\n\n/** A resume is a start within a quarter-second of where playback last stopped. */\nconst RESUME_EPSILON = 0.25;\n/** Below this, a seek is the browser settling rather than the learner moving. */\nconst SEEK_EPSILON = 0.35;\n/** A charged play that errors before this much audio played produced nothing. */\nconst NO_PROGRESS = 0.25;\n\nconst formatTime = (seconds: number): string => {\n if (!Number.isFinite(seconds) || seconds < 0) {\n return '0:00';\n }\n const whole = Math.floor(seconds);\n return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, '0')}`;\n};\n\nexport interface AudioTransportProps {\n media: ActivityMediaData;\n policy: ResolvedPlaybackPolicy;\n renderMode?: 'practice' | 'exam' | 'review';\n disabled?: boolean;\n locale?: string;\n mediaBudget?: MediaBudgetBinding;\n mediaStrings?: Partial<MediaTransportStrings>;\n /** Overrides the SDK's chrome text. See {@link LkIntlProvider}. */\n strings?: LkStringsOverride;\n onInteraction?: (event: {\n type: string;\n activityId: string;\n timestamp: number;\n payload: Record<string, unknown>;\n }) => void;\n /** Starting this player pauses every other audio element in the named group. */\n playbackGroup?: string;\n}\n\nexport function AudioTransport({\n media,\n policy,\n renderMode = 'practice',\n disabled = false,\n locale,\n mediaBudget,\n mediaStrings,\n strings,\n onInteraction,\n playbackGroup,\n}: AudioTransportProps): React.JSX.Element {\n const elementRef = useRef<HTMLAudioElement>(null);\n const statusId = useId();\n usePlaybackGroup(elementRef, playbackGroup);\n\n // Provider first, then the narrower props that shipped in 0.8.0 — so an\n // existing `mediaStrings` call site still wins over a provider-wide default\n // and keeps behaving exactly as it did.\n const suppliedStrings = mediaBudget?.strings ?? mediaStrings;\n const fromProvider = useLkStrings(strings);\n const s: MediaTransportStrings = {\n ...fromProvider.media,\n ...mediaStrings,\n ...mediaBudget?.strings,\n };\n\n const enforced = mediaBudget?.enforced ?? renderMode !== 'review';\n const budgeted = enforced && policy.maxPlays !== null && mediaBudget !== undefined;\n const maxPlays = policy.maxPlays ?? 0;\n\n // Written synchronously inside handlers and mirrored into state for\n // rendering, for the same reason `ActivitySequence` keeps `outcomesRef` in a\n // ref: two events in one tick would otherwise both read the pre-update value.\n const usedRef = useRef(mediaBudget?.entry?.plays ?? 0);\n const pendingRef = useRef(false);\n const resumeAtRef = useRef<number | null>(null);\n const highWaterRef = useRef(0);\n const chargeRef = useRef<{ claim: MediaPlayClaim; at: number } | null>(null);\n const seededRef = useRef(false);\n const lastPositionRef = useRef(-1);\n // Marks a `play()` this component itself issued after charging. The element's\n // `play` event is where the budget is enforced — so that a media key or a\n // scripted `play()` is charged too — which means our OWN start re-enters it.\n // In the confirmed tier `pendingRef` covered that; in the optimistic tier\n // nothing did, and `remaining` is still the pre-charge value in that closure,\n // so a single press was charged twice.\n const selfStartRef = useRef(false);\n // Whether the play under way is one this transport let run: paid for, or\n // free by policy. Only a pause of such a play is a place to resume from. A\n // start the player refuses (its budget spent, the player disabled, a grant\n // still out) is paused before anything is paid, and a resume point left\n // behind one made the next press a play nobody paid for. Decided by what was\n // let run, never by the order `play` and `pause` arrive in, which differs\n // between browsers, and between a browser and a test double.\n const paidRef = useRef(false);\n\n const [used, setUsed] = useState(usedRef.current);\n const [playing, setPlaying] = useState(false);\n const [pending, setPending] = useState(false);\n const [elapsed, setElapsed] = useState(0);\n const [duration, setDuration] = useState(0);\n const [muted, setMuted] = useState(false);\n const [volume, setVolume] = useState(1);\n const [confirming, setConfirming] = useState(false);\n const [notice, setNotice] = useState('');\n // A repeated identical message must announce again, so the alert node is\n // REPLACED rather than mutated. Screen readers ignore an unchanged one.\n const [noticeNonce, setNoticeNonce] = useState(0);\n\n const remaining = Math.max(0, maxPlays - used);\n\n const announce = useCallback((message: string) => {\n setNotice(message);\n setNoticeNonce((n) => n + 1);\n }, []);\n\n const emit = useCallback(\n (type: string, playsUsed: number) => {\n if (!budgeted || mediaBudget === undefined || onInteraction === undefined) {\n return;\n }\n onInteraction({\n type,\n activityId: mediaBudget.activityId ?? mediaBudget.slotId,\n timestamp: Date.now(),\n payload: {\n mediaKey: mediaBudget.key,\n mediaType: 'audio',\n playsUsed,\n maxPlays,\n playsRemaining: Math.max(0, maxPlays - playsUsed),\n },\n });\n },\n [budgeted, mediaBudget, maxPlays, onInteraction],\n );\n\n const reportPosition = useCallback(\n (seconds: number) => {\n mediaBudget?.onPosition?.(mediaBudget.key, seconds);\n },\n [mediaBudget],\n );\n\n /** A start within a hair of where playback last stopped is a resume, not a play. */\n const isResume = useCallback((element: HTMLAudioElement): boolean => {\n const at = resumeAtRef.current;\n return at !== null && Math.abs(element.currentTime - at) < RESUME_EPSILON;\n }, []);\n\n const buildClaim = useCallback((): MediaPlayClaim | null => {\n if (mediaBudget === undefined) {\n return null;\n }\n return {\n key: mediaBudget.key,\n previousPlaysUsed: usedRef.current,\n playsUsed: usedRef.current + 1,\n maxPlays,\n playsRemaining: Math.max(0, maxPlays - (usedRef.current + 1)),\n slotId: mediaBudget.slotId,\n index: mediaBudget.index,\n ...(mediaBudget.activityId !== undefined ? { activityId: mediaBudget.activityId } : {}),\n };\n }, [mediaBudget, maxPlays]);\n\n const refuse = useCallback(() => {\n announce(s.noPlaysRemaining);\n emit('media-play-refused', usedRef.current);\n }, [announce, emit, s.noPlaysRemaining]);\n\n const startPlayback = useCallback(() => {\n const element = elementRef.current;\n if (element === null) {\n return;\n }\n highWaterRef.current = element.currentTime;\n selfStartRef.current = true;\n void element.play().catch(() => {\n selfStartRef.current = false;\n /* autoplay policies and races surface through the `error` handler */\n });\n }, []);\n\n const commit = useCallback(\n (claim: MediaPlayClaim) => {\n const element = elementRef.current;\n chargeRef.current = { claim, at: element?.currentTime ?? 0 };\n usedRef.current = claim.playsUsed;\n setUsed(claim.playsUsed);\n emit('media-play-consumed', claim.playsUsed);\n },\n [emit],\n );\n\n /**\n * Charges a play, then starts it.\n *\n * The confirmed tier holds playback until the consumer's atomic write\n * settles, which is the only construction that catches a second tab: two\n * mounts seeded at 0 both claim 1, and only the server can tell them apart.\n */\n const claimAndStart = useCallback(\n (element: HTMLAudioElement, alreadyPlaying: boolean) => {\n // The one place a play is charged, so the one place a disabled player\n // refuses one. The button and the element's own `play` event check too,\n // but the last-play confirmation calls straight in here: a confirmation\n // left open when the player was disabled — by a read-aloud starting a\n // take, say — would otherwise charge a play the next moment silences.\n if (disabled) {\n if (alreadyPlaying) {\n element.pause();\n }\n return;\n }\n const claim = buildClaim();\n if (claim === null) {\n return;\n }\n const result = mediaBudget?.onPlayConsumed?.(claim);\n\n if (result !== undefined && typeof (result as Promise<unknown>).then === 'function') {\n if (alreadyPlaying) {\n element.pause();\n }\n pendingRef.current = true;\n setPending(true);\n void (result as Promise<{ playsUsed: number } | undefined>)\n .then((grant) => {\n const granted =\n grant !== undefined && Number.isInteger(grant.playsUsed)\n ? grant.playsUsed\n : claim.playsUsed;\n if (granted > maxPlays) {\n usedRef.current = granted;\n setUsed(granted);\n refuse();\n return;\n }\n commit({ ...claim, playsUsed: granted });\n startPlayback();\n })\n .catch(() => {\n // Nothing charged: the learner may retry.\n announce(s.playFailed);\n emit('media-play-errored', usedRef.current);\n })\n .finally(() => {\n pendingRef.current = false;\n setPending(false);\n });\n return;\n }\n\n commit(claim);\n if (alreadyPlaying) {\n paidRef.current = true;\n } else {\n startPlayback();\n }\n },\n [\n disabled,\n buildClaim,\n mediaBudget,\n maxPlays,\n commit,\n startPlayback,\n refuse,\n announce,\n emit,\n s.playFailed,\n ],\n );\n\n const handleButton = useCallback(() => {\n const element = elementRef.current;\n if (element === null || pendingRef.current || disabled) {\n return;\n }\n if (!element.paused) {\n element.pause();\n return;\n }\n if (!budgeted || isResume(element)) {\n startPlayback();\n return;\n }\n if (remaining === 0) {\n refuse();\n return;\n }\n if (remaining === 1 && !confirming) {\n setConfirming(true);\n return;\n }\n setConfirming(false);\n claimAndStart(element, false);\n }, [disabled, budgeted, isResume, startPlayback, remaining, refuse, confirming, claimAndStart]);\n\n // Seed the restored position once metadata is known, BEFORE the seek guard\n // can see it — otherwise the guard reverts the very restore it was given.\n const handleLoadedMetadata = useCallback(() => {\n const element = elementRef.current;\n if (element === null) {\n return;\n }\n setDuration(Number.isFinite(element.duration) ? element.duration : 0);\n const at = mediaBudget?.entry?.at;\n if (!seededRef.current && typeof at === 'number' && Number.isFinite(at) && at > 0) {\n seededRef.current = true;\n highWaterRef.current = at;\n resumeAtRef.current = at;\n element.currentTime = at;\n setElapsed(at);\n }\n }, [mediaBudget]);\n\n /**\n * The budget lives on the ELEMENT's `play` event, not on the button, so a\n * hardware media key, a browser extension or a scripted `el.play()` is\n * charged too — and an exhausted budget stops playback inside the event,\n * before a sample is audible.\n */\n const handlePlay = useCallback(() => {\n const element = elementRef.current;\n if (element === null) {\n return;\n }\n if (disabled) {\n element.pause();\n return;\n }\n // Before the budget: whichever recording starts silences the others in\n // its group, and a refused start below still leaves them paused — two\n // recordings of one sentence must never sound at once.\n if (playbackGroup !== undefined) {\n pausePlaybackGroupOthers(playbackGroup, element);\n }\n if (selfStartRef.current) {\n selfStartRef.current = false;\n paidRef.current = true;\n setPlaying(true);\n return;\n }\n if (pendingRef.current) {\n setPlaying(true);\n return;\n }\n if (isResume(element)) {\n paidRef.current = true;\n setPlaying(true);\n resumeAtRef.current = null;\n return;\n }\n if (!budgeted) {\n paidRef.current = true;\n setPlaying(true);\n return;\n }\n if (remaining === 0) {\n element.pause();\n refuse();\n return;\n }\n setPlaying(true);\n claimAndStart(element, true);\n }, [disabled, playbackGroup, isResume, budgeted, remaining, refuse, claimAndStart]);\n\n const handlePause = useCallback(() => {\n const element = elementRef.current;\n setPlaying(false);\n if (element === null) {\n return;\n }\n // A play the transport never let run is no place to resume from: the\n // position a paid-for play last stopped at stands.\n if (!paidRef.current) {\n return;\n }\n paidRef.current = false;\n // Every pause records the position — learner, browser, or the pager's own\n // pause as a pane hides. Without this, paging through a six-question\n // listening group and back would exhaust the budget by navigation alone.\n resumeAtRef.current = element.currentTime;\n reportPosition(element.currentTime);\n }, [reportPosition]);\n\n const handleEnded = useCallback(() => {\n paidRef.current = false;\n setPlaying(false);\n resumeAtRef.current = null;\n highWaterRef.current = 0;\n chargeRef.current = null;\n setElapsed(0);\n reportPosition(0);\n }, [reportPosition]);\n\n const handleTimeUpdate = useCallback(() => {\n const element = elementRef.current;\n if (element === null) {\n return;\n }\n setElapsed(element.currentTime);\n highWaterRef.current = Math.max(highWaterRef.current, element.currentTime);\n const whole = Math.floor(element.currentTime);\n if (whole !== lastPositionRef.current) {\n lastPositionRef.current = whole;\n reportPosition(element.currentTime);\n }\n }, [reportPosition]);\n\n const handleSeeking = useCallback(() => {\n const element = elementRef.current;\n if (element === null || policy.seek !== 'none') {\n return;\n }\n if (Math.abs(element.currentTime - highWaterRef.current) > SEEK_EPSILON) {\n element.currentTime = highWaterRef.current;\n announce(s.seekBlocked);\n }\n }, [policy.seek, announce, s.seekBlocked]);\n\n const handleRateChange = useCallback(() => {\n const element = elementRef.current;\n if (element === null || policy.rate !== 'fixed' || element.playbackRate === 1) {\n return;\n }\n element.playbackRate = 1;\n announce(s.rateBlocked);\n }, [policy.rate, announce, s.rateBlocked]);\n\n const handleError = useCallback(() => {\n const charge = chargeRef.current;\n const element = elementRef.current;\n // Ordering matters: an element can error with nothing charged (a bad URL\n // the learner never pressed play on), so the charge must be proven to\n // exist before its position is read.\n const progressed =\n charge !== null && element !== null && element.currentTime - charge.at >= NO_PROGRESS;\n if (charge !== null && !progressed) {\n if (mediaBudget?.onPlayRefunded !== undefined) {\n usedRef.current = charge.claim.previousPlaysUsed;\n setUsed(charge.claim.previousPlaysUsed);\n mediaBudget.onPlayRefunded(charge.claim);\n emit('media-play-refunded', charge.claim.previousPlaysUsed);\n } else {\n emit('media-play-errored', usedRef.current);\n }\n }\n chargeRef.current = null;\n setPlaying(false);\n announce(s.playFailed);\n }, [mediaBudget, emit, announce, s.playFailed]);\n\n // Keep the element's volume in step with the controls.\n useEffect(() => {\n const element = elementRef.current;\n if (element !== null) {\n element.volume = volume;\n element.muted = muted;\n }\n }, [volume, muted]);\n\n const exhausted = budgeted && remaining === 0;\n const buttonLabel = pending ? s.preparing : playing ? s.pause : s.play;\n\n return (\n <figure className=\"lk-media\" data-controls=\"minimal\">\n {/* biome-ignore lint/a11y/useMediaCaption: captions are optional in the data contract — a <track> is rendered when captionsUrl is provided; absence is the author's documented choice (Req 14.5) */}\n <audio\n ref={elementRef}\n className=\"lk-media-el\"\n preload=\"metadata\"\n aria-label={media.alt || undefined}\n onLoadedMetadata={handleLoadedMetadata}\n onPlay={handlePlay}\n onPause={handlePause}\n onEnded={handleEnded}\n onTimeUpdate={handleTimeUpdate}\n onSeeking={handleSeeking}\n onRateChange={handleRateChange}\n onError={handleError}\n >\n <source src={media.url} />\n {media.captionsUrl ? <track kind=\"captions\" src={media.captionsUrl} default /> : null}\n </audio>\n\n {/* A timeline runs with the audio, not with the script. Forcing LTR here\n fixes seek direction for RTL locales by construction; the surrounding\n text keeps the document direction. */}\n <div\n className=\"lk-media-transport\"\n dir=\"ltr\"\n {...(suppliedStrings !== undefined && locale !== undefined ? { lang: locale } : {})}\n >\n <button\n type=\"button\"\n className=\"lk-media-play\"\n onClick={handleButton}\n // NEVER `disabled`: a natively disabled button leaves the focus\n // order, so a learner who tabs into nothing is told nothing — and\n // disabling it mid-attempt would drop focus to <body>. Kept\n // focusable, it re-announces the refusal when pressed.\n aria-disabled={exhausted || disabled || undefined}\n aria-busy={pending || undefined}\n aria-label={exhausted ? `${s.play} — ${s.noPlaysRemaining}` : undefined}\n aria-describedby={budgeted ? statusId : undefined}\n >\n {buttonLabel}\n </button>\n\n <span className=\"lk-media-time\">\n {formatTime(elapsed)} / {formatTime(duration)}\n </span>\n\n {policy.seek === 'allow' ? (\n <input\n type=\"range\"\n className=\"lk-media-scrub\"\n min={0}\n max={duration || 0}\n step={0.1}\n value={Math.min(elapsed, duration || 0)}\n aria-label={s.seek}\n aria-valuetext={s.timeValue(formatTime(elapsed), formatTime(duration))}\n onChange={(event) => {\n const element = elementRef.current;\n if (element !== null) {\n element.currentTime = Number(event.target.value);\n }\n }}\n />\n ) : null}\n\n <button\n type=\"button\"\n className=\"lk-media-mute\"\n aria-pressed={muted}\n onClick={() => setMuted((m) => !m)}\n >\n {muted ? s.unmute : s.mute}\n </button>\n\n <input\n type=\"range\"\n className=\"lk-media-volume\"\n min={0}\n max={1}\n step={0.05}\n value={volume}\n aria-label={s.volume}\n onChange={(event) => setVolume(Number(event.target.value))}\n />\n\n {policy.rate === 'allow' ? (\n <select\n className=\"lk-media-rate\"\n aria-label={s.speed}\n defaultValue=\"1\"\n onChange={(event) => {\n const element = elementRef.current;\n if (element !== null) {\n element.playbackRate = Number(event.target.value);\n }\n }}\n >\n {['0.75', '1', '1.25', '1.5'].map((rate) => (\n <option key={rate} value={rate}>\n {rate}×\n </option>\n ))}\n </select>\n ) : null}\n </div>\n\n {budgeted ? (\n <p className=\"lk-media-plays\" id={statusId} role=\"status\">\n {remaining > 0 ? s.playsRemaining(remaining, maxPlays) : s.noPlaysRemaining}\n </p>\n ) : null}\n\n {/* Separate from the polite status on purpose: a refusal is a direct\n response to a user action and must not be the update a screen reader\n drops when two polite regions compete. */}\n <p className=\"lk-media-notice\" role=\"alert\" key={noticeNonce}>\n {notice}\n </p>\n\n {confirming ? (\n <fieldset className=\"lk-media-confirm\">\n <legend>{s.lastPlayConfirm}</legend>\n <button\n type=\"button\"\n className=\"lk-media-confirm-start\"\n // biome-ignore lint/a11y/noAutofocus: focus must land on the confirm action the press opened, or a keyboard learner is stranded on a button whose meaning just changed\n autoFocus\n onClick={() => {\n const element = elementRef.current;\n setConfirming(false);\n if (element !== null) {\n claimAndStart(element, false);\n }\n }}\n >\n {s.lastPlayStart}\n </button>\n <button\n type=\"button\"\n className=\"lk-media-confirm-cancel\"\n onClick={() => setConfirming(false)}\n >\n {s.lastPlayCancel}\n </button>\n </fieldset>\n ) : null}\n </figure>\n );\n}\n","import type { ActivityMedia as ActivityMediaData, InteractionEvent } from '@intellectif/lk-core';\nimport { resolvePlaybackPolicy } from '@intellectif/lk-core';\nimport { useRef } from 'react';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport type { LkStringsOverride } from '../../i18n/strings.js';\nimport type { MediaBudgetBinding, MediaTransportStrings, RenderMode } from '../types.js';\nimport { AudioTransport } from './AudioTransport.js';\nimport { pausePlaybackGroupOthers, usePlaybackGroup } from './playback-group.js';\n\n/**\n * Presentational media block shown above a question or passage. URL-only:\n * the SDK does not host media (consumer responsibility). Accessibility:\n * images use `alt` (schema requires it non-empty); audio/video expose an\n * optional label and a captions `<track>` when `captionsUrl` is provided;\n * `embed` renders a responsive sandboxed iframe with `alt` as its required\n * accessible `title` (Req 14.5).\n *\n * Audio additionally carries a playback policy (`media.playback`). With no\n * policy the emitted element is character-identical to what every release\n * before 0.8.0 produced; with something to enforce, the browser's control bar\n * is replaced by {@link AudioTransport}.\n */\nexport interface ActivityMediaProps {\n media: ActivityMediaData;\n renderMode?: RenderMode;\n disabled?: boolean;\n locale?: string;\n mediaBudget?: MediaBudgetBinding;\n mediaStrings?: Partial<MediaTransportStrings>;\n /** Overrides the SDK's chrome text. See {@link LkIntlProvider}. */\n strings?: LkStringsOverride;\n onInteraction?: (event: InteractionEvent) => void;\n /**\n * Recordings that share a name never play together: starting this one\n * pauses every other audio element mounted under the same `playbackGroup`.\n * A dictation names one group per mount, so its normal and slow recordings\n * take turns. Pausing charges no play budget. Audio only.\n */\n playbackGroup?: string;\n}\n\nexport function ActivityMedia(props: ActivityMediaProps): React.JSX.Element {\n const { media, renderMode = 'practice', playbackGroup } = props;\n // Read before the branches: every return below is conditional, and the one\n // string this component owns itself (the embed's accessible name) is on the\n // last of them.\n const s = useLkStrings(props.strings);\n // The native audio bar's membership of a playback group. Registered from the\n // ref rather than from an id so two mounts of one recording stay distinct.\n const audioRef = useRef<HTMLAudioElement>(null);\n usePlaybackGroup(audioRef, media.type === 'audio' ? playbackGroup : undefined);\n\n if (media.type === 'image') {\n return (\n <figure className=\"lk-media\">\n <img className=\"lk-media-el\" src={media.url} alt={media.alt ?? ''} />\n </figure>\n );\n }\n\n if (media.type === 'audio') {\n const policy = resolvePlaybackPolicy(media);\n\n // BEFORE the branch below, not after it. A budgeted policy always resolves\n // to `controls: 'minimal'`, so a guard placed after that early return\n // could never fire for the one case it exists to catch — the transport\n // rendered happily with no binding, counting nothing, while the paper\n // believed it had a budget.\n if (policy.maxPlays !== null && renderMode === 'exam' && props.mediaBudget === undefined) {\n throw new Error(\n `ActivityMedia: audio ${JSON.stringify(media.url)} declares maxPlays and is rendered in ` +\n 'renderMode \"exam\" without a `mediaBudget` binding. Nothing would persist the count, so ' +\n 'a refresh restores the full budget while the page says plays remain. Render it through ' +\n '<ActivitySequence mediaBudget={…}> or pass `mediaBudget` yourself.',\n );\n }\n\n // REVIEW ALWAYS GETS THE NATIVE BAR. The paper is graded; a learner\n // reviewing it cannot change an answer by listening again, so taking away\n // their scrubber, their speed control and their browser-localized controls\n // buys nothing — and costs a learner with a processing disability the\n // ability to work out what they got wrong.\n if (policy.controls === 'minimal' && renderMode !== 'review') {\n return <AudioTransport {...props} media={media} policy={policy} />;\n }\n\n const hints = renderMode === 'review' ? [] : policy.nativeControlHints;\n return (\n <figure className=\"lk-media\">\n <audio\n ref={audioRef}\n className=\"lk-media-el\"\n controls\n aria-label={media.alt || undefined}\n {...(playbackGroup !== undefined\n ? {\n onPlay: (event: React.SyntheticEvent<HTMLAudioElement>) =>\n pausePlaybackGroupOthers(playbackGroup, event.currentTarget),\n }\n : {})}\n {...(hints.length > 0\n ? {\n controlsList: hints\n .map((hint) => (hint === 'hide-download' ? 'nodownload' : 'noplaybackrate'))\n .join(' '),\n }\n : {})}\n >\n <source src={media.url} />\n {media.captionsUrl ? <track kind=\"captions\" src={media.captionsUrl} default /> : null}\n </audio>\n </figure>\n );\n }\n\n if (media.type === 'embed') {\n return (\n <figure className=\"lk-media\">\n <div className=\"lk-media-embed\">\n <iframe\n className=\"lk-media-el\"\n src={media.url}\n title={media.alt ?? s.embeddedMedia}\n loading=\"lazy\"\n referrerPolicy=\"strict-origin-when-cross-origin\"\n // The docblock always promised a sandboxed iframe; now it is one.\n // allow-scripts + allow-same-origin are required by provider\n // players (YouTube/Vimeo); top-navigation, forms, downloads and\n // popups stay blocked. Scheme allow-listing in MediaSchema is the\n // primary defense; this is depth.\n sandbox=\"allow-scripts allow-same-origin allow-presentation\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowFullScreen\n />\n </div>\n </figure>\n );\n }\n\n return (\n <figure className=\"lk-media\">\n {/* biome-ignore lint/a11y/useMediaCaption: captions are optional in the data contract — a <track> is rendered when captionsUrl is provided; absence is the author's documented choice (Req 14.5) */}\n <video className=\"lk-media-el\" controls aria-label={media.alt || undefined}>\n <source src={media.url} />\n {media.captionsUrl ? <track kind=\"captions\" src={media.captionsUrl} default /> : null}\n </video>\n </figure>\n );\n}\n"]}
@@ -1,15 +1,16 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
- var chunkNOC2M6LU_cjs = require('./chunk-NOC2M6LU.cjs');
5
- var chunkZDYNAWDZ_cjs = require('./chunk-ZDYNAWDZ.cjs');
4
+ var chunkFKNEHCNT_cjs = require('./chunk-FKNEHCNT.cjs');
5
+ var chunkOJGTJOWI_cjs = require('./chunk-OJGTJOWI.cjs');
6
6
  var chunkOD65OZIW_cjs = require('./chunk-OD65OZIW.cjs');
7
- var chunkIXMT7X3E_cjs = require('./chunk-IXMT7X3E.cjs');
8
- var chunkAPZ6JFN6_cjs = require('./chunk-APZ6JFN6.cjs');
7
+ var chunkM4UJBLVR_cjs = require('./chunk-M4UJBLVR.cjs');
8
+ var chunkJ2QIHZ3H_cjs = require('./chunk-J2QIHZ3H.cjs');
9
+ var chunkY5H4NOM6_cjs = require('./chunk-Y5H4NOM6.cjs');
9
10
  var chunkEANGKAKG_cjs = require('./chunk-EANGKAKG.cjs');
10
- var chunkL7WY5YFQ_cjs = require('./chunk-L7WY5YFQ.cjs');
11
+ var chunkUKUHVCZX_cjs = require('./chunk-UKUHVCZX.cjs');
11
12
  var chunkXOR4MPEN_cjs = require('./chunk-XOR4MPEN.cjs');
12
- var chunkAHGB5HH6_cjs = require('./chunk-AHGB5HH6.cjs');
13
+ var chunkNFGUJ52G_cjs = require('./chunk-NFGUJ52G.cjs');
13
14
  var lkCore = require('@intellectif/lk-core');
14
15
  var react = require('react');
15
16
  var jsxRuntime = require('react/jsx-runtime');
@@ -313,13 +314,15 @@ function Dictation({
313
314
  locale,
314
315
  disabled,
315
316
  ai: aiProp,
316
- delivery
317
+ delivery,
318
+ scoring
317
319
  }) {
318
320
  const isExam = renderMode === "exam";
319
321
  const isReview = renderMode === "review";
320
- const s = chunkAHGB5HH6_cjs.useLkStrings(strings);
321
- const ai = chunkZDYNAWDZ_cjs.useLearnerAi(aiProp);
322
- const policy = chunkIXMT7X3E_cjs.useDeliveryPolicy(delivery);
322
+ const s = chunkNFGUJ52G_cjs.useLkStrings(strings);
323
+ const ai = chunkOJGTJOWI_cjs.useLearnerAi(aiProp);
324
+ const policy = chunkM4UJBLVR_cjs.useDeliveryPolicy(delivery);
325
+ const scoringPolicy = chunkFKNEHCNT_cjs.useItemScoringPolicy(scoring);
323
326
  const hasKey = typeof data.transcript === "string";
324
327
  const devError = react.useMemo(() => {
325
328
  if (!chunkEANGKAKG_cjs.isDevelopment()) {
@@ -345,12 +348,22 @@ function Dictation({
345
348
  const { state, start, complete, getTimeSpent, reset } = chunkXOR4MPEN_cjs.useActivityState(
346
349
  defaultSubmitted === true ? "completed" : "idle"
347
350
  );
351
+ const tries = chunkFKNEHCNT_cjs.useTries({
352
+ policy: scoringPolicy,
353
+ graded: renderMode === "practice" && policy.feedback,
354
+ submitted: state === "completed",
355
+ disabled: disabled === true
356
+ });
357
+ const resetTries = tries.reset;
348
358
  const isControlled = value !== void 0;
349
359
  const [internalText, setInternalText] = react.useState(() => textOf(defaultValue));
350
360
  const [internalRevealed, setInternalRevealed] = react.useState(() => hintsOf(defaultValue));
351
361
  const [result, setResult] = react.useState(null);
352
362
  const [summary, setSummary] = react.useState(null);
353
363
  const [solutionShown, setSolutionShown] = react.useState(false);
364
+ const formRef = react.useRef(null);
365
+ const feedbackRef = react.useRef(null);
366
+ const focusAfterRef = react.useRef(null);
354
367
  const text = isControlled ? textOf(value) : internalText;
355
368
  const revealed = isControlled ? hintsOf(value) : internalRevealed;
356
369
  const defaultValueRef = react.useRef(defaultValue);
@@ -372,8 +385,9 @@ function Dictation({
372
385
  setResult(null);
373
386
  setSummary(null);
374
387
  setSolutionShown(false);
388
+ resetTries();
375
389
  reset(defaultSubmittedRef.current === true ? "completed" : "idle");
376
- }, [data, reset]);
390
+ }, [data, reset, resetTries]);
377
391
  const textChangedRef = react.useRef(
378
392
  null
379
393
  );
@@ -398,6 +412,18 @@ function Dictation({
398
412
  () => slowMediaFor(data, s.dictationSlowRecording),
399
413
  [data, s.dictationSlowRecording]
400
414
  );
415
+ react.useEffect(() => {
416
+ const target = focusAfterRef.current;
417
+ if (target === null) {
418
+ return;
419
+ }
420
+ focusAfterRef.current = null;
421
+ if (target === "answer") {
422
+ formRef.current?.querySelector("textarea")?.focus();
423
+ } else {
424
+ feedbackRef.current?.focus();
425
+ }
426
+ });
401
427
  if (devError) {
402
428
  throw devError;
403
429
  }
@@ -410,6 +436,7 @@ function Dictation({
410
436
  const inactive = disabled === true || submitted || isReview;
411
437
  const revealing = isReview ? outcome?.status === "scored" : !isExam && submitted;
412
438
  const marking = revealing && policy.feedback;
439
+ const solutionsShown = policy.solutions && !tries.open;
413
440
  const transcript = isExam || !hasKey ? void 0 : data.transcript;
414
441
  const hintWords = react.useMemo(
415
442
  () => transcript === void 0 ? [] : hintWordsOf(transcript),
@@ -478,7 +505,7 @@ function Dictation({
478
505
  fireInteraction("hint-requested", { revealed: next, total: hintTotal });
479
506
  };
480
507
  const resetHints = () => {
481
- if (inactive || hintsShown === 0) {
508
+ if (inactive || hintsShown === 0 || scoringPolicy.hintPenalty > 0) {
482
509
  return;
483
510
  }
484
511
  revealButtonRef.current?.focus();
@@ -498,9 +525,18 @@ function Dictation({
498
525
  fireInteraction("submitted", { length: text.length, hintsRevealed: reportedHints });
499
526
  return;
500
527
  }
501
- const scoringResult = lkCore.score("dictation", data, response);
528
+ const answer = lkCore.score("dictation", data, response);
502
529
  complete();
503
530
  const timeSpent = getTimeSpent();
531
+ const counted = tries.record({
532
+ score: answer.score,
533
+ maxScore: answer.maxScore,
534
+ hintsRevealed: reportedHints
535
+ });
536
+ const mine = counted.tries[counted.tries.length - 1];
537
+ const costed = mine === void 0 ? answer.score : mine.scored;
538
+ const scoringResult = costed === answer.score ? answer : { ...answer, score: costed, passed: lkCore.computePassThreshold(data, costed) };
539
+ const passed2 = counted.score === scoringResult.score ? scoringResult.passed : lkCore.computePassThreshold(data, counted.score);
504
540
  const xapiStatement = lkCore.xAPIBuilder.buildAnsweredStatement({
505
541
  actor: chunkEANGKAKG_cjs.ANONYMOUS_ACTOR,
506
542
  object: {
@@ -512,23 +548,23 @@ function Dictation({
512
548
  timeSpentMs: timeSpent,
513
549
  response: response.text
514
550
  });
515
- setResult(scoringResult);
516
- onComplete?.({
517
- score: scoringResult.score,
518
- maxScore: scoringResult.maxScore,
519
- passed: scoringResult.passed,
520
- timeSpent,
521
- xapiStatement
522
- });
523
- const correctWords = scoringResult.details.filter(
551
+ setResult(answer);
552
+ onComplete?.(
553
+ chunkJ2QIHZ3H_cjs.stampTake(
554
+ { score: counted.score, maxScore: counted.maxScore, passed: passed2, timeSpent, xapiStatement },
555
+ chunkJ2QIHZ3H_cjs.mintTake()
556
+ )
557
+ );
558
+ const correctWords = answer.details.filter(
524
559
  (detail) => (detail.outcome ?? (detail.correct ? "correct" : "incorrect")) === "correct"
525
560
  ).length;
561
+ setSolutionShown(false);
526
562
  setSummary({
527
563
  text: `${s.answerSubmitted} ${s.scoreAnnouncement(
528
- Math.round(scoringResult.score * 100),
529
- scoringResult.passed
530
- )} ${s.dictationWordsSummary(correctWords, scoringResult.details.length)}`,
531
- feedback: scoringResult.feedback
564
+ Math.round(counted.score * 100),
565
+ passed2
566
+ )} ${s.dictationWordsSummary(correctWords, answer.details.length)}`,
567
+ feedback: answer.feedback
532
568
  });
533
569
  fireInteraction("submitted", {
534
570
  length: text.length,
@@ -536,6 +572,15 @@ function Dictation({
536
572
  score: scoringResult.score
537
573
  });
538
574
  };
575
+ const retry = () => {
576
+ reset("idle");
577
+ setSummary(null);
578
+ focusAfterRef.current = "answer";
579
+ };
580
+ const closeTries = () => {
581
+ tries.close();
582
+ focusAfterRef.current = "feedback";
583
+ };
539
584
  const given = isControlled ? value : defaultValue;
540
585
  const hasResponse = given !== void 0 && given.type === "dictation";
541
586
  const contentLang = typeof data.locale === "string" ? data.locale.replaceAll("_", "-") : void 0;
@@ -545,8 +590,8 @@ function Dictation({
545
590
  [marking, transcript, isReview, hasResponse, data, text]
546
591
  );
547
592
  const sentenceDiff = react.useMemo(
548
- () => alignment !== null && alignment.attempt !== "" ? lkCore.diffDictationChars(alignment.reference, alignment.attempt) : null,
549
- [alignment]
593
+ () => alignment !== null && alignment.attempt !== "" && solutionsShown ? lkCore.diffDictationChars(alignment.reference, alignment.attempt) : null,
594
+ [alignment, solutionsShown]
550
595
  );
551
596
  const detailWords = react.useMemo(
552
597
  () => isReview && alignment === null && outcome?.status === "scored" ? wordsFromDetails(outcome.details) : null,
@@ -584,17 +629,19 @@ function Dictation({
584
629
  case "correct":
585
630
  return s.dictationWordCorrect(word.attempt);
586
631
  case "incorrect":
587
- return s.dictationWordWrong(word.attempt, word.reference);
632
+ return solutionsShown ? s.dictationWordWrong(word.attempt, word.reference) : s.dictationWordWrongUnnamed(word.attempt);
588
633
  case "missing":
589
- return s.dictationWordMissing(word.reference);
634
+ return solutionsShown ? s.dictationWordMissing(word.reference) : s.dictationWordMissingUnnamed;
590
635
  default:
591
636
  return s.dictationWordExtra(word.attempt);
592
637
  }
593
638
  };
594
639
  const acceptedTranscripts = transcript === void 0 || !Array.isArray(data.acceptedTranscripts) ? void 0 : data.acceptedTranscripts.filter((entry) => typeof entry === "string");
640
+ const extra = !isReview && policy.feedback && submitted && tries.counted !== null ? chunkFKNEHCNT_cjs.triesSummary(s, scoringPolicy, tries.counted, tries.open) : "";
595
641
  return /* @__PURE__ */ jsxRuntime.jsxs(
596
642
  "form",
597
643
  {
644
+ ref: formRef,
598
645
  className: "lk-dc",
599
646
  "aria-labelledby": VISIBLE_TEXT_RE.test(data.title) ? titleId : void 0,
600
647
  lang: locale,
@@ -607,7 +654,7 @@ function Dictation({
607
654
  data.media !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "lk-dc-recording", children: [
608
655
  /* @__PURE__ */ jsxRuntime.jsx("legend", { className: "lk-dc-recording-label", children: s.dictationRecording }),
609
656
  /* @__PURE__ */ jsxRuntime.jsx(
610
- chunkL7WY5YFQ_cjs.ActivityMedia,
657
+ chunkUKUHVCZX_cjs.ActivityMedia,
611
658
  {
612
659
  media: data.media,
613
660
  renderMode,
@@ -624,7 +671,7 @@ function Dictation({
624
671
  slowMedia !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "lk-dc-recording", "data-slow": "true", children: [
625
672
  /* @__PURE__ */ jsxRuntime.jsx("legend", { className: "lk-dc-recording-label", children: s.dictationSlowRecording }),
626
673
  /* @__PURE__ */ jsxRuntime.jsx(
627
- chunkL7WY5YFQ_cjs.ActivityMedia,
674
+ chunkUKUHVCZX_cjs.ActivityMedia,
628
675
  {
629
676
  media: slowMedia,
630
677
  renderMode,
@@ -650,7 +697,8 @@ function Dictation({
650
697
  children: s.dictationRevealNextWord(hintsShown, hintTotal)
651
698
  }
652
699
  ),
653
- hintsShown > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
700
+ /* @__PURE__ */ jsxRuntime.jsx(chunkFKNEHCNT_cjs.HintCost, { policy: scoringPolicy, strings: s }),
701
+ hintsShown > 0 && scoringPolicy.hintPenalty <= 0 ? /* @__PURE__ */ jsxRuntime.jsx(
654
702
  "button",
655
703
  {
656
704
  type: "button",
@@ -707,14 +755,18 @@ function Dictation({
707
755
  "aria-hidden": "true",
708
756
  dir: contentDir ?? "auto",
709
757
  lang: contentLang,
710
- ...word.status === "incorrect" && alignment !== null ? { "data-marks": "characters" } : {},
711
- children: word.status === "incorrect" && alignment !== null ? /* @__PURE__ */ jsxRuntime.jsx(
758
+ ...word.status === "incorrect" && alignment !== null && solutionsShown ? { "data-marks": "characters" } : {},
759
+ children: word.status === "incorrect" && alignment !== null && solutionsShown ? /* @__PURE__ */ jsxRuntime.jsx(
712
760
  CharOps,
713
761
  {
714
762
  ops: lkCore.diffDictationChars(word.reference, word.attempt),
715
763
  contentDir
716
764
  }
717
- ) : word.status === "missing" ? word.reference : word.attempt
765
+ ) : word.status === "missing" ? (
766
+ // Where the answer may not show, a gap the size of
767
+ // nothing in particular: the word is missing, not named.
768
+ solutionsShown ? word.reference : "\u2026"
769
+ ) : word.attempt
718
770
  }
719
771
  )
720
772
  ]
@@ -731,7 +783,7 @@ function Dictation({
731
783
  ] }),
732
784
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-dc-diff-note", children: s.dictationDiffNote })
733
785
  ] }),
734
- transcript !== void 0 && policy.solutions ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
786
+ transcript !== void 0 && solutionsShown ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
735
787
  /* @__PURE__ */ jsxRuntime.jsx(
736
788
  "button",
737
789
  {
@@ -758,14 +810,31 @@ function Dictation({
758
810
  ) : null
759
811
  ] }) : null
760
812
  ] }) : null,
761
- /* @__PURE__ */ jsxRuntime.jsx(chunkIXMT7X3E_cjs.FeedbackRegion, { id: `${data.id}-feedback`, children: /* @__PURE__ */ jsxRuntime.jsx(
762
- AnnouncementText,
813
+ /* @__PURE__ */ jsxRuntime.jsx(
814
+ chunkM4UJBLVR_cjs.FeedbackRegion,
763
815
  {
764
- announcement: isReview ? policy.feedback || !chunkIXMT7X3E_cjs.outcomeShowsMarks(outcome) ? reviewSummary : null : policy.feedback || summary === null ? summary : { text: s.answerSubmitted, feedback: null }
816
+ id: `${data.id}-feedback`,
817
+ ...scoringPolicy.retries > 0 ? { ref: feedbackRef } : {},
818
+ children: /* @__PURE__ */ jsxRuntime.jsx(
819
+ AnnouncementText,
820
+ {
821
+ announcement: isReview ? policy.feedback || !chunkM4UJBLVR_cjs.outcomeShowsMarks(outcome) ? reviewSummary : null : policy.feedback || summary === null ? summary === null || extra === "" ? summary : { ...summary, text: `${summary.text} ${extra}` } : { text: s.answerSubmitted, feedback: null }
822
+ }
823
+ )
824
+ }
825
+ ),
826
+ /* @__PURE__ */ jsxRuntime.jsx(
827
+ chunkFKNEHCNT_cjs.TryActions,
828
+ {
829
+ tries,
830
+ solutions: policy.solutions && transcript !== void 0,
831
+ strings: s,
832
+ onRetry: retry,
833
+ onClose: closeTries
765
834
  }
766
- ) }),
835
+ ),
767
836
  /* @__PURE__ */ jsxRuntime.jsx(
768
- chunkNOC2M6LU_cjs.AiExplanation,
837
+ chunkFKNEHCNT_cjs.AiExplanation,
769
838
  {
770
839
  ai,
771
840
  data,
@@ -776,7 +845,7 @@ function Dictation({
776
845
  locale,
777
846
  onInteraction,
778
847
  strings: s,
779
- delivery: policy
848
+ delivery: tries.open ? { ...policy, solutions: false } : policy
780
849
  }
781
850
  )
782
851
  ]
@@ -784,10 +853,10 @@ function Dictation({
784
853
  );
785
854
  }
786
855
  function Dictation2(props) {
787
- const strings = chunkAHGB5HH6_cjs.useLkStrings(props.strings);
788
- return /* @__PURE__ */ jsxRuntime.jsx(chunkAPZ6JFN6_cjs.ActivityErrorBoundary, { activityTitle: props.data?.title, strings, children: /* @__PURE__ */ jsxRuntime.jsx(Dictation, { ...props }) });
856
+ const strings = chunkNFGUJ52G_cjs.useLkStrings(props.strings);
857
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkY5H4NOM6_cjs.ActivityErrorBoundary, { activityTitle: props.data?.title, strings, children: /* @__PURE__ */ jsxRuntime.jsx(Dictation, { ...props }) });
789
858
  }
790
859
 
791
860
  exports.Dictation = Dictation2;
792
- //# sourceMappingURL=chunk-HREA5SMB.cjs.map
793
- //# sourceMappingURL=chunk-HREA5SMB.cjs.map
861
+ //# sourceMappingURL=chunk-UWFJGFB6.cjs.map
862
+ //# sourceMappingURL=chunk-UWFJGFB6.cjs.map