@intellectif/lk-react 15.0.0 → 16.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.
- package/CHANGELOG.md +51 -0
- package/README.md +2 -2
- package/dist/{WrittenResponse-BPbhERgk.d.cts → WrittenResponse-C2zw-1jK.d.cts} +1 -1
- package/dist/{WrittenResponse-ZAxLR_AU.d.ts → WrittenResponse-CTiNpSCC.d.ts} +1 -1
- package/dist/{chunk-ZH5WT2IE.cjs → chunk-2WM2KSO7.cjs} +25 -25
- package/dist/{chunk-ZH5WT2IE.cjs.map → chunk-2WM2KSO7.cjs.map} +1 -1
- package/dist/{chunk-XMJE34FY.cjs → chunk-2YZZL5FV.cjs} +13 -13
- package/dist/{chunk-XMJE34FY.cjs.map → chunk-2YZZL5FV.cjs.map} +1 -1
- package/dist/{chunk-2HAEQDHQ.js → chunk-3IHVDQRC.js} +492 -139
- package/dist/chunk-3IHVDQRC.js.map +1 -0
- package/dist/{chunk-IE5ZNCWN.js → chunk-4AQ4SWJ3.js} +5 -5
- package/dist/{chunk-IE5ZNCWN.js.map → chunk-4AQ4SWJ3.js.map} +1 -1
- package/dist/{chunk-RT5FTHUU.js → chunk-4X7NFEDB.js} +5 -5
- package/dist/{chunk-RT5FTHUU.js.map → chunk-4X7NFEDB.js.map} +1 -1
- package/dist/{chunk-SRD4KVAP.cjs → chunk-A2CPCOJA.cjs} +503 -149
- package/dist/chunk-A2CPCOJA.cjs.map +1 -0
- package/dist/{chunk-AF72QUBI.js → chunk-AEUP63CZ.js} +5 -5
- package/dist/{chunk-AF72QUBI.js.map → chunk-AEUP63CZ.js.map} +1 -1
- package/dist/{chunk-DQ3VVU5T.cjs → chunk-ALDGRDYD.cjs} +24 -24
- package/dist/{chunk-DQ3VVU5T.cjs.map → chunk-ALDGRDYD.cjs.map} +1 -1
- package/dist/{chunk-WKOV25NP.cjs → chunk-BO64SLHL.cjs} +8 -8
- package/dist/{chunk-WKOV25NP.cjs.map → chunk-BO64SLHL.cjs.map} +1 -1
- package/dist/{chunk-L7NE7PR4.cjs → chunk-DMCFARGP.cjs} +13 -13
- package/dist/{chunk-L7NE7PR4.cjs.map → chunk-DMCFARGP.cjs.map} +1 -1
- package/dist/{chunk-QM4AV6EG.js → chunk-HJLVWZOM.js} +4 -4
- package/dist/{chunk-QM4AV6EG.js.map → chunk-HJLVWZOM.js.map} +1 -1
- package/dist/{chunk-ZJMTHPHY.js → chunk-ICAYWVNF.js} +3 -3
- package/dist/{chunk-ZJMTHPHY.js.map → chunk-ICAYWVNF.js.map} +1 -1
- package/dist/{chunk-26GQO5I2.cjs → chunk-JYC2VV3Y.cjs} +12 -12
- package/dist/{chunk-26GQO5I2.cjs.map → chunk-JYC2VV3Y.cjs.map} +1 -1
- package/dist/{chunk-UCKA22ZJ.cjs → chunk-K63VPFOV.cjs} +9 -2
- package/dist/chunk-K63VPFOV.cjs.map +1 -0
- package/dist/{chunk-YWQH35VO.js → chunk-NOF6HJDV.js} +4 -4
- package/dist/{chunk-YWQH35VO.js.map → chunk-NOF6HJDV.js.map} +1 -1
- package/dist/{chunk-I2QGW4GD.js → chunk-OOYKKO3V.js} +11 -11
- package/dist/{chunk-I2QGW4GD.js.map → chunk-OOYKKO3V.js.map} +1 -1
- package/dist/{chunk-FKGUKFC7.cjs → chunk-OXUUE3HX.cjs} +5 -5
- package/dist/{chunk-FKGUKFC7.cjs.map → chunk-OXUUE3HX.cjs.map} +1 -1
- package/dist/{chunk-53YOZOGN.cjs → chunk-P5FGJMMI.cjs} +5 -5
- package/dist/{chunk-53YOZOGN.cjs.map → chunk-P5FGJMMI.cjs.map} +1 -1
- package/dist/{chunk-UAYWLYFW.cjs → chunk-QADR2XMQ.cjs} +18 -18
- package/dist/{chunk-UAYWLYFW.cjs.map → chunk-QADR2XMQ.cjs.map} +1 -1
- package/dist/{chunk-52GXJNQ3.js → chunk-QIA5FAAW.js} +8 -2
- package/dist/chunk-QIA5FAAW.js.map +1 -0
- package/dist/{chunk-EKBEKDWO.cjs → chunk-RJGCT35F.cjs} +13 -13
- package/dist/{chunk-EKBEKDWO.cjs.map → chunk-RJGCT35F.cjs.map} +1 -1
- package/dist/{chunk-33BQNWAQ.js → chunk-T33AJYLW.js} +5 -5
- package/dist/{chunk-33BQNWAQ.js.map → chunk-T33AJYLW.js.map} +1 -1
- package/dist/{chunk-EVBYEWHO.js → chunk-UHKZXOXD.js} +9 -2
- package/dist/chunk-UHKZXOXD.js.map +1 -0
- package/dist/{chunk-HA3VRF6E.js → chunk-UIGSH2XB.js} +3 -3
- package/dist/{chunk-HA3VRF6E.js.map → chunk-UIGSH2XB.js.map} +1 -1
- package/dist/{chunk-Z6TVOAO5.cjs → chunk-UISZWFI7.cjs} +8 -2
- package/dist/chunk-UISZWFI7.cjs.map +1 -0
- package/dist/{chunk-CE6OGJE6.js → chunk-UYRQEIB7.js} +5 -5
- package/dist/{chunk-CE6OGJE6.js.map → chunk-UYRQEIB7.js.map} +1 -1
- package/dist/{chunk-QEB6O6GU.js → chunk-W32ND3ZR.js} +6 -6
- package/dist/{chunk-QEB6O6GU.js.map → chunk-W32ND3ZR.js.map} +1 -1
- package/dist/{chunk-H6I2JDNW.js → chunk-WBLRUJ77.js} +10 -10
- package/dist/{chunk-H6I2JDNW.js.map → chunk-WBLRUJ77.js.map} +1 -1
- package/dist/{chunk-DP3DM7FR.cjs → chunk-WGIMLXRW.cjs} +6 -6
- package/dist/{chunk-DP3DM7FR.cjs.map → chunk-WGIMLXRW.cjs.map} +1 -1
- package/dist/{chunk-4H3JTX74.cjs → chunk-ZA5QUQ2V.cjs} +13 -13
- package/dist/{chunk-4H3JTX74.cjs.map → chunk-ZA5QUQ2V.cjs.map} +1 -1
- package/dist/components/ActivityPreview.cjs +12 -12
- package/dist/components/ActivityPreview.d.cts +2 -2
- package/dist/components/ActivityPreview.d.ts +2 -2
- package/dist/components/ActivityPreview.js +11 -11
- package/dist/components/ActivitySequence.cjs +13 -13
- package/dist/components/ActivitySequence.d.cts +2 -2
- package/dist/components/ActivitySequence.d.ts +2 -2
- package/dist/components/ActivitySequence.js +12 -12
- package/dist/components/Dictation.cjs +5 -5
- package/dist/components/Dictation.d.cts +1 -1
- package/dist/components/Dictation.d.ts +1 -1
- package/dist/components/Dictation.js +4 -4
- package/dist/components/FillInTheBlanks.cjs +5 -5
- package/dist/components/FillInTheBlanks.d.cts +1 -1
- package/dist/components/FillInTheBlanks.d.ts +1 -1
- package/dist/components/FillInTheBlanks.js +4 -4
- package/dist/components/GapSelect.cjs +5 -5
- package/dist/components/GapSelect.d.cts +1 -1
- package/dist/components/GapSelect.d.ts +1 -1
- package/dist/components/GapSelect.js +4 -4
- package/dist/components/InteractiveVideo.cjs +15 -11
- package/dist/components/InteractiveVideo.d.cts +50 -4
- package/dist/components/InteractiveVideo.d.ts +50 -4
- package/dist/components/InteractiveVideo.js +10 -10
- package/dist/components/MultipleChoice.cjs +5 -5
- package/dist/components/MultipleChoice.d.cts +1 -1
- package/dist/components/MultipleChoice.d.ts +1 -1
- package/dist/components/MultipleChoice.js +4 -4
- package/dist/components/PronunciationFeedback.cjs +4 -4
- package/dist/components/PronunciationFeedback.d.cts +1 -1
- package/dist/components/PronunciationFeedback.d.ts +1 -1
- package/dist/components/PronunciationFeedback.js +3 -3
- package/dist/components/ReadAloud.cjs +6 -6
- package/dist/components/ReadAloud.d.cts +2 -2
- package/dist/components/ReadAloud.d.ts +2 -2
- package/dist/components/ReadAloud.js +5 -5
- package/dist/components/StimulusPanel.cjs +4 -4
- package/dist/components/StimulusPanel.d.cts +1 -1
- package/dist/components/StimulusPanel.d.ts +1 -1
- package/dist/components/StimulusPanel.js +3 -3
- package/dist/components/WrittenResponse.cjs +5 -5
- package/dist/components/WrittenResponse.d.cts +3 -3
- package/dist/components/WrittenResponse.d.ts +3 -3
- package/dist/components/WrittenResponse.js +4 -4
- package/dist/i18n/LkIntlProvider.cjs +7 -7
- package/dist/i18n/LkIntlProvider.d.cts +2 -2
- package/dist/i18n/LkIntlProvider.d.ts +2 -2
- package/dist/i18n/LkIntlProvider.js +1 -1
- package/dist/index.cjs +41 -37
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +15 -15
- package/dist/{strings-D0IWcsUm.d.ts → strings-Dgc5-DeA.d.ts} +21 -2
- package/dist/{strings-5588Q4Te.d.cts → strings-zY2QQYpU.d.cts} +21 -2
- package/dist/theme/ThemeProvider.cjs +6 -6
- package/dist/theme/ThemeProvider.js +1 -1
- package/dist/theme/defaults.css +7 -0
- package/dist/theme/skin.css +44 -6
- package/package.json +3 -3
- package/dist/chunk-2HAEQDHQ.js.map +0 -1
- package/dist/chunk-52GXJNQ3.js.map +0 -1
- package/dist/chunk-EVBYEWHO.js.map +0 -1
- package/dist/chunk-SRD4KVAP.cjs.map +0 -1
- package/dist/chunk-UCKA22ZJ.cjs.map +0 -1
- package/dist/chunk-Z6TVOAO5.cjs.map +0 -1
|
@@ -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","jsx","jsxs"],"mappings":"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","file":"chunk-HA3VRF6E.js","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","jsx","jsxs"],"mappings":"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","file":"chunk-UIGSH2XB.js","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"]}
|
|
@@ -137,9 +137,13 @@ var DEFAULT_STRINGS = {
|
|
|
137
137
|
videoCaptionsShow: "Show captions",
|
|
138
138
|
videoCaptionsHide: "Hide captions",
|
|
139
139
|
videoCaptionsFailed: "Captions could not be loaded.",
|
|
140
|
+
videoSecondaryCaptionsFailed: "The second language could not be loaded.",
|
|
140
141
|
videoSpeedValue: (rate) => rate === 1 ? "Normal" : `${rate}\xD7`,
|
|
141
142
|
videoSettings: "Settings",
|
|
142
143
|
videoCaptionLanguage: "Captions",
|
|
144
|
+
videoSecondCaptionLanguage: "Second language",
|
|
145
|
+
videoCaptionPair: (primary, secondary) => `${primary} + ${secondary}`,
|
|
146
|
+
videoNoSecondLanguage: "No second language for this video.",
|
|
143
147
|
videoCaptionSize: "Caption size",
|
|
144
148
|
videoCaptionSizeValue: (size) => size === "small" ? "Small" : size === "large" ? "Large" : "Medium",
|
|
145
149
|
videoCaptionBackground: "Caption background",
|
|
@@ -172,6 +176,8 @@ var DEFAULT_STRINGS = {
|
|
|
172
176
|
return "Mute";
|
|
173
177
|
case "captions":
|
|
174
178
|
return "Captions";
|
|
179
|
+
case "secondary-captions":
|
|
180
|
+
return "Second caption language on/off";
|
|
175
181
|
case "transcript":
|
|
176
182
|
return "Transcript";
|
|
177
183
|
case "picture-in-picture":
|
|
@@ -297,5 +303,5 @@ exports.directionForLocale = directionForLocale;
|
|
|
297
303
|
exports.mergeStrings = mergeStrings;
|
|
298
304
|
exports.useLkDirection = useLkDirection;
|
|
299
305
|
exports.useLkStrings = useLkStrings;
|
|
300
|
-
//# sourceMappingURL=chunk-
|
|
301
|
-
//# sourceMappingURL=chunk-
|
|
306
|
+
//# sourceMappingURL=chunk-UISZWFI7.cjs.map
|
|
307
|
+
//# sourceMappingURL=chunk-UISZWFI7.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/i18n/strings.ts","../src/i18n/LkIntlProvider.tsx"],"names":["createContext","useMemo","jsx","useContext"],"mappings":"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","file":"chunk-UISZWFI7.cjs","sourcesContent":["import type { ReadAloudDimension, ReadAloudWordState, Stimulus } from '@intellectif/lk-core';\nimport type { CaptionSize } from '../components/InteractiveVideo/prefs.js';\nimport type { VideoShortcutAction } from '../components/InteractiveVideo/shortcuts.js';\nimport type { MediaTransportStrings } from '../components/types.js';\nimport type { SpeechRecorderError } from '../hooks/useSpeechRecorder.js';\n\n/**\n * Every string the SDK's own chrome renders.\n *\n * Until 0.8.1 these were literals, and `locale` set only the `lang` attribute —\n * so a learner sitting a Spanish paper read a Spanish passage inside an English\n * scaffold, and \"No plays remaining\" on a listening exam was untranslatable.\n * This is the whole surface, in one place, so a consumer can replace it.\n *\n * **Interpolation and plurals are functions, not format strings.** A message\n * like `\"Question {n} of {total}\"` needs a parser, and a parser needs plural\n * rules for every locale the SDK does not know. A function hands both problems\n * to the consumer, who has `Intl.PluralRules` and knows their own language:\n *\n * ```ts\n * questionProgress: (index, total) => `Pregunta ${index} de ${total}`,\n * wordCount: (n) => `${n} ${n === 1 ? 'palabra' : 'palabras'}`,\n * ```\n *\n * It also lets a translation reorder its arguments, which a positional token\n * cannot, and TypeScript checks the arity. No parser, no message catalogue\n * format, no runtime dependency.\n *\n * **What is deliberately NOT here: thrown errors.** Every `throw` in this\n * package is addressed to the developer who wired the component up, not to the\n * learner. Translating them would make them unsearchable and would put the one\n * audience who can act on them behind a translation. They stay English.\n */\nexport interface LkStrings {\n // ── Submission ─────────────────────────────────────────────────────────\n /** Multiple Choice submit button, and Written Response. */\n submit: string;\n /** Fill-in-the-Blanks in `practice`: the learner is checking their own work. */\n checkAnswers: string;\n /** Fill-in-the-Blanks in `exam`: nothing is checked on the client. */\n submitAnswers: string;\n\n // ── Outcome, announced after submit ────────────────────────────────────\n /**\n * The scored result, read by a screen reader on submit.\n *\n * `percent` is already rounded to a whole number. The authored overall\n * feedback is appended SEPARATELY by the component, so a translation of this\n * sentence never has to carry the author's words — the two were previously\n * concatenated into one literal, which made the score sentence untranslatable\n * without also touching authored content.\n */\n scoreAnnouncement: (percent: number, passed: boolean) => string;\n /** Prefix announced on submit, before the score sentence. */\n answerSubmitted: string;\n /** An answer submitted for grading that has none yet. Never rendered as 0%. */\n notGradedYet: string;\n /** An outcome that carries no grade and never will. */\n noGradeAvailable: string;\n\n // ── Feedback disclosure ────────────────────────────────────────────────\n showFeedback: string;\n hideFeedback: string;\n\n // ── Fill-in-the-Blanks ─────────────────────────────────────────────────\n /** Accessible name of a blank's input. `ordinal` is 1-based. */\n blankLabel: (ordinal: number) => string;\n\n // ── Gap select ────────────────────────────────────────────────────────────\n /** Accessible name of the selector for one gap in a Gap Select passage. */\n gapLabel: (ordinal: number) => string;\n /**\n * The empty first entry of a gap's selector — how a learner leaves a gap\n * alone, and how they take an answer back. Never a real choice.\n */\n gapPlaceholder: string;\n showHint: string;\n hideHint: string;\n\n // ── Written Response ───────────────────────────────────────────────────\n /**\n * Announced when an essay is handed in. Distinct from {@link LkStrings.awaitingGrade},\n * which is the `review`-mode state of a submission made earlier: this one\n * confirms the act, and is the only thing a learner hears at the moment they\n * commit work that nothing on the client will grade.\n */\n responseSubmitted: string;\n /** Live word counter. Needs the count for languages whose plural rules differ. */\n wordCount: (count: number) => string;\n /** The authored bounds, e.g. \"50–200 words\". `min` is 0 when unbounded below. */\n wordBounds: (min: number, max: number) => string;\n /** A rubric criterion the grader marked not applicable. */\n notApplicable: string;\n /** A returned grade flagged for a human to look at — learner-visible by design. */\n awaitingHumanReview: string;\n /** Submitted, no grade back yet. */\n awaitingGrade: string;\n /** The grader could not produce a grade. */\n couldNotBeGraded: string;\n\n // ── Sequence pager ─────────────────────────────────────────────────────\n previous: string;\n next: string;\n /** Progress label, also the question region's accessible name. `index` is 1-based. */\n questionProgress: (index: number, total: number) => string;\n /** Shown when no renderer is registered for an activity type. */\n unsupportedActivity: string;\n\n // ── Dictation ──────────────────────────────────────────────────────────\n /** Accessible name of the box the learner types the recording into. */\n dictationInputLabel: string;\n /** Accessible name of the group holding the recording. */\n dictationRecording: string;\n /** Accessible name of the group holding the slower recording, and its default `alt`. */\n dictationSlowRecording: string;\n /** The progressive-hint button, with how many transcript words are shown so far. */\n dictationRevealNextWord: (revealed: number, total: number) => string;\n /** Hides every revealed hint word again. */\n dictationResetHints: string;\n /** The solution toggle: shows the transcript after the attempt is marked. */\n showSolution: string;\n hideSolution: string;\n /** Accessible name of the revealed transcript. */\n dictationSolutionLabel: string;\n /** Accessible name of the word-by-word marked result. */\n dictationMarksLabel: string;\n /** Tells the learner the marks compare the normalised text, not what they typed. */\n dictationDiffNote: string;\n /**\n * The four sentences a screen reader hears for a marked word — the only\n * channel the marks have for assistive technology; the glyphs and the\n * character diff are decoration.\n */\n dictationWordCorrect: (word: string) => string;\n dictationWordWrong: (word: string, expected: string) => string;\n dictationWordMissing: (expected: string) => string;\n dictationWordExtra: (word: string) => string;\n /** The legend rows; the character-level marks reuse the same four. */\n dictationLegendCorrect: string;\n dictationLegendWrong: string;\n dictationLegendMissing: string;\n dictationLegendExtra: string;\n /** Marked result of an attempt with no words in it once punctuation and spacing are ignored, an empty one included. */\n dictationNothingTyped: string;\n /**\n * Appended to the score announcement, e.g. \"4 of 6 words correct.\" — a whole\n * sentence, because authored feedback may follow it.\n */\n dictationWordsSummary: (correct: number, total: number) => string;\n\n // ── Read aloud ─────────────────────────────────────────────────────────\n /** Accessible name of the group holding the model recording. */\n readAloudModelRecording: string;\n /** Accessible name of the group holding the slower model recording, and its default `alt`. */\n readAloudSlowRecording: string;\n /** Starts a take. */\n readAloudRecord: string;\n /** Ends the take in progress. */\n readAloudStop: string;\n /** Throws the take away so the learner can start over. */\n readAloudRerecord: string;\n /** Accessible name of the player for the take the learner just made. */\n readAloudYourRecording: string;\n /**\n * In place of the player for the learner's own take, and of the buttons that\n * play one word of it, when the page refuses to play it — most often a\n * Content-Security-Policy whose `media-src` does not allow `blob:`. Said\n * rather than left as controls that do nothing when pressed.\n */\n readAloudPlaybackUnavailable: string;\n /**\n * Live progress while recording. Whole seconds, not a `m:ss` clock: a\n * recording is bounded in seconds by the item, and a translation should not\n * have to reimplement a clock to change the word around it.\n *\n * Shown on screen and hidden from assistive technology: it changes every\n * second, and a screen reader that spoke each change would speak into the\n * take being assessed. The two sentences below are what is announced instead.\n */\n readAloudRecordingProgress: (seconds: number, maxSeconds: number) => string;\n /** Announced once when a capture begins. */\n readAloudRecordingStarted: string;\n /**\n * Announced once when a capture ends, by the learner or at its own bound.\n * Carries the length because the progress counter is not announced: a take\n * that reads back as \"20 of 20 seconds\" is one that stopped itself.\n */\n readAloudRecordingStopped: (seconds: number, maxSeconds: number) => string;\n /** How many takes are left. Called with 0 as well, so the wording is one translation's. */\n readAloudTakesRemaining: (remaining: number, max: number) => string;\n /** `exam` only: hands the item in with no recording at all, deliberately. */\n readAloudSubmitWithoutRecording: string;\n /** Pending, while the take travels to the application's storage. */\n readAloudUploading: string;\n /** The take never reached storage, so nothing was submitted. */\n readAloudUploadFailed: string;\n /** Retries whichever step just failed. */\n readAloudTryAgain: string;\n /** Pending, while the assessor judges the take. */\n readAloudAssessing: string;\n /** The binding stores recordings but judges none, so there is no feedback to show. */\n readAloudAssessmentUnavailable: string;\n /** The assessor found no speech, or too little of it, in the take. */\n readAloudNotHeard: string;\n /** The assessor produced no grade for any other reason. The code itself is never shown. */\n readAloudNotAssessed: string;\n /** The assessment could not be run at all — a failure of the call, not of the reading. */\n readAloudAssessmentFailed: string;\n /**\n * What went wrong with the microphone or with the take. One function rather\n * than five keys, so a translation keeps the five sentences together and the\n * SDK keeps one name for the concept.\n */\n readAloudRecorderError: (reason: SpeechRecorderError) => string;\n\n // ── Pronunciation feedback ─────────────────────────────────────────────\n /** Accessible name of the whole feedback region. */\n pronunciationFeedbackLabel: string;\n /** Name of one scored dimension, as a learner reads it. */\n pronunciationDimension: (dimension: ReadAloudDimension) => string;\n /** A dimension or a word the engine did not measure. NEVER rendered as 0%. */\n pronunciationNotAssessed: string;\n /** Accessible name of the word-by-word marks. */\n pronunciationWordsLabel: string;\n /** One legend row per marking state — the same four states the marks carry. */\n pronunciationLegend: (state: ReadAloudWordState) => string;\n /**\n * The four sentences a screen reader hears for a marked word: the only\n * channel the marks have for assistive technology, since the glyph and the\n * decoration are both hidden from it.\n */\n pronunciationWordCorrect: (word: string) => string;\n pronunciationWordMispronounced: (word: string) => string;\n pronunciationWordOmitted: (word: string) => string;\n pronunciationWordInserted: (word: string) => string;\n /** Opens one word's syllables, sounds and timings. */\n pronunciationWordDetails: (word: string) => string;\n pronunciationSyllables: string;\n pronunciationPhonemes: string;\n /** A syllable the engine also spelled out, e.g. `('ˈhæ', 'ha')`. */\n pronunciationSyllableSpelling: (syllable: string, grapheme: string) => string;\n /** A sound the engine named no symbol for; `ordinal` is 1-based. */\n pronunciationPhonemePosition: (ordinal: number) => string;\n /** Introduces the sounds the engine thought it heard in a sound's place. */\n pronunciationHeardAs: string;\n /** Plays one word out of the learner's own take. */\n pronunciationPlayWord: (word: string) => string;\n pronunciationBreakUnexpected: string;\n pronunciationBreakMissing: string;\n /** Shown only past the caller's own monotone threshold; the SDK sets none. */\n pronunciationMonotone: string;\n /** Names the alphabet the phoneme symbols are written in. */\n pronunciationIpaNote: string;\n\n // ── Interactive video ──────────────────────────────────────────────────\n // Play, pause, mute, volume, speed, the progress bar's name and the time\n // readout are the `media` strings below: the same controls, the same words.\n /** Jump buttons, e.g. \"Back 10 seconds\". */\n videoBack: (seconds: number) => string;\n videoForward: (seconds: number) => string;\n /** The centre button once the video has ended. */\n videoReplay: string;\n /** The time readout flips between elapsed and remaining; these name what a press shows next. */\n videoShowRemaining: string;\n videoShowElapsed: string;\n /** The captions button, named by what a press does. */\n videoCaptionsShow: string;\n videoCaptionsHide: string;\n /** In the captions menu when a caption file could not be read. */\n videoCaptionsFailed: string;\n /** The same, for the second language: the first line keeps working. */\n videoSecondaryCaptionsFailed: string;\n /** A speed in the speed menu, e.g. \"1.25×\". */\n videoSpeedValue: (rate: number) => string;\n videoSettings: string;\n videoCaptionLanguage: string;\n /** The captions page's item, and page, for a second caption line under the first. */\n videoSecondCaptionLanguage: string;\n /**\n * The captions row of the settings menu when two languages show, from the\n * tracks' own labels: \"English + Español\". A translation may order them as its\n * language reads, but the first argument is always the upper line.\n */\n videoCaptionPair: (primary: string, secondary: string) => string;\n /** Announced when Shift + C finds no other language to show. */\n videoNoSecondLanguage: string;\n videoCaptionSize: string;\n videoCaptionSizeValue: (size: CaptionSize) => string;\n videoCaptionBackground: string;\n videoKeyboardShortcuts: string;\n videoShortcutList: string;\n /** Closes the shortcut list. */\n videoClose: string;\n /** A setting's state in the settings menu. */\n videoOn: string;\n videoOff: string;\n /** What each row of the shortcut list does. */\n videoShortcut: (action: VideoShortcutAction) => string;\n /** The button that opens the panel below the video, and its two tabs. */\n videoPanel: string;\n videoContents: string;\n videoTranscript: string;\n videoTranscriptSearch: string;\n videoTranscriptNoMatch: string;\n videoPictureInPicture: string;\n videoFullscreen: string;\n videoExitFullscreen: string;\n /**\n * Why the video will not play, by `MediaError.code` (1 aborted, 2 network,\n * 3 decode, 4 source), with 0 for anything else.\n */\n videoError: (code: number) => string;\n videoTryAgain: string;\n /** A quiz with no title of its own. */\n videoQuiz: string;\n /** Where the learner is inside a quiz, e.g. \"Question 2 of 5\". */\n videoQuestionProgress: (index: number, total: number) => string;\n /** A step of the quiz's step indicator, for assistive technology. */\n videoQuestionStep: (index: number, answered: boolean) => string;\n videoNextQuestion: string;\n videoContinue: string;\n videoSkipQuiz: string;\n videoRewatch: string;\n /** Exam mode, once a question is submitted: saved, not graded. */\n videoAnswerSaved: string;\n videoRequired: string;\n /** A quiz's progress in the contents panel and on the end screen. */\n videoQuizProgress: (answered: number, total: number) => string;\n /** Announced when a quiz opens. */\n videoQuizOpened: (time: string, questions: number) => string;\n /** Announced when a seek stopped at a required quiz. */\n videoHeldAtQuiz: (time: string) => string;\n /** Announced when a seek stopped at the furthest point reached. */\n videoHeldAhead: string;\n videoEnded: string;\n /** Practice end screen: questions answered, and the score. */\n videoEndScore: (answered: number, total: number, percent: number) => string;\n /** Exam end screen: questions answered, no score. */\n videoEndAnswered: (answered: number, total: number) => string;\n videoWatchAgain: string;\n videoFinish: string;\n\n // ── Authoring preview ──────────────────────────────────────────────────\n /**\n * `<ActivityPreview>`, in place of a draft that still has something missing.\n * Author-facing, not learner-facing — but an editor is translated too.\n */\n previewIncomplete: string;\n /** `<ActivityPreview>`, in place of a draft with something wrong in it. */\n previewInvalid: string;\n\n // ── Embedded media ────────────────────────────────────────────────────────\n /**\n * Accessible name of a provider iframe whose author supplied no `alt`.\n * `MediaSchema` requires `alt` on an embed, so validated content never\n * reaches this — but `validateActivity` is opt-in, and a screen-reader\n * learner meeting an unvalidated one should not meet it in English.\n */\n embeddedMedia: string;\n\n // ── Shared stimulus ────────────────────────────────────────────────────\n /**\n * Accessible name of the stimulus region when the author gave it no title.\n * Keyed by `Stimulus['kind']`, because \"Recording\" and \"Passage\" do not share\n * a gender or an article in every language.\n */\n stimulusKind: Record<Stimulus['kind'], string>;\n /**\n * How far the material carries, e.g. \"Questions 3–8\". Called with equal\n * `first` and `last` for a single question, so a translation can choose its\n * own singular rather than receive a pre-pluralised English one.\n */\n stimulusRange: (first: number, last: number) => string;\n\n // ── Error boundary ─────────────────────────────────────────────────────\n /** Production fallback when an activity throws during render. */\n activityFailed: string;\n /** The same, when the activity's title is known. */\n activityFailedNamed: (title: string) => string;\n /** Production fallback when the activity has no title to name. */\n activityFailedUnnamed: string;\n\n // ── Audio transport ────────────────────────────────────────────────────\n /**\n * The listening-paper transport. Shipped in 0.8.0 as its own `mediaStrings`\n * prop, which still works — this is the same shape, reachable from the one\n * provider so a consumer does not wire two mechanisms.\n */\n media: MediaTransportStrings;\n}\n\n/** A partial override, nested one level for {@link LkStrings.media}. */\nexport type LkStringsOverride = Partial<Omit<LkStrings, 'media' | 'stimulusKind'>> & {\n media?: Partial<MediaTransportStrings>;\n stimulusKind?: Partial<Record<Stimulus['kind'], string>>;\n};\n\n/**\n * English defaults, byte-identical to what every component rendered before the\n * strings surface existed. Supplying no override changes nothing on screen.\n */\nexport const DEFAULT_STRINGS: LkStrings = {\n submit: 'Submit',\n checkAnswers: 'Check answers',\n submitAnswers: 'Submit answers',\n\n scoreAnnouncement: (percent, passed) =>\n `Score ${percent}%. ${passed ? 'Passed.' : 'Not passed.'}`,\n answerSubmitted: 'Answer submitted.',\n notGradedYet: 'Not graded yet.',\n noGradeAvailable: 'No grade available.',\n\n showFeedback: 'Show feedback',\n hideFeedback: 'Hide feedback',\n\n blankLabel: (ordinal) => `Fill in blank ${ordinal}`,\n\n gapLabel: (ordinal) => `Choose the answer for gap ${ordinal}`,\n gapPlaceholder: 'Choose…',\n showHint: 'Show hint',\n hideHint: 'Hide hint',\n\n responseSubmitted:\n 'Response submitted. It will be graded and your result will appear here later.',\n wordCount: (count) => `${count} ${count === 1 ? 'word' : 'words'}`,\n wordBounds: (min, max) => (min > 0 ? `${min}–${max} words` : `up to ${max} words`),\n notApplicable: 'Not applicable',\n awaitingHumanReview: 'This grade is awaiting review by a teacher.',\n awaitingGrade: 'Not graded yet. This response is waiting for its grade.',\n couldNotBeGraded: 'This response could not be graded.',\n\n previous: 'Previous',\n next: 'Next',\n questionProgress: (index, total) => `Question ${index} of ${total}`,\n unsupportedActivity: 'This activity type has no renderer. Supply one through the renderers prop.',\n\n dictationInputLabel: 'Type what you hear',\n dictationRecording: 'Recording',\n dictationSlowRecording: 'Slow recording',\n dictationRevealNextWord: (revealed, total) =>\n `Reveal the next word (${revealed} of ${total} shown)`,\n dictationResetHints: 'Reset hints',\n showSolution: 'Show solution',\n hideSolution: 'Hide solution',\n dictationSolutionLabel: 'Solution',\n dictationMarksLabel: 'Your answer, word by word',\n dictationDiffNote: 'Compared after ignoring case, punctuation and extra spaces.',\n dictationWordCorrect: (word) => `“${word}” is correct`,\n dictationWordWrong: (word, expected) => `“${word}” should be “${expected}”`,\n dictationWordMissing: (expected) => `“${expected}” is missing`,\n dictationWordExtra: (word) => `“${word}” is extra`,\n dictationLegendCorrect: 'Correct',\n dictationLegendWrong: 'Wrong',\n dictationLegendMissing: 'Missing',\n dictationLegendExtra: 'Extra',\n dictationNothingTyped: 'Nothing to compare: no words were entered.',\n dictationWordsSummary: (correct, total) =>\n `${correct} of ${total} ${total === 1 ? 'word' : 'words'} correct.`,\n\n readAloudModelRecording: 'Model recording',\n readAloudSlowRecording: 'Slow model recording',\n readAloudRecord: 'Record',\n readAloudStop: 'Stop recording',\n readAloudRerecord: 'Record again',\n readAloudYourRecording: 'Your recording',\n readAloudPlaybackUnavailable: 'Your recording cannot be played back on this page.',\n readAloudRecordingProgress: (seconds, maxSeconds) =>\n `Recording: ${seconds} of ${maxSeconds} seconds`,\n readAloudRecordingStarted: 'Recording started.',\n readAloudRecordingStopped: (seconds, maxSeconds) =>\n `Recording stopped. ${seconds} of ${maxSeconds} seconds recorded.`,\n readAloudTakesRemaining: (remaining, max) =>\n `${remaining} of ${max} recording${max === 1 ? '' : 's'} left`,\n readAloudSubmitWithoutRecording: 'Submit without recording',\n readAloudUploading: 'Sending your recording…',\n readAloudUploadFailed: 'Your recording could not be sent.',\n readAloudTryAgain: 'Try again',\n readAloudAssessing: 'Checking your pronunciation…',\n readAloudAssessmentUnavailable: 'Pronunciation feedback is not available for this activity.',\n readAloudNotHeard: 'We could not hear you. Record again somewhere quieter.',\n readAloudNotAssessed: 'This recording could not be assessed. Try recording it again.',\n readAloudAssessmentFailed: 'Your pronunciation could not be checked.',\n readAloudRecorderError: (reason) => {\n switch (reason) {\n case 'permission-denied':\n return 'This page is not allowed to use the microphone.';\n case 'no-device':\n return 'No microphone was found.';\n case 'unsupported':\n return 'This browser cannot record audio.';\n case 'too-short':\n return 'That recording was too short.';\n default:\n return 'The recording could not be made.';\n }\n },\n\n pronunciationFeedbackLabel: 'Pronunciation feedback',\n pronunciationDimension: (dimension) => {\n switch (dimension) {\n case 'accuracy':\n return 'Accuracy';\n case 'fluency':\n return 'Fluency';\n case 'completeness':\n return 'Completeness';\n default:\n return 'Intonation';\n }\n },\n pronunciationNotAssessed: 'Not assessed',\n pronunciationWordsLabel: 'Your reading, word by word',\n pronunciationLegend: (state) => {\n switch (state) {\n case 'correct':\n return 'Read correctly';\n case 'mispronounced':\n return 'Mispronounced';\n case 'omitted':\n return 'Not read';\n default:\n return 'Added';\n }\n },\n pronunciationWordCorrect: (word) => `“${word}” was read correctly`,\n pronunciationWordMispronounced: (word) => `“${word}” was mispronounced`,\n pronunciationWordOmitted: (word) => `“${word}” was not read`,\n pronunciationWordInserted: (word) => `“${word}” was added`,\n pronunciationWordDetails: (word) => `Details for “${word}”`,\n pronunciationSyllables: 'Syllables',\n pronunciationPhonemes: 'Sounds',\n pronunciationSyllableSpelling: (syllable, grapheme) => `${syllable} (spelled “${grapheme}”)`,\n pronunciationPhonemePosition: (ordinal) => `Sound ${ordinal}`,\n pronunciationHeardAs: 'Heard as',\n pronunciationPlayWord: (word) => `Play “${word}”`,\n pronunciationBreakUnexpected: 'There was an unexpected pause here.',\n pronunciationBreakMissing: 'A pause was expected here.',\n pronunciationMonotone: 'Your reading stayed on one note. Try varying your pitch.',\n pronunciationIpaNote: 'Sounds are written in the International Phonetic Alphabet.',\n videoBack: (seconds) => `Back ${seconds} seconds`,\n videoForward: (seconds) => `Forward ${seconds} seconds`,\n videoReplay: 'Replay',\n videoShowRemaining: 'Show time remaining',\n videoShowElapsed: 'Show time elapsed',\n videoCaptionsShow: 'Show captions',\n videoCaptionsHide: 'Hide captions',\n videoCaptionsFailed: 'Captions could not be loaded.',\n videoSecondaryCaptionsFailed: 'The second language could not be loaded.',\n videoSpeedValue: (rate) => (rate === 1 ? 'Normal' : `${rate}×`),\n videoSettings: 'Settings',\n videoCaptionLanguage: 'Captions',\n videoSecondCaptionLanguage: 'Second language',\n videoCaptionPair: (primary, secondary) => `${primary} + ${secondary}`,\n videoNoSecondLanguage: 'No second language for this video.',\n videoCaptionSize: 'Caption size',\n videoCaptionSizeValue: (size) =>\n size === 'small' ? 'Small' : size === 'large' ? 'Large' : 'Medium',\n videoCaptionBackground: 'Caption background',\n videoKeyboardShortcuts: 'Keyboard shortcuts',\n videoShortcutList: 'Shortcut list',\n videoClose: 'Close',\n videoOn: 'On',\n videoOff: 'Off',\n videoShortcut: (action) => {\n switch (action) {\n case 'play-pause':\n return 'Play or pause';\n case 'jump-10':\n return 'Back or forward 10 seconds';\n case 'jump-5':\n return 'Back or forward 5 seconds';\n case 'jump-1':\n return 'Back or forward 1 second';\n case 'frame':\n return 'Previous or next frame, while paused';\n case 'speed':\n return 'Slower or faster';\n case 'percent':\n return 'Jump to 0% – 90%';\n case 'start-end':\n return 'Start or end';\n case 'chapter':\n return 'Previous or next chapter or quiz';\n case 'mute':\n return 'Mute';\n case 'captions':\n return 'Captions';\n case 'secondary-captions':\n return 'Second caption language on/off';\n case 'transcript':\n return 'Transcript';\n case 'picture-in-picture':\n return 'Picture in picture';\n case 'fullscreen':\n return 'Fullscreen';\n default:\n return 'This list';\n }\n },\n videoPanel: 'Contents and transcript',\n videoContents: 'Contents',\n videoTranscript: 'Transcript',\n videoTranscriptSearch: 'Search the transcript',\n videoTranscriptNoMatch: 'No lines match.',\n videoPictureInPicture: 'Picture in picture',\n videoFullscreen: 'Fullscreen',\n videoExitFullscreen: 'Exit fullscreen',\n videoError: (code) =>\n code === 2\n ? 'The connection dropped while the video was loading.'\n : code === 3\n ? 'This video file could not be played.'\n : code === 4\n ? 'The video could not be loaded.'\n : 'The video stopped before it could play.',\n videoTryAgain: 'Try again',\n videoQuiz: 'Quiz',\n videoQuestionProgress: (index, total) => `Question ${index} of ${total}`,\n videoQuestionStep: (index, answered) => `Question ${index}${answered ? ', answered' : ''}`,\n videoNextQuestion: 'Next question',\n videoContinue: 'Continue video',\n videoSkipQuiz: 'Skip quiz',\n videoRewatch: 'Rewatch',\n videoAnswerSaved: 'Answer saved',\n videoRequired: 'Required',\n videoQuizProgress: (answered, total) =>\n answered === total\n ? 'Answered'\n : answered === 0\n ? 'Not started'\n : `${answered} of ${total} answered`,\n videoQuizOpened: (time, questions) =>\n `Video paused. Quiz at ${time}, ${questions} question${questions === 1 ? '' : 's'}.`,\n videoHeldAtQuiz: (time) => `Finish the quiz at ${time} to continue.`,\n videoHeldAhead: 'You can rewind, but not skip ahead.',\n videoEnded: 'You reached the end',\n videoEndScore: (answered, total, percent) => `You answered ${answered} of ${total} · ${percent}%`,\n videoEndAnswered: (answered, total) => `You answered ${answered} of ${total} questions`,\n videoWatchAgain: 'Watch again',\n videoFinish: 'Finish',\n\n previewIncomplete: 'This activity is not finished yet, so it cannot be previewed.',\n previewInvalid: 'This activity has a problem to fix before it can be previewed.',\n\n embeddedMedia: 'Embedded media',\n\n stimulusKind: {\n text: 'Passage',\n audio: 'Recording',\n video: 'Video',\n image: 'Image',\n mixed: 'Material',\n },\n stimulusRange: (first, last) =>\n first === last ? `Question ${first}` : `Questions ${first}–${last}`,\n\n activityFailed: 'Activity failed to render',\n activityFailedNamed: (title) => `\"${title}\" could not be displayed.`,\n activityFailedUnnamed: 'This activity could not be displayed.',\n\n media: {\n play: 'Play',\n pause: 'Pause',\n preparing: 'Preparing…',\n mute: 'Mute',\n unmute: 'Unmute',\n volume: 'Volume',\n speed: 'Playback speed',\n seek: 'Seek',\n timeValue: (elapsed, duration) => `${elapsed} of ${duration}`,\n playsRemaining: (remaining, max) =>\n `${remaining} of ${max} play${max === 1 ? '' : 's'} remaining`,\n noPlaysRemaining: 'No plays remaining',\n lastPlayConfirm: 'This is your last play. Start it now?',\n lastPlayStart: 'Start last play',\n lastPlayCancel: 'Not yet',\n seekBlocked: 'Rewinding is not available for this recording.',\n rateBlocked: 'Playback speed is fixed for this recording.',\n playFailed: 'The recording could not be started. Try again.',\n },\n};\n\n/**\n * Layers an override onto a base dictionary.\n *\n * Two levels deep and no further, because the shape is two levels deep. A\n * generic deep merge would silently accept a nested object where a function\n * belongs and fail at render instead of at the call site.\n */\nexport function mergeStrings(base: LkStrings, override?: LkStringsOverride): LkStrings {\n if (override === undefined) {\n return base;\n }\n const { media, stimulusKind, ...flat } = override;\n return {\n ...base,\n ...flat,\n stimulusKind: { ...base.stimulusKind, ...stimulusKind },\n media: { ...base.media, ...media },\n };\n}\n","'use client';\n\nimport { createContext, useContext, useMemo } from 'react';\nimport type { LkStrings, LkStringsOverride } from './strings.js';\nimport { DEFAULT_STRINGS, mergeStrings } from './strings.js';\n\nexport type { LkStrings, LkStringsOverride } from './strings.js';\nexport { DEFAULT_STRINGS, mergeStrings } from './strings.js';\n\n/** Writing direction of the surrounding UI. `auto` defers to the locale. */\nexport type LkDirection = 'ltr' | 'rtl' | 'auto';\n\ninterface LkIntlValue {\n strings: LkStrings;\n locale: string | undefined;\n direction: 'ltr' | 'rtl';\n}\n\nconst LkIntlContext = createContext<LkIntlValue | null>(null);\n\n/**\n * Locales written right-to-left, by language subtag.\n *\n * A short list rather than a dependency: these are the RTL scripts in living\n * use, and the set has not changed in decades. `Intl.Locale.textInfo` would be\n * better but is not available everywhere this package runs, and an explicit\n * `direction` prop overrides this in either direction anyway.\n */\nconst RTL_LANGUAGES = new Set(['ar', 'arc', 'dv', 'fa', 'he', 'ku', 'ps', 'sd', 'ug', 'ur', 'yi']);\n\n/** Resolves a BCP 47 tag to a writing direction. */\nexport function directionForLocale(locale: string | undefined): 'ltr' | 'rtl' {\n if (locale === undefined) {\n return 'ltr';\n }\n const language = locale.split('-')[0]?.toLowerCase() ?? '';\n return RTL_LANGUAGES.has(language) ? 'rtl' : 'ltr';\n}\n\nexport interface LkIntlProviderProps {\n /**\n * Replaces the SDK's own chrome text. Anything omitted keeps its English\n * default, so a partial translation degrades to mixed rather than to blank.\n *\n * A nested provider REPLACES the one above it rather than layering onto it —\n * as a nested `ThemeProvider` restarts from `defaultTheme`. To layer, use a\n * component's own `strings` prop, or compose with `mergeStrings`.\n */\n strings?: LkStringsOverride;\n /**\n * BCP 47 tag for the SDK's chrome — NOT for the authored content, which\n * carries its own `locale`. Sets `lang` on the wrapper so a screen reader\n * reads \"Question 3 of 10\" in the interface language rather than in the\n * language of the passage beside it (WCAG 3.1.2).\n */\n locale?: string;\n /**\n * Writing direction. Defaults to `auto`, which derives it from `locale`.\n * Set it explicitly when the surrounding application already manages `dir`\n * and you do not want a second element declaring one.\n */\n direction?: LkDirection;\n children: React.ReactNode;\n}\n\n/**\n * Supplies the SDK's chrome text and writing direction to everything beneath.\n *\n * Mirrors `ThemeProvider`: a provider for the whole tree, and a per-component\n * prop for the exceptions. The component prop wins, so a single activity can\n * be relabelled without a second provider.\n *\n * ```tsx\n * <LkIntlProvider locale=\"es\" strings={{\n * submit: 'Enviar',\n * checkAnswers: 'Comprobar respuestas',\n * questionProgress: (i, total) => `Pregunta ${i} de ${total}`,\n * wordCount: (n) => `${n} ${n === 1 ? 'palabra' : 'palabras'}`,\n * media: { noPlaysRemaining: 'No quedan reproducciones' },\n * }}>\n * <ActivitySequence … />\n * </LkIntlProvider>\n * ```\n *\n * The SDK ships **English only**. Bundling Spanish, Portuguese or Arabic would\n * mean shipping translations nobody in this repo can review, and a wrong\n * \"No plays remaining\" on a listening exam is worse than an English one that a\n * teacher can see is untranslated. The mechanism is the deliverable; the words\n * belong to whoever speaks the language.\n */\nexport function LkIntlProvider({\n strings,\n locale,\n direction = 'auto',\n children,\n}: LkIntlProviderProps): React.JSX.Element {\n const value = useMemo<LkIntlValue>(\n () => ({\n strings: mergeStrings(DEFAULT_STRINGS, strings),\n locale,\n direction: direction === 'auto' ? directionForLocale(locale) : direction,\n }),\n [strings, locale, direction],\n );\n\n // Declare `dir` only when something actually told us the direction. With no\n // locale and no explicit `direction`, writing dir=\"ltr\" would flip an RTL\n // host's own subtree back to left-to-right — the context value stays\n // concrete for `useLkDirection`; it is only the DOM declaration we withhold.\n const declared = direction === 'auto' && locale === undefined ? undefined : value.direction;\n\n return (\n <LkIntlContext.Provider value={value}>\n <div className=\"lk-intl\" lang={locale} dir={declared}>\n {children}\n </div>\n </LkIntlContext.Provider>\n );\n}\n\n/**\n * The strings in force, with an optional per-component override layered on top.\n *\n * Usable without a provider: every component calls this, and one rendered\n * standalone still needs its labels. Falling back to the English defaults is\n * what keeps `<MultipleChoice>` working with no setup at all.\n */\nexport function useLkStrings(override?: LkStringsOverride): LkStrings {\n const context = useContext(LkIntlContext);\n const base = context?.strings ?? DEFAULT_STRINGS;\n return useMemo(() => mergeStrings(base, override), [base, override]);\n}\n\n/** The direction in force. `ltr` with no provider. */\nexport function useLkDirection(): 'ltr' | 'rtl' {\n return useContext(LkIntlContext)?.direction ?? 'ltr';\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { FeedbackRegion } from './chunk-RCMYEWBE.js';
|
|
3
|
-
import { ActivityErrorBoundary, isDevelopment, randomSessionId, ANONYMOUS_ACTOR, objectIdFor } from './chunk-
|
|
4
|
-
import { ActivityMedia } from './chunk-
|
|
3
|
+
import { ActivityErrorBoundary, isDevelopment, randomSessionId, ANONYMOUS_ACTOR, objectIdFor } from './chunk-ICAYWVNF.js';
|
|
4
|
+
import { ActivityMedia } from './chunk-UIGSH2XB.js';
|
|
5
5
|
import { useActivityState } from './chunk-6N43WDVG.js';
|
|
6
|
-
import { useLkStrings } from './chunk-
|
|
6
|
+
import { useLkStrings } from './chunk-QIA5FAAW.js';
|
|
7
7
|
import { assertRedacted, validateActivity, ActivitySchemaError, seededShuffle, score, xAPIBuilder, xapiDefinitionFor } from '@intellectif/lk-core';
|
|
8
8
|
import { useMemo, useState, useRef, useEffect } from 'react';
|
|
9
9
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -315,5 +315,5 @@ function GapSelect2(props) {
|
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
export { GapSelect2 as GapSelect };
|
|
318
|
-
//# sourceMappingURL=chunk-
|
|
319
|
-
//# sourceMappingURL=chunk-
|
|
318
|
+
//# sourceMappingURL=chunk-UYRQEIB7.js.map
|
|
319
|
+
//# sourceMappingURL=chunk-UYRQEIB7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/GapSelect/GapSelect.tsx","../src/components/GapSelect/index.tsx"],"names":["result","GapSelect","jsx"],"mappings":"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","file":"chunk-CE6OGJE6.js","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n assertRedacted,\n type GapSelectBank,\n type GapSelectChoice,\n type GapSelectData,\n type GapSelectGap,\n type GapSelectLearnerResponse,\n type LearnerResponse,\n type ScoringDetail,\n type ScoringResult,\n score,\n seededShuffle,\n validateActivity,\n xAPIBuilder,\n xapiDefinitionFor,\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 { ANONYMOUS_ACTOR, isDevelopment, objectIdFor, randomSessionId } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport type { ActivityProps } from '../types.js';\n\nexport interface GapSelectProps extends ActivityProps<GapSelectData> {\n /**\n * Seeds the per-gap choice shuffle when `data.shuffleChoices` is set. Pass\n * the attempt id, exactly as on `<MultipleChoice>`: an order nobody can\n * reproduce cannot be reconciled with the attempt a server recorded. Without\n * one, a random per-mount seed is used — stable within the mount, and\n * deliberately not reproducible.\n */\n shuffleSeed?: string;\n}\n\nconst PLACEHOLDER = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\n\ntype Segment = { kind: 'text'; value: string } | { kind: 'gap'; id: string; ordinal: number };\n\n/** Splits a passage into literal text and its `{{id}}` gaps, in order. */\nfunction parsePassage(passage: string): Segment[] {\n const segments: Segment[] = [];\n const re = new RegExp(PLACEHOLDER);\n let last = 0;\n let ordinal = 0;\n let match = re.exec(passage);\n while (match !== null) {\n if (match.index > last) {\n segments.push({ kind: 'text', value: passage.slice(last, match.index) });\n }\n ordinal += 1;\n segments.push({ kind: 'gap', id: match[1] as string, ordinal });\n last = match.index + match[0].length;\n match = re.exec(passage);\n }\n if (last < passage.length) {\n segments.push({ kind: 'text', value: passage.slice(last) });\n }\n return segments;\n}\n\nconst NO_SELECTIONS: Record<string, string> = {};\n\n/**\n * The selections out of a `LearnerResponse`. A response of another activity\n * type reads as \"nothing selected\" rather than throwing — a controlled host\n * that has not yet swapped its state on an activity change would otherwise\n * crash the attempt.\n */\nfunction selectionsOf(response: LearnerResponse | undefined): Record<string, string> {\n return response !== undefined && response.type === 'gap-select'\n ? response.selections\n : NO_SELECTIONS;\n}\n\n/** Per-gap correctness keyed by gap id, preferring the unambiguous `outcome`. */\nfunction correctnessByItem(details: readonly ScoringDetail[]): Map<string, boolean> {\n const map = new Map<string, boolean>();\n for (const detail of details) {\n map.set(\n detail.itemId,\n detail.outcome !== undefined ? detail.outcome === 'correct' : detail.correct,\n );\n }\n return map;\n}\n\n/**\n * The choices a gap offers: its own list, or the bank it names.\n *\n * Both survive redaction — they are what the learner picks from — so this is\n * the one resolution that works identically on full and redacted data.\n */\nfunction choicesFor(\n banks: readonly GapSelectBank[] | undefined,\n gap: GapSelectGap,\n): readonly GapSelectChoice[] {\n if (gap.choices !== undefined) {\n return gap.choices;\n }\n return banks?.find((bank) => bank.id === gap.bankId)?.choices ?? [];\n}\n\nexport function GapSelect({\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 shuffleSeed,\n}: GapSelectProps) {\n const isExam = renderMode === 'exam';\n const isReview = renderMode === 'review';\n const s = useLkStrings(strings);\n\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n if (data.redacted === true) {\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('gap-select', data);\n return result.success ? null : new ActivitySchemaError('gap-select', 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 [internalSelections, setInternalSelections] = useState<Record<string, string>>(() =>\n selectionsOf(defaultValue),\n );\n const [result, setResult] = useState<ScoringResult | null>(null);\n const [summary, setSummary] = useState<string | null>(null);\n const [feedbackHidden, setFeedbackHidden] = useState(false);\n\n const selections = isControlled ? selectionsOf(value) : internalSelections;\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 setInternalSelections(selectionsOf(defaultValueRef.current));\n setResult(null);\n setSummary(null);\n setFeedbackHidden(false);\n reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [data, reset]);\n\n const segments = useMemo(() => parsePassage(data.passage), [data.passage]);\n const gapById = useMemo(() => new Map(data.gaps.map((gap) => [gap.id, gap])), [data.gaps]);\n\n const sessionIdRef = useRef<string | null>(null);\n /**\n * The choices each gap shows, in the order it shows them.\n *\n * Seeded PER GAP, not once for the activity: two gaps drawing on the same\n * word bank must not be dealt the same order, or the bank's distractors line\n * up column-wise and the second gap becomes easier than the first.\n */\n const choicesByGap = useMemo(() => {\n const map = new Map<string, readonly GapSelectChoice[]>();\n // biome-ignore lint/suspicious/noAssignInExpressions: sanctioned lazy ref initialization\n const seedSource = shuffleSeed ?? (sessionIdRef.current ??= randomSessionId());\n for (const gap of data.gaps) {\n const choices = choicesFor(data.banks, gap);\n map.set(\n gap.id,\n data.shuffleChoices === true\n ? seededShuffle(choices, `${seedSource}:${data.id}:${gap.id}`)\n : choices,\n );\n }\n return map;\n }, [data, shuffleSeed]);\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 // answered. Fail at render instead, in production too, exactly as every other\n // built-in does.\n if (data.redacted === true && renderMode === 'practice') {\n throw new Error(\n `Gap Select \"${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 const fireInteraction = (\n type: 'gap-selected' | 'submitted',\n payload: Record<string, unknown>,\n ): void => {\n onInteraction?.({ type, activityId: data.id, timestamp: Date.now(), payload });\n };\n\n const handleSelect = (gapId: string, choiceId: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n const next = { ...selections, [gapId]: choiceId };\n if (!isControlled) {\n setInternalSelections(next);\n }\n onChange?.({ type: 'gap-select', selections: next });\n fireInteraction('gap-selected', { gapId, choiceId });\n };\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive) {\n return;\n }\n const response: GapSelectLearnerResponse = { type: 'gap-select', selections };\n onSubmit?.(response);\n\n if (isExam) {\n // The client neither grades nor reveals: no score(), no read of any\n // answer-key field, no onComplete, and no xAPI statement — its\n // correctResponsesPattern IS the answer key.\n complete();\n setSummary(s.answerSubmitted);\n fireInteraction('submitted', { selections });\n return;\n }\n\n const scoringResult = score('gap-select', data as GapSelectData, 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: JSON.stringify(selections),\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 overall = scoringResult.feedback;\n setSummary(\n `${s.answerSubmitted} ${s.scoreAnnouncement(\n Math.round(scoringResult.score * 100),\n scoringResult.passed,\n )}${overall ? ` ${overall}` : ''}`,\n );\n fireInteraction('submitted', { selections, score: scoringResult.score });\n };\n\n const correctByGap = useMemo(() => {\n if (isExam) {\n return new Map<string, boolean>();\n }\n if (isReview) {\n return outcome?.status === 'scored'\n ? correctnessByItem(outcome.details)\n : new Map<string, boolean>();\n }\n return correctnessByItem(result?.details ?? []);\n }, [isExam, isReview, outcome, result]);\n\n const reviewSummary = useMemo(() => {\n if (!isReview || outcome === undefined) {\n return null;\n }\n if (outcome.status === 'scored') {\n return `${s.scoreAnnouncement(Math.round(outcome.score * 100), outcome.passed)}${\n outcome.feedback ? ` ${outcome.feedback}` : ''\n }`;\n }\n if (outcome.status === 'deferred') {\n return s.notGradedYet;\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 `${s.scoreAnnouncement(percent, outcome.passed)}${\n outcome.feedback ? ` ${outcome.feedback}` : ''\n }`;\n }\n return s.noGradeAvailable;\n }, [isReview, outcome, s]);\n\n const anyFeedback = data.gaps.some((gap) => gap.feedback !== undefined);\n\n return (\n <form\n className=\"lk-gs\"\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 <fieldset disabled={inactive}>\n <p className=\"lk-gs-passage\">\n {segments.map((seg, i) => {\n if (seg.kind === 'text') {\n // biome-ignore lint/suspicious/noArrayIndexKey: passage segments are positional and static\n return <span key={`t${i}`}>{seg.value}</span>;\n }\n const gap = gapById.get(seg.id);\n if (!gap) {\n return null;\n }\n const choices = choicesByGap.get(seg.id) ?? [];\n const correctness = correctByGap.get(seg.id);\n // Read defensively and never in exam: a redact() projection omits\n // both at runtime while the type still says they are there.\n const gapFeedback = isExam ? undefined : gap.feedback;\n return (\n <span key={seg.id} className=\"lk-gs-gap\">\n <select\n className=\"lk-gs-select\"\n aria-label={s.gapLabel(seg.ordinal)}\n value={selections[seg.id] ?? ''}\n disabled={inactive}\n aria-disabled={inactive || undefined}\n data-correct={\n revealing && correctness !== undefined ? String(correctness) : undefined\n }\n onChange={(e) => handleSelect(seg.id, e.target.value)}\n >\n {/*\n The empty entry is part of the contract, not a decoration.\n It is how a learner leaves a gap alone — and how they take\n an answer back — so \"not answered\" stays distinguishable\n from \"answered wrongly\" all the way into ItemOutcome, where\n the scorer reports it as `incorrect-omission`.\n */}\n <option value=\"\">{s.gapPlaceholder}</option>\n {choices.map((choice) => (\n <option key={choice.id} value={choice.id}>\n {choice.text}\n </option>\n ))}\n </select>\n {revealing && !feedbackHidden && gapFeedback ? (\n <span\n className=\"lk-gs-gap-feedback\"\n role=\"note\"\n data-correct={correctness !== undefined ? String(correctness) : undefined}\n >\n {gapFeedback}\n </span>\n ) : null}\n </span>\n );\n })}\n </p>\n {isReview ? null : (\n <button type=\"submit\" disabled={inactive}>\n {isExam ? s.submitAnswers : s.checkAnswers}\n </button>\n )}\n </fieldset>\n {revealing && anyFeedback ? (\n <button\n type=\"button\"\n className=\"lk-gs-feedback-toggle\"\n aria-expanded={!feedbackHidden}\n onClick={() => setFeedbackHidden((h) => !h)}\n >\n {feedbackHidden ? s.showFeedback : s.hideFeedback}\n </button>\n ) : null}\n <FeedbackRegion id={`${data.id}-feedback`}>\n {isReview ? reviewSummary : summary}\n </FeedbackRegion>\n </form>\n );\n}\n","'use client';\n\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport { GapSelect as GapSelectCore, type GapSelectProps } from './GapSelect.js';\n\nexport type { GapSelectProps } from './GapSelect.js';\n\n/**\n * Public Gap Select 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 GapSelect(props: GapSelectProps) {\n const strings = useLkStrings(props.strings);\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title} strings={strings}>\n <GapSelectCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/GapSelect/GapSelect.tsx","../src/components/GapSelect/index.tsx"],"names":["result","GapSelect","jsx"],"mappings":"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","file":"chunk-UYRQEIB7.js","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n assertRedacted,\n type GapSelectBank,\n type GapSelectChoice,\n type GapSelectData,\n type GapSelectGap,\n type GapSelectLearnerResponse,\n type LearnerResponse,\n type ScoringDetail,\n type ScoringResult,\n score,\n seededShuffle,\n validateActivity,\n xAPIBuilder,\n xapiDefinitionFor,\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 { ANONYMOUS_ACTOR, isDevelopment, objectIdFor, randomSessionId } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport type { ActivityProps } from '../types.js';\n\nexport interface GapSelectProps extends ActivityProps<GapSelectData> {\n /**\n * Seeds the per-gap choice shuffle when `data.shuffleChoices` is set. Pass\n * the attempt id, exactly as on `<MultipleChoice>`: an order nobody can\n * reproduce cannot be reconciled with the attempt a server recorded. Without\n * one, a random per-mount seed is used — stable within the mount, and\n * deliberately not reproducible.\n */\n shuffleSeed?: string;\n}\n\nconst PLACEHOLDER = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\n\ntype Segment = { kind: 'text'; value: string } | { kind: 'gap'; id: string; ordinal: number };\n\n/** Splits a passage into literal text and its `{{id}}` gaps, in order. */\nfunction parsePassage(passage: string): Segment[] {\n const segments: Segment[] = [];\n const re = new RegExp(PLACEHOLDER);\n let last = 0;\n let ordinal = 0;\n let match = re.exec(passage);\n while (match !== null) {\n if (match.index > last) {\n segments.push({ kind: 'text', value: passage.slice(last, match.index) });\n }\n ordinal += 1;\n segments.push({ kind: 'gap', id: match[1] as string, ordinal });\n last = match.index + match[0].length;\n match = re.exec(passage);\n }\n if (last < passage.length) {\n segments.push({ kind: 'text', value: passage.slice(last) });\n }\n return segments;\n}\n\nconst NO_SELECTIONS: Record<string, string> = {};\n\n/**\n * The selections out of a `LearnerResponse`. A response of another activity\n * type reads as \"nothing selected\" rather than throwing — a controlled host\n * that has not yet swapped its state on an activity change would otherwise\n * crash the attempt.\n */\nfunction selectionsOf(response: LearnerResponse | undefined): Record<string, string> {\n return response !== undefined && response.type === 'gap-select'\n ? response.selections\n : NO_SELECTIONS;\n}\n\n/** Per-gap correctness keyed by gap id, preferring the unambiguous `outcome`. */\nfunction correctnessByItem(details: readonly ScoringDetail[]): Map<string, boolean> {\n const map = new Map<string, boolean>();\n for (const detail of details) {\n map.set(\n detail.itemId,\n detail.outcome !== undefined ? detail.outcome === 'correct' : detail.correct,\n );\n }\n return map;\n}\n\n/**\n * The choices a gap offers: its own list, or the bank it names.\n *\n * Both survive redaction — they are what the learner picks from — so this is\n * the one resolution that works identically on full and redacted data.\n */\nfunction choicesFor(\n banks: readonly GapSelectBank[] | undefined,\n gap: GapSelectGap,\n): readonly GapSelectChoice[] {\n if (gap.choices !== undefined) {\n return gap.choices;\n }\n return banks?.find((bank) => bank.id === gap.bankId)?.choices ?? [];\n}\n\nexport function GapSelect({\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 shuffleSeed,\n}: GapSelectProps) {\n const isExam = renderMode === 'exam';\n const isReview = renderMode === 'review';\n const s = useLkStrings(strings);\n\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n if (data.redacted === true) {\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('gap-select', data);\n return result.success ? null : new ActivitySchemaError('gap-select', 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 [internalSelections, setInternalSelections] = useState<Record<string, string>>(() =>\n selectionsOf(defaultValue),\n );\n const [result, setResult] = useState<ScoringResult | null>(null);\n const [summary, setSummary] = useState<string | null>(null);\n const [feedbackHidden, setFeedbackHidden] = useState(false);\n\n const selections = isControlled ? selectionsOf(value) : internalSelections;\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 setInternalSelections(selectionsOf(defaultValueRef.current));\n setResult(null);\n setSummary(null);\n setFeedbackHidden(false);\n reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [data, reset]);\n\n const segments = useMemo(() => parsePassage(data.passage), [data.passage]);\n const gapById = useMemo(() => new Map(data.gaps.map((gap) => [gap.id, gap])), [data.gaps]);\n\n const sessionIdRef = useRef<string | null>(null);\n /**\n * The choices each gap shows, in the order it shows them.\n *\n * Seeded PER GAP, not once for the activity: two gaps drawing on the same\n * word bank must not be dealt the same order, or the bank's distractors line\n * up column-wise and the second gap becomes easier than the first.\n */\n const choicesByGap = useMemo(() => {\n const map = new Map<string, readonly GapSelectChoice[]>();\n // biome-ignore lint/suspicious/noAssignInExpressions: sanctioned lazy ref initialization\n const seedSource = shuffleSeed ?? (sessionIdRef.current ??= randomSessionId());\n for (const gap of data.gaps) {\n const choices = choicesFor(data.banks, gap);\n map.set(\n gap.id,\n data.shuffleChoices === true\n ? seededShuffle(choices, `${seedSource}:${data.id}:${gap.id}`)\n : choices,\n );\n }\n return map;\n }, [data, shuffleSeed]);\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 // answered. Fail at render instead, in production too, exactly as every other\n // built-in does.\n if (data.redacted === true && renderMode === 'practice') {\n throw new Error(\n `Gap Select \"${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 const fireInteraction = (\n type: 'gap-selected' | 'submitted',\n payload: Record<string, unknown>,\n ): void => {\n onInteraction?.({ type, activityId: data.id, timestamp: Date.now(), payload });\n };\n\n const handleSelect = (gapId: string, choiceId: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n const next = { ...selections, [gapId]: choiceId };\n if (!isControlled) {\n setInternalSelections(next);\n }\n onChange?.({ type: 'gap-select', selections: next });\n fireInteraction('gap-selected', { gapId, choiceId });\n };\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive) {\n return;\n }\n const response: GapSelectLearnerResponse = { type: 'gap-select', selections };\n onSubmit?.(response);\n\n if (isExam) {\n // The client neither grades nor reveals: no score(), no read of any\n // answer-key field, no onComplete, and no xAPI statement — its\n // correctResponsesPattern IS the answer key.\n complete();\n setSummary(s.answerSubmitted);\n fireInteraction('submitted', { selections });\n return;\n }\n\n const scoringResult = score('gap-select', data as GapSelectData, 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: JSON.stringify(selections),\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 overall = scoringResult.feedback;\n setSummary(\n `${s.answerSubmitted} ${s.scoreAnnouncement(\n Math.round(scoringResult.score * 100),\n scoringResult.passed,\n )}${overall ? ` ${overall}` : ''}`,\n );\n fireInteraction('submitted', { selections, score: scoringResult.score });\n };\n\n const correctByGap = useMemo(() => {\n if (isExam) {\n return new Map<string, boolean>();\n }\n if (isReview) {\n return outcome?.status === 'scored'\n ? correctnessByItem(outcome.details)\n : new Map<string, boolean>();\n }\n return correctnessByItem(result?.details ?? []);\n }, [isExam, isReview, outcome, result]);\n\n const reviewSummary = useMemo(() => {\n if (!isReview || outcome === undefined) {\n return null;\n }\n if (outcome.status === 'scored') {\n return `${s.scoreAnnouncement(Math.round(outcome.score * 100), outcome.passed)}${\n outcome.feedback ? ` ${outcome.feedback}` : ''\n }`;\n }\n if (outcome.status === 'deferred') {\n return s.notGradedYet;\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 `${s.scoreAnnouncement(percent, outcome.passed)}${\n outcome.feedback ? ` ${outcome.feedback}` : ''\n }`;\n }\n return s.noGradeAvailable;\n }, [isReview, outcome, s]);\n\n const anyFeedback = data.gaps.some((gap) => gap.feedback !== undefined);\n\n return (\n <form\n className=\"lk-gs\"\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 <fieldset disabled={inactive}>\n <p className=\"lk-gs-passage\">\n {segments.map((seg, i) => {\n if (seg.kind === 'text') {\n // biome-ignore lint/suspicious/noArrayIndexKey: passage segments are positional and static\n return <span key={`t${i}`}>{seg.value}</span>;\n }\n const gap = gapById.get(seg.id);\n if (!gap) {\n return null;\n }\n const choices = choicesByGap.get(seg.id) ?? [];\n const correctness = correctByGap.get(seg.id);\n // Read defensively and never in exam: a redact() projection omits\n // both at runtime while the type still says they are there.\n const gapFeedback = isExam ? undefined : gap.feedback;\n return (\n <span key={seg.id} className=\"lk-gs-gap\">\n <select\n className=\"lk-gs-select\"\n aria-label={s.gapLabel(seg.ordinal)}\n value={selections[seg.id] ?? ''}\n disabled={inactive}\n aria-disabled={inactive || undefined}\n data-correct={\n revealing && correctness !== undefined ? String(correctness) : undefined\n }\n onChange={(e) => handleSelect(seg.id, e.target.value)}\n >\n {/*\n The empty entry is part of the contract, not a decoration.\n It is how a learner leaves a gap alone — and how they take\n an answer back — so \"not answered\" stays distinguishable\n from \"answered wrongly\" all the way into ItemOutcome, where\n the scorer reports it as `incorrect-omission`.\n */}\n <option value=\"\">{s.gapPlaceholder}</option>\n {choices.map((choice) => (\n <option key={choice.id} value={choice.id}>\n {choice.text}\n </option>\n ))}\n </select>\n {revealing && !feedbackHidden && gapFeedback ? (\n <span\n className=\"lk-gs-gap-feedback\"\n role=\"note\"\n data-correct={correctness !== undefined ? String(correctness) : undefined}\n >\n {gapFeedback}\n </span>\n ) : null}\n </span>\n );\n })}\n </p>\n {isReview ? null : (\n <button type=\"submit\" disabled={inactive}>\n {isExam ? s.submitAnswers : s.checkAnswers}\n </button>\n )}\n </fieldset>\n {revealing && anyFeedback ? (\n <button\n type=\"button\"\n className=\"lk-gs-feedback-toggle\"\n aria-expanded={!feedbackHidden}\n onClick={() => setFeedbackHidden((h) => !h)}\n >\n {feedbackHidden ? s.showFeedback : s.hideFeedback}\n </button>\n ) : null}\n <FeedbackRegion id={`${data.id}-feedback`}>\n {isReview ? reviewSummary : summary}\n </FeedbackRegion>\n </form>\n );\n}\n","'use client';\n\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport { GapSelect as GapSelectCore, type GapSelectProps } from './GapSelect.js';\n\nexport type { GapSelectProps } from './GapSelect.js';\n\n/**\n * Public Gap Select 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 GapSelect(props: GapSelectProps) {\n const strings = useLkStrings(props.strings);\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title} strings={strings}>\n <GapSelectCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
|