@intellectif/lk-react 21.0.0 → 23.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 +41 -0
- package/README.md +4 -4
- package/dist/{WrittenResponse-BVPmrmQL.d.cts → WrittenResponse-CgRrjM4S.d.ts} +14 -4
- package/dist/{WrittenResponse-IAJvvL-g.d.ts → WrittenResponse-D2K1VW_V.d.cts} +14 -4
- package/dist/ai/LkAiProvider.cjs +3 -3
- package/dist/ai/LkAiProvider.d.cts +12 -1
- package/dist/ai/LkAiProvider.d.ts +12 -1
- package/dist/ai/LkAiProvider.js +1 -1
- package/dist/ai/useAiHelp.cjs +9 -5
- package/dist/ai/useAiHelp.d.cts +58 -3
- package/dist/ai/useAiHelp.d.ts +58 -3
- package/dist/ai/useAiHelp.js +3 -3
- package/dist/{chunk-M3K6BF5M.js → chunk-2OP5YHRR.js} +13 -8
- package/dist/chunk-2OP5YHRR.js.map +1 -0
- package/dist/{chunk-AY3WN4FB.js → chunk-4MXECBZT.js} +9 -9
- package/dist/chunk-4MXECBZT.js.map +1 -0
- package/dist/{chunk-EANGKAKG.cjs → chunk-54DX6SLS.cjs} +12 -2
- package/dist/chunk-54DX6SLS.cjs.map +1 -0
- package/dist/{chunk-WNDNQ6NR.js → chunk-6GQLCCGN.js} +11 -13
- package/dist/chunk-6GQLCCGN.js.map +1 -0
- package/dist/{chunk-Y4IDF5UO.cjs → chunk-6RIM5EQP.cjs} +140 -9
- package/dist/chunk-6RIM5EQP.cjs.map +1 -0
- package/dist/{chunk-NILHU555.cjs → chunk-7F2YQWMW.cjs} +17 -12
- package/dist/chunk-7F2YQWMW.cjs.map +1 -0
- package/dist/{chunk-YWJ5XJDU.cjs → chunk-B4JRBV56.cjs} +6 -6
- package/dist/{chunk-YWJ5XJDU.cjs.map → chunk-B4JRBV56.cjs.map} +1 -1
- package/dist/{chunk-CB2A56Q7.cjs → chunk-B7JU52YQ.cjs} +32 -35
- package/dist/chunk-B7JU52YQ.cjs.map +1 -0
- package/dist/{chunk-EYLBSS4Y.js → chunk-C4PSJ3ZC.js} +10 -2
- package/dist/chunk-C4PSJ3ZC.js.map +1 -0
- package/dist/{chunk-NDIKHMLJ.js → chunk-CXIP7ERK.js} +138 -8
- package/dist/chunk-CXIP7ERK.js.map +1 -0
- package/dist/{chunk-ZDDBWUUA.js → chunk-DPNCCWOV.js} +120 -27
- package/dist/chunk-DPNCCWOV.js.map +1 -0
- package/dist/{chunk-PPFVTXGJ.js → chunk-DXFCYNST.js} +11 -3
- package/dist/chunk-DXFCYNST.js.map +1 -0
- package/dist/{chunk-WXESKL75.cjs → chunk-DZ7VLLDQ.cjs} +30 -30
- package/dist/chunk-DZ7VLLDQ.cjs.map +1 -0
- package/dist/{chunk-U6SEII5V.js → chunk-E4UF4XZM.js} +4 -4
- package/dist/{chunk-U6SEII5V.js.map → chunk-E4UF4XZM.js.map} +1 -1
- package/dist/{chunk-XM5Q2ZCH.js → chunk-ERV666AR.js} +10 -13
- package/dist/chunk-ERV666AR.js.map +1 -0
- package/dist/{chunk-6SNNMVBV.cjs → chunk-FISEZBLF.cjs} +7 -7
- package/dist/{chunk-6SNNMVBV.cjs.map → chunk-FISEZBLF.cjs.map} +1 -1
- package/dist/{chunk-X4YWXZ4J.js → chunk-HYOUMTPW.js} +3 -3
- package/dist/{chunk-X4YWXZ4J.js.map → chunk-HYOUMTPW.js.map} +1 -1
- package/dist/{chunk-5Q2KFIZQ.js → chunk-HZIT24BK.js} +4 -4
- package/dist/{chunk-5Q2KFIZQ.js.map → chunk-HZIT24BK.js.map} +1 -1
- package/dist/{chunk-GPGCBDBN.cjs → chunk-IYGF7PZY.cjs} +24 -24
- package/dist/{chunk-GPGCBDBN.cjs.map → chunk-IYGF7PZY.cjs.map} +1 -1
- package/dist/{chunk-XB2AHEU5.js → chunk-JMEZYYCE.js} +11 -11
- package/dist/{chunk-XB2AHEU5.js.map → chunk-JMEZYYCE.js.map} +1 -1
- package/dist/{chunk-KRM6DX47.js → chunk-KI6DJ5ZF.js} +10 -13
- package/dist/chunk-KI6DJ5ZF.js.map +1 -0
- package/dist/{chunk-WGHMWBEJ.cjs → chunk-MXZHL4U5.cjs} +29 -31
- package/dist/chunk-MXZHL4U5.cjs.map +1 -0
- package/dist/{chunk-GHKMX5UT.cjs → chunk-NFGUJ52G.cjs} +10 -2
- package/dist/chunk-NFGUJ52G.cjs.map +1 -0
- package/dist/{chunk-W6TWQY6S.js → chunk-O7MINPKZ.js} +10 -10
- package/dist/chunk-O7MINPKZ.js.map +1 -0
- package/dist/{chunk-MPHX3Y42.cjs → chunk-OJGTJOWI.cjs} +2 -2
- package/dist/chunk-OJGTJOWI.cjs.map +1 -0
- package/dist/{chunk-W2P5RILE.cjs → chunk-PGAPZ6UE.cjs} +32 -35
- package/dist/chunk-PGAPZ6UE.cjs.map +1 -0
- package/dist/{chunk-OKEN6NJL.cjs → chunk-QAZO3ANW.cjs} +32 -32
- package/dist/chunk-QAZO3ANW.cjs.map +1 -0
- package/dist/{chunk-MCTNLUKO.js → chunk-RX3A6KIY.js} +2 -2
- package/dist/chunk-RX3A6KIY.js.map +1 -0
- package/dist/{chunk-LHFC2BCM.cjs → chunk-SHDSOOBF.cjs} +21 -21
- package/dist/{chunk-LHFC2BCM.cjs.map → chunk-SHDSOOBF.cjs.map} +1 -1
- package/dist/{chunk-U2BX2RWY.js → chunk-SPSKDNEE.js} +11 -11
- package/dist/{chunk-U2BX2RWY.js.map → chunk-SPSKDNEE.js.map} +1 -1
- package/dist/{chunk-5MO7ACSB.js → chunk-TGTP5DKF.js} +10 -10
- package/dist/{chunk-5MO7ACSB.js.map → chunk-TGTP5DKF.js.map} +1 -1
- package/dist/{chunk-GND3OBVE.js → chunk-U5VFJVQ2.js} +3 -3
- package/dist/{chunk-GND3OBVE.js.map → chunk-U5VFJVQ2.js.map} +1 -1
- package/dist/{chunk-7JBXDQTD.cjs → chunk-UKUHVCZX.cjs} +5 -5
- package/dist/{chunk-7JBXDQTD.cjs.map → chunk-UKUHVCZX.cjs.map} +1 -1
- package/dist/{chunk-M4UJBLVR.cjs → chunk-VPU6UY4O.cjs} +4 -4
- package/dist/{chunk-M4UJBLVR.cjs.map → chunk-VPU6UY4O.cjs.map} +1 -1
- package/dist/{chunk-CQ37UD4T.cjs → chunk-W6UMY7PF.cjs} +5 -5
- package/dist/{chunk-CQ37UD4T.cjs.map → chunk-W6UMY7PF.cjs.map} +1 -1
- package/dist/{chunk-GW3YIPDE.cjs → chunk-WZ2TBXVB.cjs} +18 -18
- package/dist/{chunk-GW3YIPDE.cjs.map → chunk-WZ2TBXVB.cjs.map} +1 -1
- package/dist/{chunk-XZCMUDFP.cjs → chunk-XS6OYYVR.cjs} +129 -36
- package/dist/chunk-XS6OYYVR.cjs.map +1 -0
- package/dist/{chunk-PJMXHDY5.js → chunk-XYJBRKBZ.js} +3 -3
- package/dist/{chunk-PJMXHDY5.js.map → chunk-XYJBRKBZ.js.map} +1 -1
- package/dist/components/ActivityPreview.cjs +17 -17
- package/dist/components/ActivityPreview.d.cts +2 -2
- package/dist/components/ActivityPreview.d.ts +2 -2
- package/dist/components/ActivityPreview.js +16 -16
- package/dist/components/ActivitySequence.cjs +18 -18
- package/dist/components/ActivitySequence.d.cts +2 -2
- package/dist/components/ActivitySequence.d.ts +2 -2
- package/dist/components/ActivitySequence.js +17 -17
- package/dist/components/Dictation.cjs +10 -10
- package/dist/components/Dictation.d.cts +1 -1
- package/dist/components/Dictation.d.ts +1 -1
- package/dist/components/Dictation.js +9 -9
- package/dist/components/FillInTheBlanks.cjs +10 -10
- package/dist/components/FillInTheBlanks.d.cts +1 -1
- package/dist/components/FillInTheBlanks.d.ts +1 -1
- package/dist/components/FillInTheBlanks.js +9 -9
- package/dist/components/GapSelect.cjs +10 -10
- package/dist/components/GapSelect.d.cts +1 -1
- package/dist/components/GapSelect.d.ts +1 -1
- package/dist/components/GapSelect.js +9 -9
- package/dist/components/InteractiveVideo.cjs +17 -17
- package/dist/components/InteractiveVideo.d.cts +2 -2
- package/dist/components/InteractiveVideo.d.ts +2 -2
- package/dist/components/InteractiveVideo.js +15 -15
- package/dist/components/MultipleChoice.cjs +10 -10
- package/dist/components/MultipleChoice.d.cts +1 -1
- package/dist/components/MultipleChoice.d.ts +1 -1
- package/dist/components/MultipleChoice.js +9 -9
- package/dist/components/PronunciationFeedback.cjs +5 -5
- package/dist/components/PronunciationFeedback.d.cts +1 -1
- package/dist/components/PronunciationFeedback.d.ts +1 -1
- package/dist/components/PronunciationFeedback.js +4 -4
- package/dist/components/ReadAloud.cjs +8 -8
- package/dist/components/ReadAloud.d.cts +2 -2
- package/dist/components/ReadAloud.d.ts +2 -2
- package/dist/components/ReadAloud.js +7 -7
- 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 +9 -7
- package/dist/components/WrittenResponse.d.cts +3 -3
- package/dist/components/WrittenResponse.d.ts +3 -3
- package/dist/components/WrittenResponse.js +8 -6
- 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 +45 -41
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +19 -19
- package/dist/theme/skin.css +23 -0
- package/dist/{types-Botf4Wqj.d.cts → types-CO0XLEN6.d.cts} +19 -0
- package/dist/{types-7lGlo0zE.d.ts → types-CrtIcRcK.d.ts} +19 -0
- package/package.json +3 -3
- package/dist/chunk-AY3WN4FB.js.map +0 -1
- package/dist/chunk-CB2A56Q7.cjs.map +0 -1
- package/dist/chunk-EANGKAKG.cjs.map +0 -1
- package/dist/chunk-EYLBSS4Y.js.map +0 -1
- package/dist/chunk-GHKMX5UT.cjs.map +0 -1
- package/dist/chunk-KRM6DX47.js.map +0 -1
- package/dist/chunk-M3K6BF5M.js.map +0 -1
- package/dist/chunk-MCTNLUKO.js.map +0 -1
- package/dist/chunk-MPHX3Y42.cjs.map +0 -1
- package/dist/chunk-NDIKHMLJ.js.map +0 -1
- package/dist/chunk-NILHU555.cjs.map +0 -1
- package/dist/chunk-OKEN6NJL.cjs.map +0 -1
- package/dist/chunk-PPFVTXGJ.js.map +0 -1
- package/dist/chunk-W2P5RILE.cjs.map +0 -1
- package/dist/chunk-W6TWQY6S.js.map +0 -1
- package/dist/chunk-WGHMWBEJ.cjs.map +0 -1
- package/dist/chunk-WNDNQ6NR.js.map +0 -1
- package/dist/chunk-WXESKL75.cjs.map +0 -1
- package/dist/chunk-XM5Q2ZCH.js.map +0 -1
- package/dist/chunk-XZCMUDFP.cjs.map +0 -1
- package/dist/chunk-Y4IDF5UO.cjs.map +0 -1
- package/dist/chunk-ZDDBWUUA.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/shared/capture-registry.ts","../src/components/ReadAloud/recording-bounds.ts","../src/components/ReadAloud/ReadAloud.tsx","../src/components/ReadAloud/index.tsx"],"names":["take","state","ReadAloud","jsx"],"mappings":"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","file":"chunk-AY3WN4FB.js","sourcesContent":["'use client';\n\n/**\n * Microphone captures that must stop when the region holding them goes away,\n * keyed by group name.\n *\n * Module-level for the reason `playback-group.ts` is: the thing that knows a\n * pane was hidden — the sequence — is not the thing holding the microphone,\n * and a context would have to be threaded through every activity component to\n * reach one. The caller mints the group name per region; a sequence uses the\n * slot's, so hiding one slot stops that slot's capture and no other's. The name\n * reaches a recorder only through `SequenceSlotContext`, never as a prop, so no\n * consumer string can join another region's group.\n *\n * This **supplements** a recorder's own teardown and never replaces it. The\n * sequence's pane effect runs only on a `hidden` transition, not on unmount,\n * and neither a preview nor a standalone render has panes at all — so a\n * component that records still releases the microphone from its own cleanup,\n * and this registry only covers the case where the component is left mounted\n * and merely hidden.\n *\n * No `useCaptureGroup` hook, unlike playback: that one re-registers after every\n * commit because the element behind a ref can be replaced while the ref object\n * is not, whereas a recorder's `stop` keeps one identity for the life of the\n * hook and a plain effect registers it exactly once.\n */\nconst groups = new Map<string, Set<() => void>>();\n\n/** Adds a capture's stop to a group; returns the matching removal. */\nexport function joinCaptureGroup(group: string, stop: () => void): () => void {\n let members = groups.get(group);\n if (members === undefined) {\n members = new Set();\n groups.set(group, members);\n }\n members.add(stop);\n return () => {\n const current = groups.get(group);\n if (current === undefined) {\n return;\n }\n current.delete(stop);\n if (current.size === 0) {\n groups.delete(group);\n }\n };\n}\n\n/**\n * Stops every capture in the group.\n *\n * The members are copied before they are called. Each `stop` is expected to\n * run its own removal, and a component that answers one by starting again\n * would otherwise be stopped by the sweep that its own stop provoked.\n */\nexport function stopCaptureGroup(group: string): void {\n const members = groups.get(group);\n if (members === undefined) {\n return;\n }\n for (const stop of [...members]) {\n stop();\n }\n}\n","'use client';\n\nimport { READ_ALOUD_MAX_SECONDS, READ_ALOUD_MAX_TAKES } from '@intellectif/lk-core';\n\n/** The bounds a take is captured under, as the recorder and the sentences use them. */\nexport interface RecordingBounds {\n /** Above 0, and at most `READ_ALOUD_MAX_SECONDS`. */\n maxSeconds: number;\n /** At least 0 and below `maxSeconds`, or `undefined` for no minimum. */\n minSeconds: number | undefined;\n /** A whole number from 1 to `READ_ALOUD_MAX_TAKES`, or `undefined` for no limit. */\n maxTakes: number | undefined;\n}\n\nconst isNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\n\n/**\n * The one reading of an item's `recording`: every bound the component captures\n * under, every sentence that names one, and the key that decides whether a new\n * `data` is a new reading all come from here, so no two of them can read the\n * same field differently.\n *\n * `practice` renders unvalidated content in production, so what comes in may\n * be anything a server stored. What comes out is always a set of bounds the\n * schema accepts, and bounds the schema accepts come out unchanged. Three\n * rules decide the rest, and each errs toward a take the learner can finish:\n *\n * - **A present bound that cannot be read is the schema's own ceiling**, never\n * no bound at all: a `maxSeconds` of `\"20\"` or `NaN` records up to\n * `READ_ALOUD_MAX_SECONDS`, and a `maxTakes` of `\"1\"` allows\n * `READ_ALOUD_MAX_TAKES`. Unreadable data can make the recorder stricter\n * than the ceiling, never looser: `maxTakes: \"1\"` used to mean unlimited\n * takes. `null` is an absent optional, as it is everywhere a host sends JSON.\n * - **A count is a whole number in range**: `0` and `1.5` read as `1`, `999`\n * as the ceiling.\n * - **A minimum the maximum cannot reach is no minimum.** A `minSeconds` of 30\n * under a `maxSeconds` of 20 stopped every take at 20 and then refused it as\n * too short, so the item could never be answered. The schema refuses that\n * pair; a reading that honoured it would leave the learner a dead end.\n */\nexport function readRecordingBounds(recording: unknown): RecordingBounds {\n const bounds =\n typeof recording === 'object' && recording !== null\n ? (recording as Record<string, unknown>)\n : {};\n const maxSeconds =\n isNumber(bounds.maxSeconds) && bounds.maxSeconds > 0\n ? Math.min(bounds.maxSeconds, READ_ALOUD_MAX_SECONDS)\n : READ_ALOUD_MAX_SECONDS;\n const minSeconds =\n isNumber(bounds.minSeconds) && bounds.minSeconds >= 0 && bounds.minSeconds < maxSeconds\n ? bounds.minSeconds\n : undefined;\n const takes = bounds.maxTakes;\n const maxTakes =\n takes === undefined || takes === null\n ? undefined\n : isNumber(takes)\n ? Math.min(Math.max(1, Math.floor(takes)), READ_ALOUD_MAX_TAKES)\n : READ_ALOUD_MAX_TAKES;\n return { maxSeconds, minSeconds, maxTakes };\n}\n\n/**\n * A length of audio as the whole seconds a sentence says beside `maxSeconds`,\n * never more than it. A bound may be fractional — the schema allows 2.5 — and\n * rounding a take that ran to it said \"3 of 2.5 seconds\". Rounded to the\n * nearest second for a finished take, and down for the counter of one still\n * running, which should not reach its bound before the take does.\n */\nexport function secondsWithin(\n milliseconds: number,\n maxSeconds: number,\n rounding: 'nearest' | 'down',\n): number {\n const seconds = milliseconds / 1000;\n const whole = rounding === 'nearest' ? Math.round(seconds) : Math.floor(seconds);\n return Math.min(Math.max(0, whole), maxSeconds);\n}\n","'use client';\n\nimport {\n type ActivityMedia as ActivityMediaData,\n ActivitySchemaError,\n assertRedacted,\n type GradeRecord,\n type InteractionKind,\n type LearnerResponse,\n type MediaPlaybackPolicy,\n type ReadAloudData,\n type ReadAloudLearnerResponse,\n type ReadAloudWordAlignment,\n type RecordingRef,\n type ScoringDetail,\n type SpeechAssessment,\n validateActivity,\n xAPIBuilder,\n xapiDefinitionFor,\n} from '@intellectif/lk-core';\nimport {\n type CSSProperties,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useActivityState } from '../../hooks/useActivityState.js';\nimport type { RecordedTake, SpeechRecorderStatus } from '../../hooks/useSpeechRecorder.js';\nimport { useSpeechRecorder } from '../../hooks/useSpeechRecorder.js';\nimport { localeDirectionOf } from '../../i18n/direction.js';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport type { LkStrings } from '../../i18n/strings.js';\nimport { ANONYMOUS_ACTOR, isDevelopment, objectIdFor } from '../_internal.js';\nimport { PronunciationFeedback } from '../PronunciationFeedback/index.js';\nimport { markSentence, VISUALLY_HIDDEN } from '../PronunciationFeedback/PronunciationFeedback.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { joinCaptureGroup } from '../shared/capture-registry.js';\nimport { useDeliveryPolicy } from '../shared/delivery.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport { usePlaybackRefusal } from '../shared/playback-refusal.js';\nimport {\n fractionOfGrade,\n type OutcomeReading,\n percentOfGrade,\n type ReadGrade,\n readAssessResult,\n readEvidence,\n readOutcome,\n} from '../shared/read-grade.js';\nimport {\n mintTake,\n SequenceSlotContext,\n stampTake,\n type TakeState,\n} from '../shared/sequence-slot.js';\nimport type { ActivityProps, Renderable } from '../types.js';\nimport { readRecordingBounds, secondsWithin } from './recording-bounds.js';\n\n/**\n * Names playback groups for the model recordings. Per mount and never derived\n * from `useId`: two separately hydrated roots derive the same id for the same\n * tree position, and their recordings would pause each other. The name never\n * reaches the DOM.\n */\nlet modelsGroupCount = 0;\n\n/** A title with something in it a screen reader can say. */\nconst VISIBLE_TEXT_RE = /[^\\p{White_Space}\\p{Cc}\\p{Cf}\\p{Default_Ignorable_Code_Point}]/u;\n\n/** A stored mark's id, as `gradeReadAloud` numbers the reference words. */\nconst MARK_ID_RE = /^w\\d+$/;\n\n/** The states a mark rebuilt from stored details can carry: insertions are not stored. */\nconst STORED_STATES: readonly ReadAloudWordAlignment['state'][] = [\n 'correct',\n 'mispronounced',\n 'omitted',\n];\n\n/** The level meter's resolution, in steps the skin can select on. */\nconst LEVEL_STEPS = 10;\n\n/**\n * What an application's assessor came back with.\n *\n * `code` is a plain `string` and not `SpeechUnscorableCode`: an application's\n * own assessor may refuse a take for a reason the SDK has no name for, and a\n * narrower type would make it unreportable. This component classifies the two\n * codes that mean \"nothing was heard\" and treats every other one the same way.\n */\nexport type ReadAloudAssessResult =\n | { status: 'graded'; assessment: SpeechAssessment; grade: GradeRecord }\n | { status: 'unscorable'; code: string; assessment?: SpeechAssessment }\n | { status: 'failed'; retryable: boolean };\n\n/**\n * How a take reaches the application, and how a judgement comes back.\n *\n * Named `recordingBinding` rather than `recording` because both\n * `ReadAloudData.recording` (the bounds) and `ReadAloudLearnerResponse.recording`\n * (the stored take) already exist: the plain name would read as one of those.\n */\nexport interface RecordingBinding {\n /**\n * Puts the take in your storage and returns its key. **Required** outside\n * `review`: without it a learner can speak into a control that submits\n * nothing. Settle it — a promise that never does leaves the submit pending\n * for ever.\n */\n upload(take: RecordedTake): Promise<RecordingRef>;\n /**\n * Judges the stored take. `practice` only — an exam never assesses on the\n * client. Optional: a binding without it records, uploads and submits, and\n * the feedback is replaced by a notice.\n */\n assess?(ref: RecordingRef): Promise<ReadAloudAssessResult>;\n /** A playable link to a stored take. `review` only. */\n playbackUrl?(ref: RecordingRef): Promise<string>;\n}\n\nexport interface ReadAloudProps extends ActivityProps<ReadAloudData> {\n /** How a take is stored and judged. See {@link RecordingBinding}. */\n recordingBinding?: RecordingBinding;\n /**\n * The evidence behind a stored grade, for `review`. Without it a graded\n * outcome still shows its marks, rebuilt from `outcome.grade.details`; with\n * neither, the score is shown and no marks are.\n */\n assessment?: SpeechAssessment;\n /**\n * Confidence above which a break note is shown, 0..1, forwarded to\n * {@link PronunciationFeedback}. No default — see its own prop.\n */\n breakThreshold?: number;\n /** The same, for the monotone note. */\n monotoneThreshold?: number;\n /**\n * The URL of a self-hosted copy of `CAPTURE_PROCESSOR_SOURCE`, handed to the\n * recorder, for a Content-Security-Policy whose `script-src` does not allow\n * `blob:`. Without it the capture module is loaded from a `blob:` URL, and a\n * policy that refuses one sends every take through the deprecated\n * main-thread `ScriptProcessorNode` — a take that still records, so nothing\n * says so. See `useSpeechRecorder`'s option of the same name.\n */\n workletUrl?: string;\n /**\n * Never called. A read-aloud answer is a key in your storage, and there is\n * none until the take has been uploaded — so there is no intermediate\n * response to report, and reporting the take itself would offer a host a\n * recording it has nowhere to put. `onSubmit` carries the response, once.\n */\n onChange?: (response: LearnerResponse) => void;\n}\n\n/** What the live region says: the SDK's own sentence, then any grader feedback. */\ninterface Announcement {\n text: string;\n feedback: string | null;\n}\n\n/**\n * How far the submitted take has got. `idle` also means \"nothing submitted yet\".\n *\n * Every other phase names its `take`, the number `mintTake` gave the submit\n * that reached it: a phase describes one take, and a pager told about it must\n * be able to tell that take from the one before it.\n */\ntype SubmitPhase =\n | { kind: 'idle' }\n | { kind: 'uploading'; take: number }\n | { kind: 'upload-failed'; take: number }\n | { kind: 'assessing'; take: number }\n | { kind: 'submitted'; take: number }\n | { kind: 'unassessed'; take: number }\n /**\n * The assessor's judgement, as `readAssessResult` read it: `grade` is `null`\n * when what came back cannot be shown as a grade, and `assessment` is `null`\n * when there is no evidence to mark words from.\n */\n | {\n kind: 'graded';\n take: number;\n assessment: SpeechAssessment | null;\n grade: ReadGrade | null;\n }\n | { kind: 'unscorable'; take: number; code: string | undefined }\n | { kind: 'assess-failed'; take: number; retryable: boolean };\n\n/**\n * Whether the screen offers \"Try again\" for the take this phase describes —\n * the one answer to that question, read by the button that offers it and by\n * the take state a pager is told, so the two cannot disagree. A retry re-sends\n * the SAME take: a take that failed to upload is sent again, and a stored take\n * whose assessment failed is judged again without being uploaded twice.\n */\nfunction offersRetry(phase: SubmitPhase, uploaded: RecordingRef | null): boolean {\n return (\n phase.kind === 'upload-failed' ||\n (phase.kind === 'assess-failed' && phase.retryable && uploaded !== null)\n );\n}\n\n/**\n * The state of the phase's take, as a pager deciding whether its set is\n * finished reads it. Derived from the phase alone, so no path through a submit\n * can leave a pager waiting on a take this component has finished with: every\n * path ends in a phase.\n */\nfunction takeStateOf(phase: SubmitPhase, uploaded: RecordingRef | null): TakeState {\n if (phase.kind === 'uploading' || phase.kind === 'assessing') {\n return 'in-flight';\n }\n return offersRetry(phase, uploaded) ? 'retryable' : 'settled';\n}\n\n/**\n * Whether the recorder is capturing, or about to: the one answer to \"is a take\n * being recorded\", read by everything that must hold still for one. A\n * permission prompt counts — a prompt answered after the pane went away would\n * otherwise open a microphone nobody is in front of, and a model recording\n * played while the prompt is up would be charged a play and then silenced.\n */\nfunction capturing(status: SpeechRecorderStatus): boolean {\n return status === 'recording' || status === 'requesting-permission';\n}\n\n/**\n * What a take is a take OF: the item, the words, their language and the bounds\n * the take is captured under. A change to any of these is a different reading,\n * and a take of the old one answers nothing.\n *\n * Deliberately NOT the identity of `data`. `redact()` returns a new object on\n * every call, and `activities={raw.map(redact)}` is a documented way to pass a\n * paper, so a host that rebuilds its items on every render is ordinary — and a\n * reset keyed on identity silently threw away every grade still in flight and\n * refunded the take budget on each of those renders, so a `maxTakes: 1` exam\n * accepted a second take. The title, the instructions and the model recordings\n * are presentation; editing them leaves the take a take of the same reading.\n *\n * The bounds are the CLAMPED ones, which are what the recorder is given. Read\n * field by field into strings, so no value unvalidated data carries can make\n * the key throw.\n */\nfunction readingKeyOf(\n data: Renderable<ReadAloudData>,\n bounds: { maxSeconds: number; minSeconds: number | undefined; maxTakes: number | undefined },\n): string {\n const text = (value: unknown): string | null => (typeof value === 'string' ? value : null);\n return JSON.stringify([\n text(data.id),\n text(data.referenceText),\n text(data.locale),\n String(bounds.maxSeconds),\n String(bounds.minSeconds),\n String(bounds.maxTakes),\n ]);\n}\n\n/**\n * The slower model recording as an `ActivityMedia`: it follows the model\n * recording's playback policy minus `maxPlays` (`controls`, `seek`, `rate`,\n * the native hints), so a locked scrubber or a fixed speed binds both files —\n * and it never carries a budget of its own, so `<ActivityMedia>` never demands\n * a binding for it. `undefined` when there is no slow recording.\n */\nfunction slowMediaFor(\n data: Renderable<ReadAloudData>,\n label: string,\n): ActivityMediaData | undefined {\n const slow = data.slowMedia;\n if (slow === undefined) {\n return undefined;\n }\n const policy: MediaPlaybackPolicy = data.media?.playback ?? {};\n const { maxPlays: _budget, ...inherited } = policy;\n const hasPolicy = Object.values(inherited).some((value) => value !== undefined);\n return {\n type: 'audio',\n url: slow.url,\n alt: slow.alt ?? label,\n ...(hasPolicy ? { playback: inherited } : {}),\n };\n}\n\n/**\n * Word marks rebuilt from stored scoring details — a server-recorded mark shown\n * after the fact, with no assessment on the client. Inserted words are not in\n * the details (`gradeReadAloud` stores one mark per REFERENCE word, and a word\n * the learner added is not one the item asked for), so none appear.\n *\n * The details are a read grade's, so every entry is well-formed; what is left\n * to decide here is whether they are word marks at all, rather than a rubric's\n * own breakdown.\n */\nfunction marksFromDetails(details: readonly ScoringDetail[]): ReadAloudWordAlignment[] | null {\n if (details.length === 0 || !details.every((detail) => MARK_ID_RE.test(detail.itemId))) {\n return null;\n }\n return details.map((detail) => {\n const heard = Array.isArray(detail.learnerResponse)\n ? detail.learnerResponse.join(' ')\n : detail.learnerResponse;\n const reference = Array.isArray(detail.correctResponse)\n ? detail.correctResponse.join(' ')\n : detail.correctResponse;\n const correct = detail.outcome !== undefined ? detail.outcome === 'correct' : detail.correct;\n const state: ReadAloudWordAlignment['state'] = correct\n ? 'correct'\n : detail.outcome === 'incorrect-omission'\n ? 'omitted'\n : 'mispronounced';\n return {\n itemId: detail.itemId,\n reference,\n heard,\n state,\n // `ScoringDetail.score` is the word's accuracy divided by 100; the marks\n // carry it back in the units the assessment used.\n ...(typeof detail.score === 'number' ? { accuracy: detail.score * 100 } : {}),\n };\n });\n}\n\n/** The stored take a learner response points at, or `null` for a blank or another type. */\nfunction recordingOf(response: LearnerResponse | undefined): RecordingRef | null {\n return response !== undefined && response.type === 'read-aloud' ? response.recording : null;\n}\n\n/** The response this component emits; `takes` is written only when one was made. */\nfunction responseOf(recording: RecordingRef | null, takes: number): ReadAloudLearnerResponse {\n return { type: 'read-aloud', recording, ...(takes > 0 ? { takes } : {}) };\n}\n\n/**\n * Which learner message an unscorable code earns. The two codes that mean\n * \"nothing was heard\" send the learner back to a quieter room; every other one,\n * the SDK's and an application's own alike — and no code at all — says only\n * that this take could not be assessed. **The code itself is never shown** — it\n * is a developer's label.\n */\nfunction unscorableMessage(code: string | undefined, s: LkStrings): string {\n return code === 'no_speech' || code === 'insufficient_voiced_time'\n ? s.readAloudNotHeard\n : s.readAloudNotAssessed;\n}\n\n/**\n * The announcement, with grader feedback in its own isolated span: its\n * direction is its own, so an English \"Well read.\" in an Arabic interface keeps\n * its full stop at its end. Its language is not guessed — feedback may be\n * written in the interface's language or the reading's.\n */\nfunction AnnouncementText({ announcement }: { announcement: Announcement | null }) {\n if (announcement === null) {\n return null;\n }\n return (\n <>\n {announcement.text}\n {announcement.feedback ? (\n <>\n {' '}\n <span dir=\"auto\">{announcement.feedback}</span>\n </>\n ) : null}\n </>\n );\n}\n\n/**\n * The server's outcome in `review`, as `readOutcome` read it. Nothing on this\n * path scores, infers or defaults a grade.\n *\n * `scoreShown` says whether the grade is already on screen — it is, whenever\n * `<PronunciationFeedback>` was given the same grade — so the sentence is not\n * rendered twice. The wrapper and its attributes are rendered either way,\n * because they are what a stylesheet and a diagnostic read.\n */\nfunction OutcomeSummary({\n reading,\n s,\n scoreShown,\n}: {\n reading: OutcomeReading;\n s: LkStrings;\n scoreShown: boolean;\n}) {\n switch (reading.kind) {\n case 'graded':\n return (\n <div\n className=\"lk-ra-outcome\"\n data-status={reading.status}\n data-passed={String(reading.grade.passed)}\n >\n {scoreShown ? null : (\n <>\n <p className=\"lk-ra-grade\">\n {s.scoreAnnouncement(percentOfGrade(reading.grade), reading.grade.passed)}\n </p>\n {reading.grade.feedback ? (\n <p className=\"lk-ra-grade-feedback\" dir=\"auto\">\n {reading.grade.feedback}\n </p>\n ) : null}\n </>\n )}\n </div>\n );\n case 'deferred':\n return (\n <div className=\"lk-ra-outcome\" data-status=\"deferred\">\n <p className=\"lk-ra-grade\">{s.awaitingGrade}</p>\n </div>\n );\n case 'unscorable':\n // An unscorable code is a developer-facing label: it goes on the element\n // for diagnostics and routing, and never in front of the learner as text.\n return (\n <div\n className=\"lk-ra-outcome\"\n data-status=\"unscorable\"\n {...(reading.code !== undefined ? { 'data-code': reading.code } : {})}\n >\n <p className=\"lk-ra-grade\">{s.couldNotBeGraded}</p>\n </div>\n );\n default:\n // A stored outcome that claims a grade and carries none that can be read\n // gets the deferred vocabulary's own sentence rather than \"Score NaN%.\":\n // there is a grade, and this screen cannot say what it was. No\n // `data-passed`, because there is no verdict to style.\n return (\n <div\n className=\"lk-ra-outcome\"\n {...(reading.status !== undefined ? { 'data-status': reading.status } : {})}\n >\n <p className=\"lk-ra-grade\">{s.couldNotBeGraded}</p>\n </div>\n );\n }\n}\n\n/**\n * Reads a text aloud and hands the recording to the application.\n *\n * The SDK records; it never judges. A take is captured as mono 16-bit PCM WAV\n * at 16 kHz — the one format `inspectWav` measures directly — handed to\n * `recordingBinding.upload`, and reported through `onSubmit`. Whether anything\n * assesses it, and where, is the application's: in `practice` an optional\n * `assess` may come back with a grade, and in `exam` nothing is ever assessed\n * on the client.\n *\n * **`recordingBinding.upload` is required outside `review`** and its absence\n * throws at render, in production too: a learner must never speak into a\n * control that stores nothing. `assess` is not required — a binding without it\n * records, uploads and submits, and shows a notice in place of the feedback.\n *\n * **A grade that comes back is read, not trusted** — whether `assess` returned\n * it or `outcome` carries it. A score that is not a number between 0 and its\n * maximum, or a `passed` that is not a boolean, is shown as could-not-be-graded\n * and never reaches `onComplete` or a statement; `onComplete` fires exactly when\n * a score is on screen, with that score.\n *\n * **Under a strict Content-Security-Policy** the take is played back from a\n * `blob:` URL, so `media-src` must allow `blob:` — refused, the player and the\n * per-word buttons give way to a note saying the take cannot be played here —\n * and the capture module loads from one too, so either `script-src` allows\n * `blob:` or `workletUrl` names a self-hosted copy of `CAPTURE_PROCESSOR_SOURCE`.\n *\n * ```tsx\n * <ReadAloud\n * data={item}\n * recordingBinding={{ upload, assess }}\n * onSubmit={persist}\n * onComplete={record}\n * />\n * ```\n */\nexport function ReadAloud({\n data,\n onComplete,\n onSubmit,\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 recordingBinding,\n assessment,\n breakThreshold,\n monotoneThreshold,\n workletUrl,\n delivery,\n}: ReadAloudProps) {\n const isExam = renderMode === 'exam';\n // A read-aloud's grade comes back from the host's assessor, but showing it is\n // still feedback: without it the learner hears that the take was handed in,\n // and the grade still reaches `onComplete`. It has no solution and no hints.\n const policy = useDeliveryPolicy(delivery);\n const isReview = renderMode === 'review';\n const s = useLkStrings(strings);\n // The pager slot this component sits in, if any: the capture group its pane\n // stops, and where the state of each take is reported. A context and never a\n // prop, so none of it is on this component's published signature — a\n // consumer string in a prop could stop another pager's microphone. `null`\n // for a standalone component, which then belongs to no group at all and\n // releases the microphone from its own unmount alone.\n const slot = useContext(SequenceSlotContext);\n const captureGroup = slot?.captureGroup;\n\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n // No `hasKey` branch, unlike `<Dictation>`: a read-aloud item has no answer\n // key, so `redact()` produces a projection the strict redacted schema\n // describes exactly and there is no revealed variant to make room for.\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('read-aloud', data);\n return result.success ? null : new ActivitySchemaError('read-aloud', result.errors);\n }, [data]);\n\n // The bounds a take is captured under, READ and not merely defaulted, because\n // `practice` renders unvalidated content in production: a `maxSeconds` of\n // 100000 arms a recording that never stops itself, a `maxTakes` of \"1\" meant\n // unlimited takes, and a minimum above the maximum refused every take. One\n // reader owns all of it — see `readRecordingBounds`.\n const { maxSeconds, minSeconds, maxTakes } = readRecordingBounds(data.recording);\n const readingKey = readingKeyOf(data, { maxSeconds, minSeconds, maxTakes });\n\n const recorder = useSpeechRecorder(\n useMemo(\n () => ({\n maxDurationMs: maxSeconds * 1000,\n ...(minSeconds !== undefined ? { minDurationMs: minSeconds * 1000 } : {}),\n ...(maxTakes !== undefined ? { maxTakes } : {}),\n // Forwarded, never defaulted: without it the recorder mints its own\n // `blob:` module, which is the one a strict policy refuses.\n ...(workletUrl !== undefined ? { workletUrl } : {}),\n }),\n [maxSeconds, minSeconds, maxTakes, workletUrl],\n ),\n );\n\n const { state, start, complete, getTimeSpent, reset } = useActivityState(\n defaultSubmitted === true ? 'completed' : 'idle',\n );\n const [phase, setPhase] = useState<SubmitPhase>({ kind: 'idle' });\n const [uploaded, setUploaded] = useState<RecordingRef | null>(null);\n const [takeUrl, setTakeUrl] = useState<string | null>(null);\n const [playbackUrl, setPlaybackUrl] = useState<string | null>(null);\n // Re-keys the alert so the SAME message announces again — a learner who is\n // refused the microphone twice hears it twice.\n const [errorNonce, setErrorNonce] = useState(0);\n\n const ids = useId();\n const titleId = `${ids}-title`;\n // Per mount, not per activity id: two renderings of one item on a page (a\n // review list, a preview beside a sequence) are separate players.\n const [modelsGroup] = useState(() => {\n modelsGroupCount += 1;\n return `lk-ra-models-${modelsGroupCount}`;\n });\n\n // The latest callbacks, so an effect can fire an event without depending on a\n // binding or a handler the host rebuilds on every render.\n const interactionRef = useRef(onInteraction);\n interactionRef.current = onInteraction;\n const bindingRef = useRef(recordingBinding);\n bindingRef.current = recordingBinding;\n // Guards a second submit while one is in flight: two clicks would upload the\n // same take twice and charge the application for two assessments.\n const submittingRef = useRef(false);\n // Which reading this instance is on. Everything asynchronous reads it once\n // and re-reads it after every await, so an instance that has been unmounted,\n // or handed a different reading, reports nothing for the one it has left —\n // the treatment the playback-url effect below already gives itself.\n //\n // A counter rather than the boolean `live` that effect uses, because a\n // boolean cleared on the reading reset would stay cleared and leave a\n // perfectly live component unable to submit its next take.\n const runRef = useRef(0);\n // The pager slot as of the latest render, and the last take it was told\n // about, so an unmount can tell it that take is over — a take whose result\n // this instance will now never report must not hold a set open for ever.\n const slotRef = useRef(slot);\n slotRef.current = slot;\n const reportedTakeRef = useRef<number | null>(null);\n useEffect(\n () => () => {\n runRef.current += 1;\n const take = reportedTakeRef.current;\n if (take !== null) {\n slotRef.current?.takeState(take, 'settled');\n }\n },\n [],\n );\n\n const fire = useCallback(\n (type: InteractionKind, payload: Record<string, unknown>): void => {\n interactionRef.current?.({ type, activityId: data.id, timestamp: Date.now(), payload });\n },\n [data.id],\n );\n\n // `recording-started` and `recording-stopped` are reported from the recorder's\n // own status rather than from the click handlers: a take also ends when it\n // reaches its bound, and a handler would report only the ends a learner asked\n // for.\n const lastStatusRef = useRef<SpeechRecorderStatus>(recorder.status);\n useEffect(() => {\n if (lastStatusRef.current === recorder.status) {\n return;\n }\n lastStatusRef.current = recorder.status;\n if (recorder.status === 'recording') {\n fire('recording-started', { takes: recorder.takesUsed, durationMs: 0 });\n } else if (recorder.status === 'recorded' && recorder.take !== null) {\n fire('recording-stopped', {\n takes: recorder.takesUsed,\n durationMs: recorder.take.durationMs,\n });\n }\n }, [recorder.status, recorder.take, recorder.takesUsed, fire]);\n\n useEffect(() => {\n if (recorder.error !== null) {\n setErrorNonce((nonce) => nonce + 1);\n }\n }, [recorder.error]);\n\n // The take preview. Guarded because an embedder may forbid object URLs, and a\n // preview is worth less than the take it previews.\n const take = recorder.take;\n useEffect(() => {\n if (take === null || typeof URL.createObjectURL !== 'function') {\n return;\n }\n const url = URL.createObjectURL(take.blob);\n setTakeUrl(url);\n return () => {\n URL.revokeObjectURL(url);\n setTakeUrl(null);\n };\n }, [take]);\n\n const restored = recordingOf(value ?? defaultValue);\n const restoredKey = restored?.key ?? null;\n // Keyed by the reading as well as the take: the reading reset below clears\n // the link, and a link nothing mints again would leave the stored take with\n // no player for as long as it stays on screen.\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by the stored take and the reading, not by the identity of a response object a host rebuilds every render\n useEffect(() => {\n const binding = bindingRef.current;\n if (!isReview || restored === null || binding?.playbackUrl === undefined) {\n return;\n }\n let live = true;\n void Promise.resolve(binding.playbackUrl(restored)).then(\n (url) => {\n if (live) {\n setPlaybackUrl(url);\n }\n },\n () => {\n /* a link that could not be minted; the marks still render without it */\n },\n );\n return () => {\n live = false;\n };\n }, [isReview, restoredKey, readingKey]);\n\n // What the registry may do to this component, and it is narrow: release the\n // DEVICE, and only while one is actually open.\n //\n // The pager stops a hidden slot's captures on EVERY hidden transition, so a\n // join that discarded unconditionally threw away a take that was already\n // encoded — and `discard` deliberately refunds nothing, so Next then Prev\n // left the learner an activity they could no longer answer and no message\n // saying why. That contradicts the pager's own founding principle: slots stay\n // mounted precisely so nobody silently loses an answer. A capture still\n // running has nothing to lose yet; a finished take has everything.\n //\n // The status is read from a ref so this callback keeps ONE identity and the\n // effect below joins the group exactly once, as `playback-group`'s does.\n const statusRef = useRef(recorder.status);\n statusRef.current = recorder.status;\n const releaseMicrophone = useCallback(() => {\n if (capturing(statusRef.current)) {\n recorder.discard();\n }\n }, [recorder.discard]);\n\n // The registry SUPPLEMENTS the recorder's own unmount cleanup; it covers the\n // one case that cleanup cannot, a component left mounted and merely hidden.\n useEffect(() => {\n if (captureGroup === undefined) {\n return;\n }\n return joinCaptureGroup(captureGroup, releaseMicrophone);\n }, [captureGroup, releaseMicrophone]);\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.\n const defaultSubmittedRef = useRef(defaultSubmitted);\n useEffect(() => {\n defaultSubmittedRef.current = defaultSubmitted;\n }, [defaultSubmitted]);\n // Keyed by the READING, never by the identity of `data` — see `readingKeyOf`\n // for the regression that identity caused. A host that rebuilds an\n // unchanged item keeps the take, the budget and any grade still in flight.\n const lastReadingRef = useRef(readingKey);\n useEffect(() => {\n if (lastReadingRef.current === readingKey) {\n return;\n }\n lastReadingRef.current = readingKey;\n submittingRef.current = false;\n // Anything still in flight belongs to the reading that has just left.\n runRef.current += 1;\n setPhase({ kind: 'idle' });\n setUploaded(null);\n setPlaybackUrl(null);\n // `reset`, not `discard`: the takes belong to the READING, and `discard`\n // refunds none of them on purpose. A new reading that inherited the old\n // one's spent takes renders a recorder that looks live and refuses to\n // record — reachable in `<ActivityPreview>`, whose remount key is derived\n // from the media alone, so an author who tries one take and then edits the\n // reference text can never record again without reloading the page.\n recorder.reset();\n reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [readingKey, reset, recorder.reset]);\n\n // What became of each take, told to the pager slot this sits in: the one\n // channel through which a sequence learns whether its set can be reported.\n //\n // Read from the committed PHASE, and only from it. Every way a submit can end\n // — a grade, a take with no grade, a failure, a retry, a learner recording\n // again, a reading reset — ends in a phase, so none of them can leave a set\n // waiting on a take this component is done with. And an effect runs after\n // the commit, which is after `onSubmit` and `onComplete` were called for the\n // same phase: a pager told a take has settled has already been handed\n // everything that take produced. The one state that must not wait for a\n // commit — a take on its way — is reported as the submit starts.\n const phaseTake = phase.kind === 'idle' ? null : phase.take;\n const phaseTakeState = takeStateOf(phase, uploaded);\n useEffect(() => {\n // An idle phase settles the take before it: re-recording drops a take\n // whose failure was being offered a retry, and so does a reading reset.\n const take = phaseTake ?? reportedTakeRef.current;\n if (slot === null || take === null) {\n return;\n }\n reportedTakeRef.current = take;\n slot.takeState(take, phaseTakeState);\n }, [slot, phaseTake, phaseTakeState]);\n\n // Model recordings stay silent for as long as the learner records: a\n // microphone that hears the model reading would assess the model's\n // pronunciation, not theirs.\n //\n // Pausing once, when the capture starts, is not enough — the play controls\n // are still on screen and still work throughout the take, so the hazard\n // simply moves one door along. The listener holds the guard for the whole\n // capture and is removed with it; `play` does not bubble, so it is taken in\n // the capture phase. It is what silences the browser's own control bar; a\n // budgeted model is refused before it is charged, by the `disabled` its\n // player is rendered with below.\n const modelsRef = useRef<HTMLDivElement>(null);\n const isCapturing = capturing(recorder.status);\n useEffect(() => {\n if (!isCapturing) {\n return;\n }\n const node = modelsRef.current;\n if (node === null) {\n return;\n }\n const hush = (): void => {\n for (const media of node.querySelectorAll('audio, video')) {\n const element = media as HTMLMediaElement;\n if (!element.paused) {\n element.pause();\n }\n }\n };\n hush();\n node.addEventListener('play', hush, true);\n return () => {\n node.removeEventListener('play', hush, true);\n };\n }, [isCapturing]);\n\n const slowMedia = useMemo(\n () => slowMediaFor(data, s.readAloudSlowRecording),\n [data, s.readAloudSlowRecording],\n );\n\n // The learner's own take, as every element that plays it plays it: the\n // preview before it is sent, the stored link a review mints, and the\n // per-word buttons in the feedback panel. One refusal covers all of them.\n const takeAudio = isReview ? playbackUrl : takeUrl;\n const playback = usePlaybackRefusal(takeAudio);\n // Where focus goes when a retry takes the \"Try again\" button away: the\n // submit it retries, which says it is busy for as long as the retry runs.\n const submitRef = useRef<HTMLButtonElement>(null);\n\n // All hooks are called before these throws, so hook order stays stable.\n if (devError) {\n throw devError;\n }\n // `review` needs no upload: it renders what a server already stored.\n if (!isReview && recordingBinding?.upload === undefined) {\n throw new Error(\n `ReadAloud \"${data.id}\" was rendered in renderMode \"${renderMode}\" without ` +\n '`recordingBinding.upload`. The learner could record a take that is stored ' +\n 'nowhere and submitted as nothing, which looks like a working activity the ' +\n 'whole way through. Pass a `recordingBinding` whose `upload` puts the take in ' +\n 'your storage and returns its key. `assess` is optional: without it the take is ' +\n 'still recorded, uploaded and submitted, and a notice replaces the feedback.',\n );\n }\n\n const submitted = state === 'completed';\n const inactive = disabled === true || submitted || isReview;\n const busy = phase.kind === 'uploading' || phase.kind === 'assessing';\n const takesLeft = maxTakes === undefined ? undefined : Math.max(0, maxTakes - recorder.takesUsed);\n\n /** Marks a failure so the alert announces even when the sentence repeats. */\n const raise = (next: SubmitPhase): void => {\n setErrorNonce((nonce) => nonce + 1);\n setPhase(next);\n };\n\n /**\n * Tells the pager slot, if there is one, that `take` is on its way — before\n * anything is awaited. The report a pager's set waits on must exist before\n * the first result it waits for can arrive; the take's later states follow\n * from the phase it reaches (see the effect above).\n */\n const beginTakeReport = (take: number): void => {\n reportedTakeRef.current = take;\n slot?.takeState(take, 'in-flight');\n };\n\n const runAssess = async (\n ref: RecordingRef,\n current: RecordedTake | null,\n take: number,\n ): Promise<void> => {\n const assess = bindingRef.current?.assess;\n const durationMs = current?.durationMs ?? 0;\n const takes = recorder.takesUsed;\n const run = runRef.current;\n if (assess === undefined) {\n // D5: a binding that stores but does not judge is a supported binding,\n // not a mistake — it is what an authoring preview supplies.\n setPhase({ kind: 'unassessed', take });\n return;\n }\n setPhase({ kind: 'assessing', take });\n fire('assessment-requested', { takes, durationMs });\n let answer: unknown;\n try {\n answer = await assess(ref);\n } catch {\n // Detached while the assessor was working: an instance that has been\n // unmounted, or handed a different reading, reports nothing for the one\n // it has left — not a phase, not an event, and not a grade.\n if (run !== runRef.current) {\n return;\n }\n raise({ kind: 'assess-failed', take, retryable: true });\n fire('assessment-failed', { takes, durationMs, retryable: true });\n return;\n }\n if (run !== runRef.current) {\n return;\n }\n // Read once, here, and nothing below touches `answer` again: the phase the\n // screen renders from, `onComplete` and the statement all take what the\n // reader returned, so they cannot disagree about what came back.\n const result = readAssessResult(answer);\n if (result.status === 'failed') {\n raise({ kind: 'assess-failed', take, retryable: result.retryable });\n fire('assessment-failed', { takes, durationMs, retryable: result.retryable });\n return;\n }\n if (result.status === 'unscorable') {\n // Reported as a failed assessment because that is what it is to a\n // consumer's log — a take that produced no grade — and `code` says which\n // kind. `retryable` is true because another take is offered.\n setPhase({ kind: 'unscorable', take, code: result.code });\n fire('assessment-failed', {\n takes,\n durationMs,\n retryable: true,\n ...(result.code !== undefined ? { code: result.code } : {}),\n });\n return;\n }\n const timeSpent = getTimeSpent();\n setPhase({ kind: 'graded', take, assessment: result.assessment, grade: result.grade });\n const grade = result.grade;\n if (grade === null) {\n // A judgement that carries no grade the reader can show is not an\n // `ActivityResult`: reporting one would mean inventing the score, exactly\n // as an `unscorable` result would. To a consumer's log it is what an\n // unscorable take is — an assessment that produced no grade, with another\n // take offered.\n fire('assessment-failed', { takes, durationMs, retryable: true });\n return;\n }\n\n // xAPI's `score.scaled` is a fraction in [-1, 1], and the SDK's builder\n // writes `ScoringResult.score` into it verbatim, then validates — so a\n // points-based grade (82 out of 100, which `GradeRecord` allows and a\n // grader's own units readily produce) does not merely ship a wrong number:\n // the build THROWS, and the grade never reaches the screen at all. The read\n // grade's fraction is in 0..1 by construction, and `passed` is a boolean by\n // construction, so the statement validates whatever the host sent. The\n // learner's own numbers go to `onComplete` untouched.\n //\n // Stamped with its take, as the response was: a pager records a grade only\n // for the take its slot holds now, so a grade that outlived a newer take\n // can never be filed beside that newer take's response.\n onComplete?.(\n stampTake(\n {\n score: grade.score,\n maxScore: grade.maxScore,\n passed: grade.passed,\n timeSpent,\n 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 timeSpentMs: timeSpent,\n response: result.recognizedText ?? '',\n // `ScoringResult.details` is required where `GradeRecord.details` is\n // optional, and an empty list is the honest reading of \"no per-word\n // marks were stored\" — never a mark of its own.\n scoringResult: {\n score: fractionOfGrade(grade),\n maxScore: 1,\n passed: grade.passed,\n feedback: grade.feedback,\n details: grade.details ?? [],\n },\n }),\n },\n take,\n ),\n );\n };\n\n const submitTake = async (current: RecordedTake | null): Promise<void> => {\n const binding = bindingRef.current;\n if (inactive || submittingRef.current || binding === undefined) {\n return;\n }\n submittingRef.current = true;\n const takes = recorder.takesUsed;\n const run = runRef.current;\n // Every submit is a take of its own, a retried upload included: a number\n // no earlier submit on this page had, so whatever an earlier one still\n // brings back cannot be taken for this one's.\n const take = mintTake();\n beginTakeReport(take);\n try {\n let ref: RecordingRef | null = null;\n if (current !== null) {\n setPhase({ kind: 'uploading', take });\n try {\n ref = await binding.upload(current);\n } catch {\n if (run !== runRef.current) {\n return;\n }\n // The take never reached storage, so nothing is submitted: a take\n // that failed to upload must not become a blank response.\n raise({ kind: 'upload-failed', take });\n fire('recording-upload-failed', {\n takes,\n durationMs: current.durationMs,\n retryable: true,\n });\n return;\n }\n // Detached while the take travelled. The bytes are in the application's\n // storage and this instance drops their key, which is a smaller cost\n // than the alternative: `onSubmit` carries no activity id, so a\n // response reported for a reading the learner has left is one a pager\n // would file against whatever is on screen now.\n if (run !== runRef.current) {\n return;\n }\n setUploaded(ref);\n fire('recording-uploaded', { takes, durationMs: current.durationMs });\n }\n onSubmit?.(stampTake(responseOf(ref, takes), take));\n fire('submitted', { takes, durationMs: current?.durationMs ?? 0 });\n if (isExam || ref === null) {\n // Exam never assesses, never scores, never reveals, and builds no xAPI\n // statement: its `correctResponsesPattern` would be the answer key, and\n // a read-aloud is graded on a server from the stored bytes.\n complete();\n setPhase({ kind: 'submitted', take });\n return;\n }\n await runAssess(ref, current, take);\n } finally {\n submittingRef.current = false;\n }\n };\n\n /**\n * Judges the take that is already stored, again — the SAME take, so the same\n * number. Guarded like a submit: an assessment costs an application money,\n * and two presses of one retry must not buy two of them.\n */\n const retryAssess = async (ref: RecordingRef, take: number): Promise<void> => {\n if (submittingRef.current) {\n return;\n }\n submittingRef.current = true;\n beginTakeReport(take);\n try {\n await runAssess(ref, recorder.take, take);\n } finally {\n submittingRef.current = false;\n }\n };\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive || busy || recorder.take === null || phase.kind !== 'idle') {\n return;\n }\n void submitTake(recorder.take);\n };\n\n const beginTake = (): void => {\n if (inactive || busy || !recorder.canRecord) {\n return;\n }\n if (recorder.take !== null) {\n fire('recording-discarded', {\n takes: recorder.takesUsed,\n durationMs: recorder.take.durationMs,\n });\n }\n setPhase({ kind: 'idle' });\n setUploaded(null);\n start();\n void recorder.start();\n };\n\n // The reading's own language and the direction its tag names. No `_`→`-`\n // normalisation, unlike `<Dictation>`: `ReadAloudData.locale` is canonical by\n // schema, so there is nothing to normalise.\n const contentLang = typeof data.locale === 'string' ? data.locale : undefined;\n const contentDir = localeDirectionOf(data.locale);\n\n // What the host sent, as the one reader of a host's grade reads it. Every\n // path below that shows or decides anything about a grade — the summary, the\n // panel, the marks, who renders the score, the announcement — reads these and\n // never `outcome` or `assessment` directly.\n const outcomeReading = useMemo(() => readOutcome(outcome), [outcome]);\n const keptEvidence = useMemo(() => readEvidence(assessment), [assessment]);\n const storedGrade =\n outcomeReading?.kind === 'graded' && outcomeReading.status === 'graded'\n ? outcomeReading.grade\n : null;\n // The marks a review shows: the assessment when the caller kept one, and the\n // stored details otherwise. With neither, the score is shown and no marks are.\n const storedMarks = useMemo(\n () =>\n policy.feedback && isReview && keptEvidence === null && storedGrade?.details !== undefined\n ? marksFromDetails(storedGrade.details)\n : null,\n [policy.feedback, isReview, keptEvidence, storedGrade],\n );\n // No panel at all without feedback: the marks, the score and the grader's\n // words are all in it.\n const feedbackAssessment = !policy.feedback\n ? null\n : isReview\n ? keptEvidence\n : phase.kind === 'graded'\n ? phase.assessment\n : null;\n const feedbackGrade = isReview ? storedGrade : phase.kind === 'graded' ? phase.grade : null;\n // A take the page refused to play is handed to no per-word button: each of\n // them would do nothing when pressed, and the note in the player's place\n // already says why.\n const feedbackAudio = playback.refused ? null : takeAudio;\n\n // Whether the score and the grader's words are already on screen inside the\n // feedback panel, which is what decides who renders them here.\n //\n // The props say the panel WAS GIVEN a grade; only the DOM says it rendered\n // one. The panel can fail outright — D2's dev throw for evidence the\n // validator refuses is one way, and nothing on either path validates a host's\n // grade either — and clipping on the props alone then hides a score that\n // exists nowhere, leaving a learner \"this activity could not be displayed\"\n // and no percentage at all. So the props may only ever clip for the single\n // commit before the fact arrives, and the fact may always un-clip: a grade on\n // screen twice is a far smaller defect than a grade nowhere. `.lk-pf-grade`\n // is the panel's own hook for this, named as such in its source.\n const rootRef = useRef<HTMLFormElement>(null);\n const [panelLostTheGrade, setPanelLostTheGrade] = useState(false);\n // Only a READ grade is handed to the panel, and the panel shows a read grade's\n // score without exception — so the prediction is made from the reader's\n // output, and never from a grade the panel would refuse.\n const panelHasGrade = feedbackAssessment !== null && feedbackGrade !== null;\n const gradeShownInPanel = panelHasGrade && !panelLostTheGrade;\n // Deliberately without a dependency list: what it reads is the committed DOM,\n // which no list can name. The updater keeps the settled value a fixed point,\n // so a render that changes nothing here costs one extra pass and then stops.\n useEffect(() => {\n const root = rootRef.current;\n const lost = panelHasGrade && (root === null || root.querySelector('.lk-pf-grade') === null);\n setPanelLostTheGrade((was) => (was === lost ? was : lost));\n });\n\n const announcement = useMemo<Announcement | null>(() => {\n if (isReview) {\n return null;\n }\n // The two moments of a capture, said once each. The per-second counter used\n // to be a live region of its own, so a ten-second take produced ten\n // announcements — and on any device without headphones a screen reader\n // speaks each of them into the take about to be assessed FOR PRONUNCIATION.\n // This component already stops the model recordings for exactly that\n // reason; the counter it generates itself is the same hazard.\n if (recorder.status === 'recording') {\n return { text: s.readAloudRecordingStarted, feedback: null };\n }\n // The length is in the sentence because the counter is no longer announced:\n // a take that ran to its bound reads back as \"20 of 20 seconds\", which is\n // how a learner hears that it stopped itself rather than that they stopped it.\n const stopped: Announcement | null =\n recorder.status === 'recorded' && take !== null\n ? {\n text: s.readAloudRecordingStopped(\n secondsWithin(take.durationMs, maxSeconds, 'nearest'),\n maxSeconds,\n ),\n feedback: null,\n }\n : null;\n if (isExam) {\n if (phase.kind === 'submitted' || submitted) {\n return { text: s.answerSubmitted, feedback: null };\n }\n return phase.kind === 'idle' ? stopped : null;\n }\n switch (phase.kind) {\n case 'graded': {\n const grade = phase.grade;\n // A judgement with no grade that can be read says so, in the same\n // sentence a review uses — never nothing, which reads as a grade\n // still on its way.\n if (grade === null) {\n return { text: `${s.answerSubmitted} ${s.couldNotBeGraded}`, feedback: null };\n }\n // Graded, and said to be handed in only: the policy shows no grades.\n if (!policy.feedback) {\n return { text: s.answerSubmitted, feedback: null };\n }\n return {\n text: `${s.answerSubmitted} ${s.scoreAnnouncement(percentOfGrade(grade), grade.passed)}`,\n feedback: grade.feedback,\n };\n }\n case 'unscorable':\n return { text: unscorableMessage(phase.code, s), feedback: null };\n case 'unassessed':\n return { text: s.readAloudAssessmentUnavailable, feedback: null };\n case 'idle':\n return stopped;\n default:\n // Uploading and assessing have their own pending sentence, and both\n // failures have an alert; a stale \"recording stopped\" beside either\n // would be the loudest thing on the screen.\n return null;\n }\n }, [isReview, isExam, phase, submitted, s, recorder.status, take, maxSeconds, policy.feedback]);\n\n const alertText =\n recorder.error !== null\n ? s.readAloudRecorderError(recorder.error)\n : phase.kind === 'upload-failed'\n ? s.readAloudUploadFailed\n : phase.kind === 'assess-failed'\n ? s.readAloudAssessmentFailed\n : null;\n\n return (\n <form\n className=\"lk-ra\"\n ref={rootRef}\n // A title with nothing to say would name the form with silence.\n aria-labelledby={VISIBLE_TEXT_RE.test(data.title) ? titleId : undefined}\n lang={locale}\n data-render-mode={renderMode}\n style={theme as CSSProperties | undefined}\n onSubmit={handleSubmit}\n >\n {/*\n The form's name: read from here, it keeps the content's language. A\n paragraph of its own, so without a language to go by its own first\n letter decides its direction.\n */}\n <p id={titleId} className=\"lk-ra-title\" dir={contentDir ?? 'auto'} lang={contentLang}>\n {data.title}\n </p>\n\n {typeof data.instructions === 'string' && data.instructions !== '' ? (\n <p className=\"lk-ra-instructions\" dir={contentDir ?? 'auto'} lang={contentLang}>\n {data.instructions}\n </p>\n ) : null}\n\n {/* The reading itself. Never redacted: a read-aloud has no answer key, so\n the learner's copy carries the text in full. */}\n <p className=\"lk-ra-reference\" dir={contentDir ?? 'auto'} lang={contentLang}>\n {data.referenceText}\n </p>\n\n {data.media !== undefined || slowMedia !== undefined ? (\n <div className=\"lk-ra-models\" ref={modelsRef}>\n {/*\n Each model recording is a named group (a fieldset with a legend,\n which is what `role=\"group\"` plus a label spells natively), so the\n two players are told apart by name and not only by position.\n */}\n {data.media !== undefined ? (\n <fieldset className=\"lk-ra-model\">\n <legend className=\"lk-ra-model-label\">{s.readAloudModelRecording}</legend>\n {/* The ONLY budgeted recording: the binding is whatever the pager or the caller passes.\n Disabled while a take is captured, so a press is refused BEFORE a play is\n charged — silenced after the charge, it cost the learner a play they never\n heard. Nothing is announced for the refusal: a screen reader speaking now\n would speak into the take. */}\n <ActivityMedia\n media={data.media}\n renderMode={renderMode}\n playbackGroup={modelsGroup}\n {...(mediaBudget !== undefined ? { mediaBudget } : {})}\n {...(mediaStrings !== undefined ? { mediaStrings } : {})}\n {...(strings !== undefined ? { strings } : {})}\n {...(onInteraction !== undefined ? { onInteraction } : {})}\n {...(locale !== undefined ? { locale } : {})}\n {...(disabled === true || isCapturing ? { disabled: true } : {})}\n />\n </fieldset>\n ) : null}\n {slowMedia !== undefined ? (\n <fieldset className=\"lk-ra-model\" data-slow=\"true\">\n <legend className=\"lk-ra-model-label\">{s.readAloudSlowRecording}</legend>\n {/* No budget of its own, by contract — so no binding is passed and none is demanded. */}\n <ActivityMedia\n media={slowMedia}\n renderMode={renderMode}\n playbackGroup={modelsGroup}\n {...(mediaStrings !== undefined ? { mediaStrings } : {})}\n {...(strings !== undefined ? { strings } : {})}\n {...(locale !== undefined ? { locale } : {})}\n {...(disabled === true || isCapturing ? { disabled: true } : {})}\n />\n </fieldset>\n ) : null}\n </div>\n ) : null}\n\n {/* Gone once the item is committed, rather than left behind disabled: a\n submitted exam answer is not a control a learner should still meet. */}\n {isReview || submitted ? null : (\n <div\n className=\"lk-ra-recorder\"\n data-status={recorder.status}\n data-level={\n recorder.status === 'recording'\n ? Math.min(LEVEL_STEPS, Math.round(recorder.level * LEVEL_STEPS))\n : undefined\n }\n >\n {recorder.status === 'recording' ? (\n <button\n type=\"button\"\n className=\"lk-ra-stop\"\n disabled={inactive}\n onClick={() => recorder.stop()}\n >\n {s.readAloudStop}\n </button>\n ) : (\n // aria-disabled, never disabled, once the takes are spent: a\n // natively disabled button drops keyboard focus to the page body,\n // and this is the control a learner comes back to.\n <button\n type=\"button\"\n className=\"lk-ra-record\"\n disabled={inactive}\n aria-disabled={!recorder.canRecord || busy || undefined}\n onClick={beginTake}\n >\n {recorder.take === null ? s.readAloudRecord : s.readAloudRerecord}\n </button>\n )}\n {recorder.status === 'recording' ? (\n // Visible, and hidden from assistive technology, beside the level\n // meter the recorder's `data-level` drives. Its text changes every\n // whole second, so as a live region it announced ten times in a\n // ten-second take — into the microphone. What a screen-reader\n // learner needs instead is said once each, below.\n <p className=\"lk-ra-progress\" aria-hidden=\"true\">\n {s.readAloudRecordingProgress(\n secondsWithin(recorder.elapsedMs, maxSeconds, 'down'),\n maxSeconds,\n )}\n </p>\n ) : null}\n {takesLeft !== undefined && maxTakes !== undefined ? (\n <p className=\"lk-ra-takes\">{s.readAloudTakesRemaining(takesLeft, maxTakes)}</p>\n ) : null}\n </div>\n )}\n\n {takeAudio === null ? null : playback.refused ? (\n // In the player's place, not beside it: a player the page will not\n // load looks exactly like one that works until it is pressed, and then\n // does nothing. A note rather than an alert — the learner can still\n // submit, and nothing here asks them to act.\n <p className=\"lk-ra-take-unavailable\" role=\"note\">\n {s.readAloudPlaybackUnavailable}\n </p>\n ) : (\n // biome-ignore lint/a11y/useMediaCaption: the learner's own take has no caption track to offer; its accessible name says whose recording it is\n <audio\n className=\"lk-ra-take\"\n controls\n src={takeAudio}\n aria-label={s.readAloudYourRecording}\n onError={(event) => playback.refuse(event.currentTarget)}\n {...(isReview ? {} : { 'data-take': recorder.takesUsed })}\n />\n )}\n\n {isReview || submitted ? null : (\n <div className=\"lk-ra-actions\">\n <button\n ref={submitRef}\n type=\"submit\"\n className=\"lk-ra-submit\"\n disabled={disabled === true}\n aria-disabled={recorder.take === null || phase.kind !== 'idle' || undefined}\n aria-busy={busy || undefined}\n >\n {s.submit}\n </button>\n {isExam ? (\n // Explicit, and only here: an exam answer with no recording is a\n // decision a learner takes, never something a failed upload does\n // for them.\n <button\n type=\"button\"\n className=\"lk-ra-submit-blank\"\n disabled={inactive}\n aria-disabled={busy || undefined}\n onClick={() => {\n if (!busy) {\n void submitTake(null);\n }\n }}\n >\n {s.readAloudSubmitWithoutRecording}\n </button>\n ) : null}\n {offersRetry(phase, uploaded) ? (\n <button\n type=\"button\"\n className=\"lk-ra-retry\"\n onClick={() => {\n // The retry takes this button away with the phase it answers,\n // and focus on a removed element drops to the page body — a\n // keyboard learner is then back at the top of the page with no\n // idea the retry started. It goes to the submit being retried.\n submitRef.current?.focus();\n if (phase.kind === 'upload-failed') {\n void submitTake(recorder.take);\n } else if (phase.kind === 'assess-failed' && uploaded !== null) {\n void retryAssess(uploaded, phase.take);\n }\n }}\n >\n {s.readAloudTryAgain}\n </button>\n ) : null}\n </div>\n )}\n\n {busy ? (\n <p className=\"lk-ra-pending\" role=\"status\" aria-busy=\"true\">\n {phase.kind === 'uploading' ? s.readAloudUploading : s.readAloudAssessing}\n </p>\n ) : null}\n\n {alertText !== null ? (\n <p key={errorNonce} className=\"lk-ra-alert\" role=\"alert\">\n {alertText}\n </p>\n ) : null}\n\n {feedbackAssessment !== null ? (\n <PronunciationFeedback\n data={{ referenceText: data.referenceText, locale: data.locale }}\n assessment={feedbackAssessment}\n {...(feedbackGrade !== null ? { grade: feedbackGrade } : {})}\n {...(feedbackAudio !== null ? { audioUrl: feedbackAudio } : {})}\n {...(breakThreshold !== undefined ? { breakThreshold } : {})}\n {...(monotoneThreshold !== undefined ? { monotoneThreshold } : {})}\n {...(locale !== undefined ? { locale } : {})}\n {...(strings !== undefined ? { strings } : {})}\n />\n ) : null}\n\n {storedMarks !== null ? (\n <div className=\"lk-ra-marks\">\n {/* The list's name and the hidden sentences are interface text; only\n the words carry the reading's language. */}\n <ol className=\"lk-ra-words\" aria-label={s.pronunciationWordsLabel} dir={contentDir}>\n {storedMarks.map((entry, index) => {\n const sentence = markSentence(entry, s);\n return (\n <li\n // biome-ignore lint/suspicious/noArrayIndexKey: mark order is the identity; words repeat\n key={index}\n className=\"lk-ra-word\"\n data-state={entry.state}\n >\n {/* One channel for assistive technology, not two: the same\n sentence as a `title` is an accessible description, read\n straight after the name it repeats. */}\n <span className=\"lk-visually-hidden\" style={VISUALLY_HIDDEN}>\n {sentence}\n </span>\n <span\n className=\"lk-ra-word-text\"\n aria-hidden=\"true\"\n dir={contentDir ?? 'auto'}\n lang={contentLang}\n >\n {entry.reference}\n </span>\n </li>\n );\n })}\n </ol>\n {/* Three rows, not four: a stored mark set carries no insertions. */}\n <ul className=\"lk-ra-legend\">\n {STORED_STATES.map((state) => (\n <li key={state} data-state={state}>\n {s.pronunciationLegend(state)}\n </li>\n ))}\n </ul>\n </div>\n ) : null}\n\n <FeedbackRegion id={`${data.id}-feedback`}>\n {isReview ? (\n // A grade read back is feedback; \"not graded yet\" and a grade that\n // could not be read are not.\n outcomeReading !== undefined && (policy.feedback || outcomeReading.kind !== 'graded') ? (\n <OutcomeSummary reading={outcomeReading} s={s} scoreShown={gradeShownInPanel} />\n ) : null\n ) : gradeShownInPanel ? (\n // Announced, not repeated. A grade the feedback panel is already\n // showing would otherwise be read on screen twice, feedback and all —\n // but a live region is the only thing that TELLS a screen-reader\n // learner a grade arrived, since nothing takes focus. Clipped rather\n // than `display: none`, which would stop it being announced at all.\n <span className=\"lk-visually-hidden\" style={VISUALLY_HIDDEN}>\n <AnnouncementText announcement={announcement} />\n </span>\n ) : (\n <AnnouncementText announcement={announcement} />\n )}\n </FeedbackRegion>\n </form>\n );\n}\n","'use client';\n\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport { ReadAloud as ReadAloudCore, type ReadAloudProps } from './ReadAloud.js';\n\n// `RecordedTake` travels with `RecordingBinding` because `upload`, the one\n// method a binding must have, takes one: a binding written against this subpath\n// must not need a second import path to name its own argument.\nexport type { RecordedTake } from '../../hooks/useSpeechRecorder.js';\nexport type { ReadAloudAssessResult, ReadAloudProps, RecordingBinding } from './ReadAloud.js';\n\n/**\n * Public Read Aloud activity: the core wrapped in `ActivityErrorBoundary` so a\n * render failure (including the dev schema-validation throw and the\n * missing-`upload` guard) degrades to an accessible fallback instead of\n * crashing the host.\n */\nexport function ReadAloud(props: ReadAloudProps) {\n const strings = useLkStrings(props.strings);\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title} strings={strings}>\n <ReadAloudCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/GapSelect/GapSelect.tsx","../src/components/GapSelect/index.tsx"],"names":["useLkStrings","useLearnerAi","useDeliveryPolicy","useItemScoringPolicy","useMemo","isDevelopment","assertRedacted","result","validateActivity","ActivitySchemaError","useActivityState","useTries","useState","useRef","hintsOf","useEffect","randomSessionId","seededShuffle","useComponentAiHints","score","computePassThreshold","xAPIBuilder","ANONYMOUS_ACTOR","objectIdFor","xapiDefinitionFor","stampTake","mintTake","triesSummary","jsxs","jsx","ActivityMedia","HintCost","AiHints","FeedbackRegion","outcomeShowsMarks","TryActions","AiExplanation","GapSelect","ActivityErrorBoundary"],"mappings":"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","file":"chunk-CB2A56Q7.cjs","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n assertRedacted,\n computePassThreshold,\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 { useLearnerAi } from '../../ai/LkAiProvider.js';\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 { AiExplanation, AiHints, useComponentAiHints } from '../shared/AiHelp.js';\nimport { outcomeShowsMarks, useDeliveryPolicy } from '../shared/delivery.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport { mintTake, stampTake } from '../shared/sequence-slot.js';\nimport {\n HintCost,\n hintsOf,\n TryActions,\n triesSummary,\n useItemScoringPolicy,\n useTries,\n} from '../shared/tries.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 ai: aiProp,\n delivery,\n scoring,\n}: GapSelectProps) {\n const isExam = renderMode === 'exam';\n const isReview = renderMode === 'review';\n const s = useLkStrings(strings);\n const ai = useLearnerAi(aiProp);\n const policy = useDeliveryPolicy(delivery);\n const scoringPolicy = useItemScoringPolicy(scoring);\n\n 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 // Before anything that resets them: a new question starts with no tries.\n const tries = useTries({\n policy: scoringPolicy,\n graded: renderMode === 'practice' && policy.feedback,\n submitted: state === 'completed',\n disabled: disabled === true,\n });\n const resetTries = tries.reset;\n const isControlled = value !== undefined;\n const [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 [overall, setOverall] = useState<string | null>(null);\n const [feedbackHidden, setFeedbackHidden] = useState(false);\n // Hints the learner had been shown before this mount: a restored answer's\n // count stands, and the hints shown here add to it.\n const seedHintsRef = useRef(hintsOf(value ?? defaultValue));\n const formRef = useRef<HTMLFormElement>(null);\n const feedbackRef = useRef<HTMLDivElement>(null);\n const focusAfterRef = useRef<'answer' | 'feedback' | null>(null);\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 setOverall(null);\n setFeedbackHidden(false);\n seedHintsRef.current = hintsOf(defaultValueRef.current);\n resetTries();\n reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [data, reset, resetTries]);\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 // Hints are a hook, so they are read here, before the throws below.\n const answered = state === 'completed';\n const aiHints = useComponentAiHints({\n ai,\n data,\n renderMode,\n submitted: answered,\n disabled: disabled === true,\n response: { type: 'gap-select', selections },\n locale,\n onInteraction,\n strings: s,\n delivery: policy,\n });\n // Every hint the learner has been shown on this question, counted from its\n // start: what a scoring policy charges for. Hints exist only in practice.\n const hintsRevealed = renderMode === 'practice' ? seedHintsRef.current + aiHints.used : 0;\n const responseOf = (next: Record<string, string>): GapSelectLearnerResponse => ({\n type: 'gap-select',\n selections: next,\n ...(hintsRevealed > 0 ? { hintsRevealed } : {}),\n });\n // A hint shown is part of the answer's record: say so as it happens.\n const hintsSeenRef = useRef(aiHints.used);\n // biome-ignore lint/correctness/useExhaustiveDependencies: a new hint is the trigger, and the response it reports is this render's\n useEffect(() => {\n if (hintsSeenRef.current === aiHints.used) {\n return;\n }\n hintsSeenRef.current = aiHints.used;\n if (aiHints.used > 0) {\n onChange?.(responseOf(selections));\n }\n }, [aiHints.used]);\n useEffect(() => {\n const target = focusAfterRef.current;\n if (target === null) {\n return;\n }\n focusAfterRef.current = null;\n if (target === 'answer') {\n formRef.current?.querySelector<HTMLElement>('select:not(:disabled)')?.focus();\n } else {\n feedbackRef.current?.focus();\n }\n });\n\n // All hooks are called before these throws, so hook order stays stable.\n if (devError) {\n throw devError;\n }\n // Practice grades locally, and a redacted projection has no answer key to\n // grade against — `score()` would throw RedactedScoringError from the submit\n // handler, where no error boundary can reach it and after the learner has\n // 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 // What the delivery policy lets that reveal show: right and wrong, and the\n // author's feedback. A gap select never writes in the right choice, so\n // `solutions` has nothing here to hide.\n const marking = revealing && policy.feedback;\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?.(responseOf(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 = responseOf(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 answer = score('gap-select', data as GapSelectData, response);\n complete();\n const timeSpent = getTimeSpent();\n // The question's score under the policy — see MultipleChoice.\n const counted = tries.record({\n score: answer.score,\n maxScore: answer.maxScore,\n hintsRevealed,\n });\n const mine = counted.tries[counted.tries.length - 1];\n const costed = mine === undefined ? answer.score : mine.scored;\n const scoringResult =\n costed === answer.score\n ? answer\n : { ...answer, score: costed, passed: computePassThreshold(data as GapSelectData, costed) };\n const passed =\n counted.score === scoringResult.score\n ? scoringResult.passed\n : computePassThreshold(data as GapSelectData, counted.score);\n const xapiStatement = xAPIBuilder.buildAnsweredStatement({\n actor: ANONYMOUS_ACTOR,\n object: {\n id: objectIdFor(data.id),\n name: { [data.locale ?? 'en-US']: data.title },\n ...xapiDefinitionFor(data),\n },\n scoringResult,\n timeSpentMs: timeSpent,\n response: JSON.stringify(selections),\n });\n setResult(answer);\n onComplete?.(\n stampTake(\n { score: counted.score, maxScore: counted.maxScore, passed, timeSpent, xapiStatement },\n mintTake(),\n ),\n );\n setOverall(answer.feedback);\n setSummary(\n `${s.answerSubmitted} ${s.scoreAnnouncement(Math.round(counted.score * 100), passed)}`,\n );\n fireInteraction('submitted', { selections, score: scoringResult.score });\n };\n\n /** \"Try again\": the choices stay, to be changed; the marks go. */\n const retry = (): void => {\n reset('idle');\n setSummary(null);\n setOverall(null);\n focusAfterRef.current = 'answer';\n };\n\n /** \"Keep this answer\": no more tries. A gap select never writes in the right choice. */\n const closeTries = (): void => {\n tries.close();\n focusAfterRef.current = 'feedback';\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 const extra =\n !isReview && policy.feedback && submitted && tries.counted !== null\n ? triesSummary(s, scoringPolicy, tries.counted, tries.open)\n : '';\n const announced =\n summary === null\n ? null\n : `${summary}${extra ? ` ${extra}` : ''}${overall ? ` ${overall}` : ''}`;\n\n return (\n <form\n ref={formRef}\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 marking && 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 {marking && !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 {aiHints.offered && renderMode === 'practice' ? (\n <HintCost policy={scoringPolicy} strings={s} />\n ) : null}\n <AiHints help={aiHints} ai={ai} strings={s} />\n {isReview ? null : (\n <button type=\"submit\" disabled={inactive}>\n {isExam ? s.submitAnswers : s.checkAnswers}\n </button>\n )}\n </fieldset>\n {marking && 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\n id={`${data.id}-feedback`}\n {...(scoringPolicy.retries > 0 ? { ref: feedbackRef } : {})}\n >\n {isReview\n ? policy.feedback || !outcomeShowsMarks(outcome)\n ? reviewSummary\n : null\n : policy.feedback || summary === null\n ? announced\n : s.answerSubmitted}\n </FeedbackRegion>\n <TryActions\n tries={tries}\n solutions={false}\n strings={s}\n onRetry={retry}\n onClose={closeTries}\n />\n <AiExplanation\n ai={ai}\n data={data}\n renderMode={renderMode}\n submitted={submitted}\n response={{ type: 'gap-select', selections }}\n outcome={outcome}\n locale={locale}\n onInteraction={onInteraction}\n strings={s}\n // An explanation all but always names the answer: not while another\n // try is on offer.\n delivery={tries.open ? { ...policy, solutions: false } : policy}\n />\n </form>\n );\n}\n","'use client';\n\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport { 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 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/_internal.ts"],"names":[],"mappings":";;;AA8BO,SAAS,aAAA,GAAyB;AACvC,EAAA,IAAI;AACF,IAAA,OAAO,OAAA,CAAQ,IAAI,QAAA,KAAa,YAAA;AAAA,EAClC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAOO,IAAM,eAAA,GAA6B;AAAA,EACxC,UAAA,EAAY,OAAA;AAAA,EACZ,OAAA,EAAS,EAAE,QAAA,EAAU,6CAAA,EAA+C,MAAM,WAAA;AAC5E;AAGO,SAAS,YAAY,EAAA,EAAoB;AAC9C,EAAA,OAAO,CAAA,0BAAA,EAA6B,kBAAA,CAAmB,EAAE,CAAC,CAAA,CAAA;AAC5D;AAQO,SAAS,eAAA,GAA0B;AACxC,EAAA,MAAM,IAAK,UAAA,CAA0D,MAAA;AACrE,EAAA,IAAI,GAAG,UAAA,EAAY;AACjB,IAAA,OAAO,EAAE,UAAA,EAAW;AAAA,EACtB;AACA,EAAA,OAAO,KAAK,IAAA,CAAK,MAAA,GAAS,QAAA,CAAS,EAAE,EAAE,KAAA,CAAM,CAAC,CAAC,CAAA,EAAG,IAAA,CAAK,QAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACvF","file":"chunk-EANGKAKG.cjs","sourcesContent":["import type { XAPIActor } from '@intellectif/lk-core';\n\n/**\n * The one global this module reads by name. Declared here, not globally: this\n * package compiles without Node's types on purpose, and nothing else in it may\n * come to rely on `process`.\n */\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/**\n * Whether this is anything other than a production build — the one answer\n * every development check and the error boundary's stack trace read.\n *\n * **The literal `process.env.NODE_ENV`, spelled exactly so.** It is the one\n * expression bundlers replace at build time — Vite, webpack, esbuild, Next.js\n * and Rollup's replace plugin all substitute it — and a browser has no\n * `process` to read at run time. The reading this replaces,\n * `globalThis.process?.env?.NODE_ENV`, is not that expression, so no bundler\n * touched it, and in a browser it found no `process` and answered\n * \"development\" in every production app: learners were shown a stack trace\n * where each component's production fallback belonged. Tests running in Node\n * never saw it, because Node has a `process`.\n *\n * This package's own build must leave the literal in its output, for the\n * consumer's bundler to replace. A library build that substituted it would\n * freeze every consumer into whichever mode the library was built in.\n *\n * The `try` covers code that reaches a runtime unbundled, with no `process`\n * at all: that is development, as it always was.\n */\nexport function isDevelopment(): boolean {\n try {\n return process.env.NODE_ENV !== 'production';\n } catch {\n return true;\n }\n}\n\n/**\n * Activity components cannot know the learner's identity (Req 3.1 fixes the\n * prop set). They emit a structurally-valid statement with this anonymous\n * actor; real identity is applied by the useXAPI/LRS layer (XAPIConfig.actor).\n */\nexport const ANONYMOUS_ACTOR: XAPIActor = {\n objectType: 'Agent',\n account: { homePage: 'https://github.com/intellectif/learning-kit', name: 'anonymous' },\n};\n\n/** `data.id` is not an IRI; xAPI object ids must be. Wrap it as a URN. */\nexport function objectIdFor(id: string): string {\n return `urn:learning-kit:activity:${encodeURIComponent(id)}`;\n}\n\n/**\n * Random per-mount session id for the shuffle seed. `crypto.randomUUID` is\n * unavailable outside secure contexts (plain-http LAN/staging hosts), so a\n * Math.random fallback keeps the component from crashing there — the seed\n * only randomises presentation order, so cryptographic strength is not needed.\n */\nexport function randomSessionId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `s-${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/i18n/strings.ts","../src/i18n/LkIntlProvider.tsx"],"names":[],"mappings":"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","file":"chunk-EYLBSS4Y.js","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 // ── Tries and what they cost (a scoring policy, in practice) ───────────\n /** The button that reopens a graded answer for another try. */\n tryAgain: string;\n /** Ends the tries on a question and shows the right answer. */\n showAnswer: string;\n /** Ends the tries on a question where the paper shows no right answers. */\n keepAnswer: string;\n /**\n * After a graded try, how many more there are, and what each costs: a whole\n * percentage of the question's marks, `0` when a try costs nothing.\n */\n triesLeft: (tries: number, costPercent: number) => string;\n /**\n * When an earlier try is the one that counts: which rule chose it, and what\n * the try just made scored, as a whole percentage.\n */\n countedTry: (counts: 'first' | 'best', percent: number) => string;\n /**\n * When hints or tries cost this answer something: what it scored before\n * that, as a whole percentage. The score sentence before it is what it counts.\n */\n scoreBeforeCosts: (percent: number) => string;\n /** Beside the hints on a paper where they cost marks, before one is asked for. */\n hintCost: (percent: number) => 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 /**\n * A wrong word, where the right answer may not show — the paper withholds\n * solutions, or the learner is offered another try: it is wrong, and not\n * what it should be.\n */\n dictationWordWrongUnnamed: (word: string) => string;\n /** A missing word, where the right answer may not show: missing, and not named. */\n dictationWordMissingUnnamed: 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 /** The end card, while an answer is still being stored or graded: why Finish waits. */\n videoAnswerPending: 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 // ── AI help ────────────────────────────────────────────────────────────\n /** The button that asks for an explanation of a graded answer. */\n aiExplain: string;\n /** The same button while the explanation is being written. */\n aiExplaining: string;\n /** The heading over an explanation, and its accessible name. */\n aiExplanation: string;\n /** When no explanation can be shown: the port failed, or its answer was refused. */\n aiExplanationUnavailable: string;\n /** The button that asks for a hint. */\n aiHint: string;\n /** The same button while the hint is being written. */\n aiHintLoading: string;\n /** The label before each hint. */\n aiHintNumber: (hintNumber: number) => string;\n /** The accessible name of the list of hints. */\n aiHints: string;\n /** When no hint can be shown: the port failed, or its hint gave the answer away. */\n aiHintUnavailable: string;\n /** In place of the hint button once every hint has been used. */\n aiNoMoreHints: string;\n /** Beneath every AI text: who wrote it. */\n aiNotice: 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 tryAgain: 'Try again',\n showAnswer: 'Show answer',\n keepAnswer: 'Keep this answer',\n triesLeft: (tries, costPercent) =>\n `${tries} ${tries === 1 ? 'try' : 'tries'} left.${\n costPercent > 0 ? ` Each costs ${costPercent}% of the marks.` : ''\n }`,\n countedTry: (counts, percent) =>\n `Your ${counts === 'first' ? 'first' : 'best'} try counts. This one scored ${percent}%.`,\n scoreBeforeCosts: (percent) => `Before hints and tries, this answer scored ${percent}%.`,\n hintCost: (percent) => `Each hint costs ${percent}% of this question's marks.`,\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 dictationWordWrongUnnamed: (word) => `“${word}” is wrong`,\n dictationWordMissingUnnamed: 'A word 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 videoAnswerPending: 'Saving your answer…',\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 aiExplain: 'Explain my answer',\n aiExplaining: 'Explaining…',\n aiExplanation: 'Explanation',\n aiExplanationUnavailable: 'No explanation is available right now.',\n aiHint: 'Get a hint',\n aiHintLoading: 'Finding a hint…',\n aiHintNumber: (hintNumber) => `Hint ${hintNumber}`,\n aiHints: 'Hints',\n aiHintUnavailable: 'No hint is available right now.',\n aiNoMoreHints: 'No more hints for this question.',\n aiNotice: 'Written by AI. It can make mistakes.',\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"]}
|