@intellectif/lk-react 14.0.0 → 15.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 +49 -0
- package/README.md +5 -2
- package/dist/{WrittenResponse-pB1ik8hg.d.cts → WrittenResponse-BPbhERgk.d.cts} +1 -1
- package/dist/{WrittenResponse-C0P34En1.d.ts → WrittenResponse-ZAxLR_AU.d.ts} +1 -1
- package/dist/{chunk-YEYKJFEC.cjs → chunk-26GQO5I2.cjs} +12 -12
- package/dist/{chunk-YEYKJFEC.cjs.map → chunk-26GQO5I2.cjs.map} +1 -1
- package/dist/chunk-2HAEQDHQ.js +2330 -0
- package/dist/chunk-2HAEQDHQ.js.map +1 -0
- package/dist/{chunk-47HHC4GD.js → chunk-33BQNWAQ.js} +5 -5
- package/dist/{chunk-47HHC4GD.js.map → chunk-33BQNWAQ.js.map} +1 -1
- package/dist/{chunk-SE6WWDVW.cjs → chunk-4H3JTX74.cjs} +13 -13
- package/dist/{chunk-SE6WWDVW.cjs.map → chunk-4H3JTX74.cjs.map} +1 -1
- package/dist/{chunk-TDJEKXEP.js → chunk-52GXJNQ3.js} +84 -2
- package/dist/chunk-52GXJNQ3.js.map +1 -0
- package/dist/{chunk-USBANZ7K.cjs → chunk-53YOZOGN.cjs} +5 -5
- package/dist/{chunk-USBANZ7K.cjs.map → chunk-53YOZOGN.cjs.map} +1 -1
- package/dist/{chunk-LLXCX5KK.js → chunk-AF72QUBI.js} +5 -5
- package/dist/{chunk-LLXCX5KK.js.map → chunk-AF72QUBI.js.map} +1 -1
- package/dist/{chunk-XUEIUEPI.js → chunk-CE6OGJE6.js} +5 -5
- package/dist/{chunk-XUEIUEPI.js.map → chunk-CE6OGJE6.js.map} +1 -1
- package/dist/{chunk-RRH7ZPXC.cjs → chunk-DP3DM7FR.cjs} +6 -6
- package/dist/{chunk-RRH7ZPXC.cjs.map → chunk-DP3DM7FR.cjs.map} +1 -1
- package/dist/{chunk-J2CDPIRC.cjs → chunk-DQ3VVU5T.cjs} +27 -25
- package/dist/chunk-DQ3VVU5T.cjs.map +1 -0
- package/dist/{chunk-U3APKQ7P.cjs → chunk-EKBEKDWO.cjs} +13 -13
- package/dist/{chunk-U3APKQ7P.cjs.map → chunk-EKBEKDWO.cjs.map} +1 -1
- package/dist/{chunk-RXS455MR.js → chunk-EVBYEWHO.js} +3 -2
- package/dist/chunk-EVBYEWHO.js.map +1 -0
- package/dist/{chunk-B2XQLU2E.cjs → chunk-FKGUKFC7.cjs} +24 -6
- package/dist/chunk-FKGUKFC7.cjs.map +1 -0
- package/dist/{chunk-YMBBM5K7.js → chunk-H6I2JDNW.js} +10 -10
- package/dist/{chunk-YMBBM5K7.js.map → chunk-H6I2JDNW.js.map} +1 -1
- package/dist/{chunk-XIBOFTOL.js → chunk-HA3VRF6E.js} +22 -4
- package/dist/chunk-HA3VRF6E.js.map +1 -0
- package/dist/{chunk-I33FOKZ7.js → chunk-I2QGW4GD.js} +14 -12
- package/dist/chunk-I2QGW4GD.js.map +1 -0
- package/dist/{chunk-72UGRIMM.js → chunk-IE5ZNCWN.js} +5 -5
- package/dist/{chunk-72UGRIMM.js.map → chunk-IE5ZNCWN.js.map} +1 -1
- package/dist/{chunk-NDQD3ZRY.cjs → chunk-L7NE7PR4.cjs} +13 -13
- package/dist/{chunk-NDQD3ZRY.cjs.map → chunk-L7NE7PR4.cjs.map} +1 -1
- package/dist/{chunk-D57QZILZ.js → chunk-QEB6O6GU.js} +62 -37
- package/dist/chunk-QEB6O6GU.js.map +1 -0
- package/dist/{chunk-62AKSD5Y.js → chunk-QM4AV6EG.js} +39 -11
- package/dist/chunk-QM4AV6EG.js.map +1 -0
- package/dist/{chunk-LGCUOLP6.js → chunk-RT5FTHUU.js} +5 -5
- package/dist/{chunk-LGCUOLP6.js.map → chunk-RT5FTHUU.js.map} +1 -1
- package/dist/chunk-SRD4KVAP.cjs +2332 -0
- package/dist/chunk-SRD4KVAP.cjs.map +1 -0
- package/dist/{chunk-BVRUUYYR.cjs → chunk-UAYWLYFW.cjs} +18 -18
- package/dist/{chunk-BVRUUYYR.cjs.map → chunk-UAYWLYFW.cjs.map} +1 -1
- package/dist/{chunk-D4DQFTA7.cjs → chunk-UCKA22ZJ.cjs} +3 -2
- package/dist/chunk-UCKA22ZJ.cjs.map +1 -0
- package/dist/{chunk-F4TBCRGI.cjs → chunk-WKOV25NP.cjs} +42 -13
- package/dist/chunk-WKOV25NP.cjs.map +1 -0
- package/dist/{chunk-WTS34S5E.cjs → chunk-XMJE34FY.cjs} +13 -13
- package/dist/{chunk-WTS34S5E.cjs.map → chunk-XMJE34FY.cjs.map} +1 -1
- package/dist/{chunk-SWY6PWXK.js → chunk-YWQH35VO.js} +4 -4
- package/dist/{chunk-SWY6PWXK.js.map → chunk-YWQH35VO.js.map} +1 -1
- package/dist/{chunk-LB3FEV7Z.cjs → chunk-Z6TVOAO5.cjs} +84 -2
- package/dist/chunk-Z6TVOAO5.cjs.map +1 -0
- package/dist/{chunk-36FTYJWJ.cjs → chunk-ZH5WT2IE.cjs} +80 -55
- package/dist/chunk-ZH5WT2IE.cjs.map +1 -0
- package/dist/{chunk-BO6P723E.js → chunk-ZJMTHPHY.js} +3 -3
- package/dist/{chunk-BO6P723E.js.map → chunk-ZJMTHPHY.js.map} +1 -1
- package/dist/components/ActivityPreview.cjs +12 -12
- package/dist/components/ActivityPreview.d.cts +2 -2
- package/dist/components/ActivityPreview.d.ts +2 -2
- package/dist/components/ActivityPreview.js +11 -11
- package/dist/components/ActivitySequence.cjs +13 -13
- package/dist/components/ActivitySequence.d.cts +14 -7
- package/dist/components/ActivitySequence.d.ts +14 -7
- package/dist/components/ActivitySequence.js +12 -12
- package/dist/components/Dictation.cjs +5 -5
- package/dist/components/Dictation.d.cts +1 -1
- package/dist/components/Dictation.d.ts +1 -1
- package/dist/components/Dictation.js +4 -4
- package/dist/components/FillInTheBlanks.cjs +5 -5
- package/dist/components/FillInTheBlanks.d.cts +1 -1
- package/dist/components/FillInTheBlanks.d.ts +1 -1
- package/dist/components/FillInTheBlanks.js +4 -4
- package/dist/components/GapSelect.cjs +5 -5
- package/dist/components/GapSelect.d.cts +1 -1
- package/dist/components/GapSelect.d.ts +1 -1
- package/dist/components/GapSelect.js +4 -4
- package/dist/components/InteractiveVideo.cjs +26 -0
- package/dist/components/InteractiveVideo.cjs.map +1 -0
- package/dist/components/InteractiveVideo.d.cts +77 -0
- package/dist/components/InteractiveVideo.d.ts +77 -0
- package/dist/components/InteractiveVideo.js +17 -0
- package/dist/components/InteractiveVideo.js.map +1 -0
- package/dist/components/MultipleChoice.cjs +5 -5
- package/dist/components/MultipleChoice.d.cts +1 -1
- package/dist/components/MultipleChoice.d.ts +1 -1
- package/dist/components/MultipleChoice.js +4 -4
- package/dist/components/PronunciationFeedback.cjs +4 -4
- package/dist/components/PronunciationFeedback.d.cts +5 -2
- package/dist/components/PronunciationFeedback.d.ts +5 -2
- package/dist/components/PronunciationFeedback.js +3 -3
- package/dist/components/ReadAloud.cjs +6 -6
- package/dist/components/ReadAloud.d.cts +2 -2
- package/dist/components/ReadAloud.d.ts +2 -2
- package/dist/components/ReadAloud.js +5 -5
- package/dist/components/StimulusPanel.cjs +4 -4
- package/dist/components/StimulusPanel.d.cts +1 -1
- package/dist/components/StimulusPanel.d.ts +1 -1
- package/dist/components/StimulusPanel.js +3 -3
- package/dist/components/WrittenResponse.cjs +5 -5
- package/dist/components/WrittenResponse.d.cts +3 -3
- package/dist/components/WrittenResponse.d.ts +3 -3
- package/dist/components/WrittenResponse.js +4 -4
- package/dist/i18n/LkIntlProvider.cjs +7 -7
- package/dist/i18n/LkIntlProvider.d.cts +2 -2
- package/dist/i18n/LkIntlProvider.d.ts +2 -2
- package/dist/i18n/LkIntlProvider.js +1 -1
- package/dist/index.cjs +44 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +15 -14
- package/dist/index.js.map +1 -1
- package/dist/{strings--ADnHcaA.d.cts → strings-5588Q4Te.d.cts} +118 -1
- package/dist/{strings-CtdSf2St.d.ts → strings-D0IWcsUm.d.ts} +118 -1
- package/dist/theme/ThemeProvider.cjs +6 -6
- package/dist/theme/ThemeProvider.js +1 -1
- package/dist/theme/defaults.css +2 -0
- package/dist/theme/skin.css +1365 -1
- package/package.json +13 -3
- package/dist/chunk-36FTYJWJ.cjs.map +0 -1
- package/dist/chunk-62AKSD5Y.js.map +0 -1
- package/dist/chunk-B2XQLU2E.cjs.map +0 -1
- package/dist/chunk-D4DQFTA7.cjs.map +0 -1
- package/dist/chunk-D57QZILZ.js.map +0 -1
- package/dist/chunk-F4TBCRGI.cjs.map +0 -1
- package/dist/chunk-I33FOKZ7.js.map +0 -1
- package/dist/chunk-J2CDPIRC.cjs.map +0 -1
- package/dist/chunk-LB3FEV7Z.cjs.map +0 -1
- package/dist/chunk-RXS455MR.js.map +0 -1
- package/dist/chunk-TDJEKXEP.js.map +0 -1
- package/dist/chunk-XIBOFTOL.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/shared/capture-registry.ts","../src/components/shared/sequence-slot.ts","../src/components/ReadAloud/recording-bounds.ts","../src/components/ReadAloud/ReadAloud.tsx","../src/components/ReadAloud/index.tsx"],"names":["takes","take","state","ReadAloud","jsx"],"mappings":"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","file":"chunk-QEB6O6GU.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 { createContext } from 'react';\n\n/**\n * How far a take that was handed in has got, as a pager deciding whether its\n * set is finished needs to know it.\n *\n * - `in-flight` — being stored or being judged. The set waits for it.\n * - `retryable` — it failed, and the learner is being offered another try at\n * the SAME take. The set waits for it while the learner is on that question.\n * - `settled` — nothing more will come of it: a grade, a take with no grade, a\n * failure nobody can retry, or a take the learner walked away from.\n */\nexport type TakeState = 'in-flight' | 'retryable' | 'settled';\n\n/**\n * What a pager hands the one slot it renders, and only through\n * {@link SequenceSlotContext} — never as a prop, so none of it can appear on a\n * public component's signature. A consumer string in any of it could stop\n * another component's microphone or finish another paper's set.\n */\nexport interface SequenceSlotChannel {\n /**\n * The capture-registry group whose captures stop when the slot's pane is\n * hidden. Minted per pager and per slot, so no two pagers share one.\n */\n readonly captureGroup: string;\n /**\n * Reports the state of the take numbered `take`, a number from\n * {@link mintTake}. A take numbered below the slot's latest is a take the\n * learner has since replaced, and the pager ignores it.\n */\n takeState(take: number, state: TakeState): void;\n}\n\n/** `null` outside a pager: a standalone component belongs to no slot. */\nexport const SequenceSlotContext = createContext<SequenceSlotChannel | null>(null);\n\n/**\n * The last take number handed out. Module-level and never reset, so a number\n * is unique for the life of the page: across mounts, across papers, and across\n * two pagers side by side. A result can therefore only ever match the take that\n * produced it, whatever slot and activity ids a later paper reuses — ids a host\n * chooses, and can repeat, never decide it.\n */\nlet lastTake = 0;\n\n/** A take number no other take on this page has had, or will have. */\nexport function mintTake(): number {\n lastTake += 1;\n return lastTake;\n}\n\n/**\n * The take each reported value belongs to. A `WeakMap` rather than a field, so\n * a response or a result a host receives carries no extra key it would persist\n * or compare, and the number never outlives the object.\n */\nconst takes = new WeakMap<object, number>();\n\n/** Marks `value` as produced by take `take`, and returns it. */\nexport function stampTake<T extends object>(value: T, take: number): T {\n takes.set(value, take);\n return value;\n}\n\n/** The take `value` was produced by, or `undefined` when nothing stamped it. */\nexport function takeOf(value: unknown): number | undefined {\n return typeof value === 'object' && value !== null ? takes.get(value) : undefined;\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 { 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}: ReadAloudProps) {\n const isExam = renderMode === 'exam';\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 isReview && keptEvidence === null && storedGrade?.details !== undefined\n ? marksFromDetails(storedGrade.details)\n : null,\n [isReview, keptEvidence, storedGrade],\n );\n const feedbackAssessment = 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 return grade === null\n ? { text: `${s.answerSubmitted} ${s.couldNotBeGraded}`, feedback: null }\n : {\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]);\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 outcomeReading !== undefined ? (\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,11 +1,25 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { localeDirectionOf } from './chunk-YORQQXGO.js';
|
|
3
|
-
import { ActivityErrorBoundary, isDevelopment } from './chunk-
|
|
4
|
-
import { useLkStrings } from './chunk-
|
|
3
|
+
import { ActivityErrorBoundary, isDevelopment } from './chunk-ZJMTHPHY.js';
|
|
4
|
+
import { useLkStrings } from './chunk-52GXJNQ3.js';
|
|
5
5
|
import { ActivitySchemaError, alignReadAloud, validateSpeechAssessment } from '@intellectif/lk-core';
|
|
6
|
-
import {
|
|
6
|
+
import { useState, useCallback, useId, useMemo, useRef, useEffect } from 'react';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
|
|
9
|
+
function usePlaybackRefusal(url) {
|
|
10
|
+
const [refusedUrl, setRefusedUrl] = useState(null);
|
|
11
|
+
const refuse = useCallback((element) => {
|
|
12
|
+
const source = element.getAttribute("src");
|
|
13
|
+
if (source !== null) {
|
|
14
|
+
setRefusedUrl(source);
|
|
15
|
+
}
|
|
16
|
+
}, []);
|
|
17
|
+
return { refused: url !== null && url !== void 0 && url === refusedUrl, refuse };
|
|
18
|
+
}
|
|
19
|
+
function isSourceRefusal(error) {
|
|
20
|
+
return typeof error === "object" && error !== null && error.name === "NotSupportedError";
|
|
21
|
+
}
|
|
22
|
+
|
|
9
23
|
// src/components/shared/read-grade.ts
|
|
10
24
|
var DIMENSIONS = [
|
|
11
25
|
"accuracy",
|
|
@@ -322,6 +336,8 @@ function PronunciationFeedback({
|
|
|
322
336
|
const dimensions = useMemo(() => readDimensions(assessment, reading), [assessment, reading]);
|
|
323
337
|
const audioRef = useRef(null);
|
|
324
338
|
const stopRef = useRef(null);
|
|
339
|
+
const playback = usePlaybackRefusal(audioUrl);
|
|
340
|
+
const playable = audioUrl !== void 0 && !playback.refused;
|
|
325
341
|
useEffect(
|
|
326
342
|
() => () => {
|
|
327
343
|
if (stopRef.current !== null) {
|
|
@@ -343,7 +359,10 @@ function PronunciationFeedback({
|
|
|
343
359
|
}
|
|
344
360
|
element.currentTime = startMs / 1e3;
|
|
345
361
|
try {
|
|
346
|
-
void element.play().catch(() => {
|
|
362
|
+
void element.play().catch((error) => {
|
|
363
|
+
if (isSourceRefusal(error)) {
|
|
364
|
+
playback.refuse(element);
|
|
365
|
+
}
|
|
347
366
|
});
|
|
348
367
|
} catch {
|
|
349
368
|
}
|
|
@@ -469,7 +488,7 @@ function PronunciationFeedback({
|
|
|
469
488
|
)) }) })
|
|
470
489
|
] }) : null
|
|
471
490
|
] }),
|
|
472
|
-
|
|
491
|
+
playable && typeof word.startMs === "number" && typeof word.durationMs === "number" ? /* @__PURE__ */ jsx(
|
|
473
492
|
"button",
|
|
474
493
|
{
|
|
475
494
|
type: "button",
|
|
@@ -491,10 +510,19 @@ function PronunciationFeedback({
|
|
|
491
510
|
alphabet === "ipa" ? /* @__PURE__ */ jsx("p", { className: "lk-pf-alphabet", children: s.pronunciationIpaNote }) : null
|
|
492
511
|
] }) : null,
|
|
493
512
|
past(monotone, monotoneThreshold) ? /* @__PURE__ */ jsx("p", { className: "lk-pf-monotone", role: "note", children: s.pronunciationMonotone }) : null,
|
|
494
|
-
audioUrl
|
|
513
|
+
audioUrl === void 0 ? null : playback.refused ? /* @__PURE__ */ jsx("p", { className: "lk-pf-audio-unavailable", role: "note", children: s.readAloudPlaybackUnavailable }) : (
|
|
495
514
|
// biome-ignore lint/a11y/useMediaCaption: the learner's own take has no caption track to offer, and this element is never played as content — it carries no controls and exists only so one word can be replayed from a button that names it
|
|
496
|
-
/* @__PURE__ */ jsx(
|
|
497
|
-
|
|
515
|
+
/* @__PURE__ */ jsx(
|
|
516
|
+
"audio",
|
|
517
|
+
{
|
|
518
|
+
ref: audioRef,
|
|
519
|
+
className: "lk-pf-audio",
|
|
520
|
+
src: audioUrl,
|
|
521
|
+
preload: "metadata",
|
|
522
|
+
onError: (event) => playback.refuse(event.currentTarget)
|
|
523
|
+
}
|
|
524
|
+
)
|
|
525
|
+
)
|
|
498
526
|
]
|
|
499
527
|
}
|
|
500
528
|
);
|
|
@@ -504,6 +532,6 @@ function PronunciationFeedback2(props) {
|
|
|
504
532
|
return /* @__PURE__ */ jsx(ActivityErrorBoundary, { strings, children: /* @__PURE__ */ jsx(PronunciationFeedback, { ...props }) });
|
|
505
533
|
}
|
|
506
534
|
|
|
507
|
-
export { PronunciationFeedback2 as PronunciationFeedback, VISUALLY_HIDDEN, fractionOfGrade, markSentence, percentOfGrade, readAssessResult, readEvidence, readOutcome };
|
|
508
|
-
//# sourceMappingURL=chunk-
|
|
509
|
-
//# sourceMappingURL=chunk-
|
|
535
|
+
export { PronunciationFeedback2 as PronunciationFeedback, VISUALLY_HIDDEN, fractionOfGrade, markSentence, percentOfGrade, readAssessResult, readEvidence, readOutcome, usePlaybackRefusal };
|
|
536
|
+
//# sourceMappingURL=chunk-QM4AV6EG.js.map
|
|
537
|
+
//# sourceMappingURL=chunk-QM4AV6EG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/shared/playback-refusal.ts","../src/components/shared/read-grade.ts","../src/components/PronunciationFeedback/PronunciationFeedback.tsx","../src/components/PronunciationFeedback/index.tsx"],"names":["useState","PronunciationFeedback","jsx"],"mappings":"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","file":"chunk-QM4AV6EG.js","sourcesContent":["'use client';\n\nimport { useCallback, useState } from 'react';\n\n/**\n * Whether the page refused to play the learner's own take, and the handler\n * every element that plays it reports a refusal through.\n *\n * The take is played from a `blob:` URL — the preview of a take not yet sent —\n * or from a link a review minted, and a page can refuse either without a word:\n * a Content-Security-Policy whose `media-src` does not allow `blob:` blocks the\n * load, the element fires `error`, and every control that plays it goes on\n * looking as if it works. So the refusal is decided in one place, by the URL\n * that was refused: a new take is a new URL and starts out playable again, and\n * a refusal an element reports after it was handed another URL cannot mark the\n * new one.\n *\n * `refuse` takes the element, not the URL, and reads the address the element\n * is loading at the moment it fails: an `error` belongs to the source the\n * element holds when it fires, and a closure's URL may already be a render\n * behind it.\n */\nexport function usePlaybackRefusal(url: string | null | undefined): {\n refused: boolean;\n refuse: (element: HTMLMediaElement) => void;\n} {\n const [refusedUrl, setRefusedUrl] = useState<string | null>(null);\n const refuse = useCallback((element: HTMLMediaElement) => {\n const source = element.getAttribute('src');\n if (source !== null) {\n setRefusedUrl(source);\n }\n }, []);\n return { refused: url !== null && url !== undefined && url === refusedUrl, refuse };\n}\n\n/**\n * Whether a rejected `play()` is the page refusing the source, rather than an\n * autoplay policy refusing the gesture. Only the first is the take's to report:\n * an autoplay refusal says nothing about whether the take can be played.\n */\nexport function isSourceRefusal(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as { name?: unknown }).name === 'NotSupportedError'\n );\n}\n","'use client';\n\nimport type {\n CriterionScore,\n GradeRecord,\n ReadAloudDimension,\n ScoringDetail,\n ScoringOutcome,\n SpeechAssessment,\n} from '@intellectif/lk-core';\n\n/**\n * The one reader of a grade that arrives from a host.\n *\n * Nothing validates a `GradeRecord`, an `ItemOutcome` or an assess result on\n * the way in: each is the application's, it crosses a network, and a field a\n * backend serialised as `null`, as a string or as an object is the ordinary way\n * to meet one. Every render path used to dereference them in its own way, so\n * every guard added for one shape left its sibling open — `criteria: null`\n * guarded and `criteria: [null]` not, a numeric `score` guarded and a string\n * `maxScore` shown as 8200%, `feedback` as an object taking a whole activity\n * down, `passed: null` dropping a grade from every callback in development and\n * sending an invalid statement in production.\n *\n * So the question \"what did the host send?\" has one answer, given here. The\n * panel, the outcome summary, the announcement, the decision about who shows\n * the score, `onComplete` and the xAPI statement read ONLY what these functions\n * return, and a payload they refuse is shown as could-not-be-graded — never the\n * error boundary, never `NaN`, never a percentage invented from a string.\n *\n * The rules, once:\n *\n * - **A required field is well-formed or the grade is refused.** `score` and\n * `maxScore` are finite numbers, `maxScore` above 0 and `score` between 0 and\n * it; `passed` is a boolean. A numeric string is not a number: `\"100\"` is how\n * a grade out of 100 turned into 8200%, and coercing it would only move the\n * guess somewhere else.\n * - **An optional field is absent when it is `undefined` or `null`** — how a\n * backend writes an absent optional — and otherwise well-formed or dropped.\n * `feedback` that is not a string is no feedback.\n * - **`criteria` keeps each well-formed entry and drops the rest.** Criteria are\n * named rows, each independent: an entry that cannot be read names no row.\n * - **`details` is read whole or not at all.** It is the word marks in order,\n * and a list with one word quietly missing would tell a learner they never\n * read a word they did.\n * - **Nothing here throws.** A payload whose getter throws is a payload that\n * cannot be read.\n */\n\n/** Marks a grade as read by {@link readGrade}. Type-only: it never exists at run time. */\ndeclare const READ: unique symbol;\n\n/**\n * A grade {@link readGrade} has checked: `0 <= score <= maxScore`, `maxScore >\n * 0`, both finite, `passed` a boolean, `feedback` a string or `null`, and\n * `criteria` and `details` (when present) holding only well-formed entries.\n * Still a `GradeRecord`, so it can be handed to anything that takes one — and\n * reading it again gives the same grade.\n */\nexport type ReadGrade = GradeRecord & { readonly [READ]: true };\n\n/** A stored outcome, as a renderer may show it. */\nexport type OutcomeReading =\n /** A grade to show: the outcome's own for `scored`, its `grade` for `graded`. */\n | { kind: 'graded'; status: 'scored' | 'graded'; grade: ReadGrade }\n | { kind: 'deferred' }\n /** `code` is a developer's label: put on an attribute, never shown. */\n | { kind: 'unscorable'; code: string | undefined }\n /** Claimed a grade, or claimed nothing a renderer knows, and could not be read. */\n | { kind: 'unreadable'; status: 'scored' | 'graded' | undefined };\n\n/** An assessor's answer, as a renderer and a statement may use it. */\nexport type AssessReading =\n | {\n status: 'graded';\n /** `null` when what came back cannot be read as a grade. */\n grade: ReadGrade | null;\n /** The evidence when it is an object at all; whether it VALIDATES is the panel's question. */\n assessment: SpeechAssessment | null;\n recognizedText: string | undefined;\n }\n | { status: 'unscorable'; code: string | undefined }\n | { status: 'failed'; retryable: boolean };\n\n/** One dimension row: a whole percentage, or `undefined` for a dimension nobody measured. */\nexport interface DimensionReading {\n dimension: ReadAloudDimension;\n percent: number | undefined;\n}\n\n/**\n * Every dimension a read-aloud grade can weigh, in the order a learner reads\n * them: what was said, how it flowed, how much of it was there, and how it was\n * pitched. Fixed rather than derived from the evidence, so a dimension the\n * engine did not measure is reported as unmeasured instead of vanishing.\n */\nconst DIMENSIONS: readonly ReadAloudDimension[] = [\n 'accuracy',\n 'fluency',\n 'completeness',\n 'prosody',\n];\n\nconst OUTCOMES: readonly ScoringOutcome[] = [\n 'correct',\n 'incorrect',\n 'correct-omission',\n 'incorrect-omission',\n];\n\n/**\n * How far past its own maximum a score may sit and still be read as that\n * maximum. A grader that sums weighted parts lands on `1.0000000000000002`\n * for a perfect take as readily as on `1`, and \"could not be graded\" is the\n * wrong thing to tell the learner who earned it. Far below any difference a\n * whole percentage can show.\n */\nconst OVERSHOOT = 1e-9;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\n/** An optional field a backend left out, written either way it writes one. */\nfunction isAbsent(value: unknown): value is null | undefined {\n return value === undefined || value === null;\n}\n\n/** A score on `0..max`, a hair of overshoot read as `max`; `null` for anything else. */\nfunction withinMax(score: unknown, max: number): number | null {\n if (!isFiniteNumber(score) || score < 0) {\n return null;\n }\n if (score <= max) {\n return score;\n }\n return score - max <= max * OVERSHOOT ? max : null;\n}\n\n/** A learner's or a reference response: a string, or strings. */\nfunction isResponse(value: unknown): value is string | string[] {\n return (\n typeof value === 'string' ||\n (Array.isArray(value) && value.every((entry) => typeof entry === 'string'))\n );\n}\n\n/** Runs a read that touches a host's object, whose getters may throw. */\nfunction guarded<T>(read: () => T, refused: T): T {\n try {\n return read();\n } catch {\n return refused;\n }\n}\n\nfunction readCriterion(value: unknown): CriterionScore | null {\n if (!isRecord(value) || typeof value.name !== 'string') {\n return null;\n }\n const { maxScore, notApplicable } = value;\n if (!isAbsent(maxScore) && !(isFiniteNumber(maxScore) && maxScore > 0)) {\n return null;\n }\n if (!isAbsent(notApplicable) && typeof notApplicable !== 'boolean') {\n return null;\n }\n // `CriterionScore.maxScore` defaults to 1, the SDK's scaled convention.\n const score = isAbsent(value.score) ? undefined : withinMax(value.score, maxScore ?? 1);\n if (score === null) {\n return null;\n }\n return {\n name: value.name,\n ...(score !== undefined ? { score } : {}),\n ...(!isAbsent(maxScore) ? { maxScore } : {}),\n ...(notApplicable === true ? { notApplicable } : {}),\n };\n}\n\nfunction readDetail(value: unknown): ScoringDetail | null {\n if (!isRecord(value)) {\n return null;\n }\n const { itemId, correct, outcome, learnerResponse, correctResponse, weight, score } = value;\n if (\n typeof itemId !== 'string' ||\n typeof correct !== 'boolean' ||\n !isResponse(learnerResponse) ||\n !isResponse(correctResponse)\n ) {\n return null;\n }\n const checkedOutcome = OUTCOMES.find((known) => known === outcome);\n if (!isAbsent(outcome) && checkedOutcome === undefined) {\n return null;\n }\n if (!isAbsent(weight) && !isFiniteNumber(weight)) {\n return null;\n }\n // A detail's own score is a fraction of 1.\n const checkedScore = isAbsent(score) ? undefined : withinMax(score, 1);\n if (checkedScore === null) {\n return null;\n }\n return {\n itemId,\n correct,\n learnerResponse,\n correctResponse,\n ...(checkedOutcome !== undefined ? { outcome: checkedOutcome } : {}),\n ...(!isAbsent(weight) ? { weight } : {}),\n ...(checkedScore !== undefined ? { score: checkedScore } : {}),\n };\n}\n\nfunction readDetails(value: unknown): ScoringDetail[] | undefined {\n if (!Array.isArray(value)) {\n return undefined;\n }\n const details: ScoringDetail[] = [];\n for (const entry of value) {\n const detail = readDetail(entry);\n if (detail === null) {\n return undefined;\n }\n details.push(detail);\n }\n return details;\n}\n\n/**\n * A host's grade as a checked copy, or `null` when it carries no grade that can\n * be shown or reported. See the module notes for the rules.\n */\nexport function readGrade(value: unknown): ReadGrade | null {\n return guarded(() => {\n if (!isRecord(value)) {\n return null;\n }\n const { maxScore, passed } = value;\n if (!isFiniteNumber(maxScore) || maxScore <= 0 || typeof passed !== 'boolean') {\n return null;\n }\n const score = withinMax(value.score, maxScore);\n if (score === null) {\n return null;\n }\n const criteria = Array.isArray(value.criteria)\n ? value.criteria.flatMap((entry) => {\n const criterion = readCriterion(entry);\n return criterion === null ? [] : [criterion];\n })\n : undefined;\n const details = readDetails(value.details);\n const grade: GradeRecord = {\n score,\n maxScore,\n passed,\n feedback: typeof value.feedback === 'string' ? value.feedback : null,\n ...(criteria !== undefined ? { criteria } : {}),\n ...(details !== undefined ? { details } : {}),\n };\n return grade as ReadGrade;\n }, null);\n}\n\n/** A read grade as a fraction of 1: in `0..1`, because the reader made it so. */\nexport function fractionOfGrade(grade: ReadGrade): number {\n return grade.score / grade.maxScore;\n}\n\n/** A read grade as a whole percentage: in `0..100`, because the reader made it so. */\nexport function percentOfGrade(grade: ReadGrade): number {\n return Math.round(fractionOfGrade(grade) * 100);\n}\n\n/**\n * A stored outcome as a renderer may show it, or `undefined` for none —\n * `null` included, which is how a backend writes an absent optional.\n *\n * A `graded` outcome is read through its `grade`, the record\n * `outcomeFromGrade` mirrors onto it, so the summary and the panel beside it\n * can never show two different numbers; and it must be well-formed as a whole,\n * its own `score`, `maxScore` and `passed` included — a payload with a corrupt\n * copy of the grade is not one to pick a side of.\n */\nexport function readOutcome(value: unknown): OutcomeReading | undefined {\n if (isAbsent(value)) {\n return undefined;\n }\n return guarded(\n (): OutcomeReading => {\n if (!isRecord(value)) {\n return { kind: 'unreadable', status: undefined };\n }\n switch (value.status) {\n case 'deferred':\n return { kind: 'deferred' };\n case 'unscorable':\n return {\n kind: 'unscorable',\n code: typeof value.code === 'string' ? value.code : undefined,\n };\n case 'scored': {\n const grade = readGrade(value);\n return grade === null\n ? { kind: 'unreadable', status: 'scored' }\n : { kind: 'graded', status: 'scored', grade };\n }\n case 'graded': {\n const grade = readGrade(value.grade);\n return grade === null || readGrade(value) === null\n ? { kind: 'unreadable', status: 'graded' }\n : { kind: 'graded', status: 'graded', grade };\n }\n default:\n return { kind: 'unreadable', status: undefined };\n }\n },\n { kind: 'unreadable', status: undefined },\n );\n}\n\n/**\n * An assessor's answer as a renderer and a statement may use it.\n *\n * `failed` and `unscorable` need little: a `retryable` that is not `true`\n * offers no retry, which never spends an application's money on a guess, and a\n * `code` that is not a string is no code. Everything else — a `graded` result,\n * and anything that is not an object or names no status this component knows —\n * is read as a judgement: its grade when one can be read, its evidence when it\n * is an object. A judgement with neither is shown as could-not-be-graded.\n */\nexport function readAssessResult(value: unknown): AssessReading {\n const nothing: AssessReading = {\n status: 'graded',\n grade: null,\n assessment: null,\n recognizedText: undefined,\n };\n return guarded((): AssessReading => {\n if (!isRecord(value)) {\n return nothing;\n }\n if (value.status === 'failed') {\n return { status: 'failed', retryable: value.retryable === true };\n }\n if (value.status === 'unscorable') {\n return {\n status: 'unscorable',\n code: typeof value.code === 'string' ? value.code : undefined,\n };\n }\n if (value.status !== 'graded') {\n return nothing;\n }\n const assessment = readEvidence(value.assessment);\n const recognizedText = guarded(\n () =>\n typeof assessment?.recognizedText === 'string' ? assessment.recognizedText : undefined,\n undefined,\n );\n return { status: 'graded', grade: readGrade(value.grade), assessment, recognizedText };\n }, nothing);\n}\n\n/**\n * Evidence worth handing to the panel: an object. Whether it validates is the\n * panel's to decide, because what a refusal costs differs by mode.\n */\nexport function readEvidence(value: unknown): SpeechAssessment | null {\n return isRecord(value) ? (value as unknown as SpeechAssessment) : null;\n}\n\n/** One criterion's whole percentage of its own maximum, or `undefined` when it carries none. */\nfunction percentOfCriterion(criterion: CriterionScore): number | undefined {\n if (criterion.notApplicable === true || criterion.score === undefined) {\n return undefined;\n }\n return Math.round((criterion.score / (criterion.maxScore ?? 1)) * 100);\n}\n\n/**\n * The dimension rows, from one source for the whole set and never a mix: the\n * grade's criteria when it has any it can read, and the evidence's own scores\n * otherwise. A dimension pulled out of the evidence and set beside graded ones\n * would read as if it had counted towards the grade.\n *\n * The evidence is read as defensively as the grade: in production the panel\n * shows these rows even for evidence the validator refused, which can carry\n * anything at all under `scores`. `scale` is 100 by schema, so an evidence\n * score is already a percentage — and one outside `0..100` is not one.\n */\nexport function readDimensions(\n assessment: unknown,\n grade: ReadGrade | null | undefined,\n): DimensionReading[] {\n const criteria = grade?.criteria ?? [];\n if (criteria.length > 0) {\n return DIMENSIONS.map((dimension) => {\n const criterion = criteria.find((entry) => entry.name === dimension);\n return {\n dimension,\n percent: criterion === undefined ? undefined : percentOfCriterion(criterion),\n };\n });\n }\n const scores = guarded(() => (isRecord(assessment) ? assessment.scores : undefined), undefined);\n return DIMENSIONS.map((dimension) => {\n const score = withinMax(\n guarded(() => (isRecord(scores) ? scores[dimension] : undefined), undefined),\n 100,\n );\n // Rounded rather than reformatted: the engine's own number is what was graded.\n return { dimension, percent: score === null ? undefined : Math.round(score) };\n });\n}\n","'use client';\n\nimport {\n ActivitySchemaError,\n alignReadAloud,\n type GradeRecord,\n type ReadAloudData,\n type ReadAloudWordAlignment,\n type SpeechAssessment,\n type SpeechPhoneme,\n type SpeechWord,\n type ThemeTokens,\n type ValidationResult,\n validateSpeechAssessment,\n} from '@intellectif/lk-core';\nimport { type CSSProperties, useEffect, useId, useMemo, useRef, useState } from 'react';\nimport { localeDirectionOf } from '../../i18n/direction.js';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport type { LkStrings, LkStringsOverride } from '../../i18n/strings.js';\nimport { isDevelopment } from '../_internal.js';\nimport { isSourceRefusal, usePlaybackRefusal } from '../shared/playback-refusal.js';\nimport { percentOfGrade, readDimensions, readGrade } from '../shared/read-grade.js';\n\n/**\n * Present for assistive technology, invisible on screen. Inline rather than\n * left to the optional skin, for the reason `<Dictation>` gives: the per-word\n * sentences are the marks' only channel for a screen reader, and a page that\n * does not load the skin would otherwise show every one of them beside its\n * word. Exported so `<ReadAloud>`, whose review fallback renders the same\n * marks from stored details, carries one definition rather than a copy.\n */\nexport const VISUALLY_HIDDEN: CSSProperties = {\n position: 'absolute',\n width: 1,\n height: 1,\n margin: -1,\n padding: 0,\n overflow: 'hidden',\n clip: 'rect(0 0 0 0)',\n clipPath: 'inset(50%)',\n whiteSpace: 'nowrap',\n border: 0,\n};\n\n/** The four marking states, in the order the legend lists them. */\nconst LEGEND_STATES: readonly ReadAloudWordAlignment['state'][] = [\n 'correct',\n 'mispronounced',\n 'omitted',\n 'inserted',\n];\n\nexport interface PronunciationFeedbackProps {\n /**\n * The item's reading and its language. Only these two fields, because they\n * are the only ones a mark depends on — everything else about the activity\n * belongs to whoever rendered it.\n */\n data: Pick<ReadAloudData, 'referenceText' | 'locale'>;\n /**\n * The assessor's evidence. Checked with `validateSpeechAssessment` before it\n * is aligned: see the note on this component about what a bad one costs.\n */\n assessment: SpeechAssessment;\n /**\n * The grade the evidence produced, when one exists. Its `criteria` are what\n * the dimensions are read from, because they are the numbers the learner was\n * actually graded on. A grade that cannot be read — a score that is not a\n * number between 0 and its maximum, a `passed` that is not a boolean — is\n * shown as could-not-be-graded, never as a percentage made up from it.\n */\n grade?: GradeRecord;\n /**\n * The learner's own take, playable. With it, each word that carries timings\n * gets a button that plays just that word. A take the page refuses to play —\n * a `blob:` URL under a Content-Security-Policy whose `media-src` does not\n * allow one, most often — offers no such button, and the panel says the take\n * cannot be played here.\n */\n audioUrl?: string;\n /**\n * Confidence above which a break note is shown, 0..1. **No default**: how\n * confident an engine has to be before a pause is worth telling a learner\n * about is a calibration, and the SDK has no calibrated answer. Without it no\n * break note is shown at all.\n */\n breakThreshold?: number;\n /** The same, for the monotone note read off `assessment.prosody`. */\n monotoneThreshold?: number;\n /**\n * BCP 47 tag stamped as `lang` on the root: the INTERFACE language, the one\n * this component's own sentences are written in. It is not `data.locale`,\n * which is the language of the reading and is put on the words themselves.\n */\n locale?: string;\n /** Per-instance token overrides, applied as inline CSS vars on the root. */\n theme?: Partial<ThemeTokens>;\n /** Overrides the SDK's chrome text for this panel. See {@link LkIntlProvider}. */\n strings?: LkStringsOverride;\n}\n\n/**\n * What the validator says about evidence that may not even be readable. Its\n * cross-field rules read the value directly, so a getter that throws escapes\n * it — and evidence that cannot be read is evidence refused, not a reason to\n * take the grade beside it down.\n */\nfunction checkEvidence(assessment: unknown): ValidationResult<SpeechAssessment> {\n try {\n return validateSpeechAssessment(assessment);\n } catch {\n return {\n success: false,\n errors: [{ path: [], message: 'The assessment could not be read.', code: 'unreadable' }],\n };\n }\n}\n\n/** Whether a word carries anything a disclosure would have to show. */\nfunction hasDetail(word: SpeechWord | undefined): word is SpeechWord {\n if (word === undefined) {\n return false;\n }\n return (\n word.accuracy !== undefined ||\n (word.syllables !== undefined && word.syllables.length > 0) ||\n (word.phonemes !== undefined && word.phonemes.length > 0) ||\n word.breaks !== undefined ||\n (typeof word.startMs === 'number' && typeof word.durationMs === 'number')\n );\n}\n\n/** A confidence that clears the caller's threshold. No threshold, no note. */\nfunction past(confidence: number | undefined, threshold: number | undefined): boolean {\n return (\n threshold !== undefined &&\n typeof confidence === 'number' &&\n Number.isFinite(confidence) &&\n confidence > threshold\n );\n}\n\n/** One phoneme, named by its symbol or — when the engine named none — by its place. */\nfunction phonemeLabel(phoneme: SpeechPhoneme, index: number, s: LkStrings): string {\n return phoneme.symbol ?? s.pronunciationPhonemePosition(index + 1);\n}\n\n/**\n * What a screen reader hears for one marked word — the marks' only channel for\n * assistive technology, since the glyph and the decoration are both hidden from\n * it. Exported because `<ReadAloud>`'s review fallback marks the same four\n * states from stored details, and a second copy of these four sentences would\n * be a second thing to translate.\n *\n * Branches on `state` and **never** on `heard === ''`: an inserted word whose\n * text normalises to nothing carries the empty string too, and would otherwise\n * be announced as an omission.\n */\nexport function markSentence(entry: ReadAloudWordAlignment, s: LkStrings): string {\n switch (entry.state) {\n case 'correct':\n return s.pronunciationWordCorrect(entry.reference);\n case 'mispronounced':\n return s.pronunciationWordMispronounced(entry.reference);\n case 'omitted':\n return s.pronunciationWordOmitted(entry.reference);\n default:\n return s.pronunciationWordInserted(entry.heard);\n }\n}\n\n/**\n * The per-word marks of a read-aloud take, plus the dimensions they were\n * graded on.\n *\n * **A bad assessment never destroys a good grade.** `alignReadAloud` throws a\n * `TypeError` for evidence `validateSpeechAssessment` refuses, so this\n * component runs the validator first. In development the refusal is thrown as\n * an `ActivitySchemaError`, which is the package's dev-error convention and\n * puts the problem in front of whoever wired the adapter up. In production the\n * dimensions and the grade are rendered and the word list is left out — a\n * learner who earned 82% should see 82%, not \"this activity could not be\n * displayed\" from an error boundary.\n *\n * **Nor does a bad grade take the evidence down.** `grade` is read, not\n * trusted: a score that is not a number between 0 and its maximum, or a\n * `passed` that is not a boolean, is shown as could-not-be-graded beside the\n * dimensions and the marks, and criteria that cannot be read name no dimension.\n *\n * There is **no `renderMode`**: nothing here submits, grades or reveals an\n * answer key. A read-aloud item has none, and the marks exist only once a\n * server has graded the take, so there is no mode in which they must be\n * withheld.\n *\n * ```tsx\n * <PronunciationFeedback\n * data={item}\n * assessment={assessment}\n * grade={grade}\n * audioUrl={takeUrl}\n * breakThreshold={0.75}\n * monotoneThreshold={0.6}\n * />\n * ```\n */\nexport function PronunciationFeedback({\n data,\n assessment,\n grade,\n audioUrl,\n breakThreshold,\n monotoneThreshold,\n locale,\n theme,\n strings,\n}: PronunciationFeedbackProps) {\n const s = useLkStrings(strings);\n const ids = useId();\n const [openWord, setOpenWord] = useState<number | null>(null);\n\n const checked = useMemo(() => checkEvidence(assessment), [assessment]);\n\n const devError = useMemo(\n () =>\n isDevelopment() && !checked.success\n ? new ActivitySchemaError('read-aloud', checked.errors)\n : null,\n [checked],\n );\n\n // The checked copy, never the argument, for the same reason lk-core aligns\n // from it: a property getter cannot answer the validator one word and the\n // aligner another.\n const words = useMemo<ReadAloudWordAlignment[] | null>(\n () => (checked.success ? alignReadAloud(data, checked.data) : null),\n [checked, data],\n );\n\n // The grade as the one reader of a host's grade reads it: `undefined` when\n // none was given, and `null` when one was and cannot be shown. Everything\n // below that shows a number reads this and the dimension rows, never `grade`.\n const reading = useMemo(\n () => (grade === undefined || grade === null ? undefined : readGrade(grade)),\n [grade],\n );\n const dimensions = useMemo(() => readDimensions(assessment, reading), [assessment, reading]);\n\n // The take, for the per-word play buttons. A ref and an effect, never a read\n // during render: this component server-renders like every other one here.\n const audioRef = useRef<HTMLAudioElement>(null);\n const stopRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const playback = usePlaybackRefusal(audioUrl);\n // A take the page refused is offered to no button: pressed, each would do\n // nothing, which is the silent failure the note below replaces.\n const playable = audioUrl !== undefined && !playback.refused;\n useEffect(\n () => () => {\n if (stopRef.current !== null) {\n clearTimeout(stopRef.current);\n }\n },\n [],\n );\n\n // All hooks are called before this throw, so hook order stays stable.\n if (devError) {\n throw devError;\n }\n\n /** Plays one word out of the take, and stops at the end of it. */\n const playWord = (startMs: number, durationMs: number): void => {\n const element = audioRef.current;\n if (element === null) {\n return;\n }\n if (stopRef.current !== null) {\n clearTimeout(stopRef.current);\n }\n element.currentTime = startMs / 1000;\n try {\n void element.play().catch((error: unknown) => {\n // An autoplay refusal is not this component's to report; a source the\n // page will not load is, and the element's own `error` may not have\n // fired yet for an engine that loads nothing until asked.\n if (isSourceRefusal(error)) {\n playback.refuse(element);\n }\n });\n } catch {\n /* an engine with no media stack at all; the button simply does nothing */\n }\n stopRef.current = setTimeout(() => {\n stopRef.current = null;\n element.pause();\n }, durationMs);\n };\n\n // The reading's own language and the direction its tag names, put on the\n // words and on nothing else: the sentences beside them are interface text.\n // No `_`→`-` normalisation, unlike `<Dictation>` — `ReadAloudData.locale` is\n // canonical by 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 const alphabet = checked.success ? checked.data.phonemeAlphabet : undefined;\n const monotone = checked.success ? checked.data.prosody?.monotoneConfidence : undefined;\n // Read through `at`, not through an index: `wordIndex` belongs to a public\n // type a caller may have built by hand, and it is allowed to point nowhere.\n const evidenceWords: readonly SpeechWord[] = checked.success ? checked.data.words : [];\n\n return (\n <section\n className=\"lk-pf\"\n aria-label={s.pronunciationFeedbackLabel}\n lang={locale}\n style={theme as CSSProperties | undefined}\n >\n {reading !== undefined ? (\n // `.lk-pf-grade` is load-bearing beyond the skin: `<ReadAloud>` looks\n // for it after the commit to decide whether it must render the score\n // itself, so a grade is never lost between the two components. It\n // hands this panel only a grade it has already read, so the block it\n // finds always carries the score.\n //\n // `data-passed` only where there is a verdict: a grade that could not\n // be read has none, and styling it as a fail would invent one.\n <div\n className=\"lk-pf-grade\"\n {...(reading !== null ? { 'data-passed': String(reading.passed) } : {})}\n >\n <p className=\"lk-pf-score\">\n {reading === null\n ? s.couldNotBeGraded\n : s.scoreAnnouncement(percentOfGrade(reading), reading.passed)}\n </p>\n {/* The grader's words, isolated: their direction is their own, so an\n English comment in an Arabic interface keeps its full stop at its\n end. Their language is not guessed — feedback may be written in\n either the interface's language or the reading's. */}\n {reading?.feedback ? (\n <p className=\"lk-pf-grade-feedback\" dir=\"auto\">\n {reading.feedback}\n </p>\n ) : null}\n </div>\n ) : null}\n\n <dl className=\"lk-pf-dimensions\">\n {dimensions.map(({ dimension, percent }) => (\n <div className=\"lk-pf-dimension\" key={dimension} data-dimension={dimension}>\n <dt className=\"lk-pf-dimension-name\">{s.pronunciationDimension(dimension)}</dt>\n {/* An absent score is NEVER a 0: a dimension nobody measured and a\n dimension measured at nothing are different findings, and only\n one of them is the learner's. */}\n <dd className=\"lk-pf-dimension-score\">\n {percent === undefined ? s.pronunciationNotAssessed : `${percent}%`}\n </dd>\n </div>\n ))}\n </dl>\n\n {words !== null ? (\n <>\n {/* The list's name and the hidden sentences are interface text and\n keep the interface language; only the words carry the reading's.\n Its direction is the reading's, never resolved from the list's own\n text: the first strong character there is a hidden sentence. */}\n <ol className=\"lk-pf-words\" aria-label={s.pronunciationWordsLabel} dir={contentDir}>\n {words.map((entry, index) => {\n const sentence = markSentence(entry, s);\n const shown = entry.state === 'inserted' ? entry.heard : entry.reference;\n const word =\n entry.wordIndex === undefined ? undefined : evidenceWords.at(entry.wordIndex);\n const open = openWord === index;\n const panelId = `${ids}-w${index}`;\n return (\n <li\n // biome-ignore lint/suspicious/noArrayIndexKey: alignment order is the identity; words repeat\n key={index}\n className=\"lk-pf-word\"\n data-state={entry.state}\n >\n {/* The state's only channel for assistive technology, and\n exactly one channel: the same sentence as a `title` is an\n accessible description, which a screen reader reads\n straight after the name it duplicates. */}\n <span className=\"lk-visually-hidden\" style={VISUALLY_HIDDEN}>\n {sentence}\n </span>\n {/* Decoration: the glyph the skin draws and the word itself.\n Hidden from assistive technology, which already has the\n sentence above — and the glyph is never the only channel. */}\n <span\n className=\"lk-pf-word-text\"\n aria-hidden=\"true\"\n dir={contentDir ?? 'auto'}\n lang={contentLang}\n >\n {shown}\n </span>\n {hasDetail(word) ? (\n <>\n <button\n type=\"button\"\n className=\"lk-pf-word-toggle\"\n aria-expanded={open}\n aria-controls={panelId}\n onClick={() => setOpenWord(open ? null : index)}\n >\n {s.pronunciationWordDetails(shown)}\n </button>\n {open ? (\n <div id={panelId} className=\"lk-pf-word-detail\">\n <dl className=\"lk-pf-word-facts\">\n <dt>{s.pronunciationDimension('accuracy')}</dt>\n <dd>\n {word.accuracy === undefined\n ? s.pronunciationNotAssessed\n : `${Math.round(word.accuracy)}%`}\n </dd>\n {word.syllables !== undefined && word.syllables.length > 0 ? (\n <>\n <dt>{s.pronunciationSyllables}</dt>\n <dd>\n <ul className=\"lk-pf-syllables\">\n {word.syllables.map((syllable, position) => (\n <li\n // biome-ignore lint/suspicious/noArrayIndexKey: syllables are positional and repeat within a word\n key={position}\n dir={contentDir ?? 'auto'}\n lang={contentLang}\n >\n {syllable.grapheme === undefined\n ? syllable.text\n : s.pronunciationSyllableSpelling(\n syllable.text,\n syllable.grapheme,\n )}\n </li>\n ))}\n </ul>\n </dd>\n </>\n ) : null}\n {word.phonemes !== undefined && word.phonemes.length > 0 ? (\n <>\n <dt>{s.pronunciationPhonemes}</dt>\n <dd>\n <ul className=\"lk-pf-phonemes\">\n {word.phonemes.map((phoneme, position) => (\n <li\n // biome-ignore lint/suspicious/noArrayIndexKey: a word may sound the same phoneme twice\n key={position}\n className=\"lk-pf-phoneme\"\n >\n <span className=\"lk-pf-phoneme-symbol\">\n {phonemeLabel(phoneme, position, s)}\n </span>\n {phoneme.heardAs !== undefined &&\n phoneme.heardAs.length > 0 ? (\n <>\n <span className=\"lk-pf-heard-label\">\n {s.pronunciationHeardAs}\n </span>\n <ul className=\"lk-pf-heard-as\">\n {phoneme.heardAs.map((candidate, rank) => (\n <li\n // biome-ignore lint/suspicious/noArrayIndexKey: candidates are ranked and a symbol may repeat\n key={rank}\n >\n {candidate.symbol}\n </li>\n ))}\n </ul>\n </>\n ) : null}\n </li>\n ))}\n </ul>\n </dd>\n </>\n ) : null}\n </dl>\n {playable &&\n typeof word.startMs === 'number' &&\n typeof word.durationMs === 'number' ? (\n <button\n type=\"button\"\n className=\"lk-pf-word-play\"\n onClick={() =>\n playWord(word.startMs as number, word.durationMs as number)\n }\n >\n {s.pronunciationPlayWord(shown)}\n </button>\n ) : null}\n {past(word.breaks?.unexpected, breakThreshold) ? (\n <p className=\"lk-pf-break\" data-break=\"unexpected\">\n {s.pronunciationBreakUnexpected}\n </p>\n ) : null}\n {past(word.breaks?.missing, breakThreshold) ? (\n <p className=\"lk-pf-break\" data-break=\"missing\">\n {s.pronunciationBreakMissing}\n </p>\n ) : null}\n </div>\n ) : null}\n </>\n ) : null}\n </li>\n );\n })}\n </ol>\n <ul className=\"lk-pf-legend\">\n {LEGEND_STATES.map((state) => (\n <li key={state} data-state={state}>\n {s.pronunciationLegend(state)}\n </li>\n ))}\n </ul>\n {/* Only where there are symbols to explain, and only for the alphabet\n this note names. An engine that reported another one, or none at\n all, gets no note rather than a wrong one. */}\n {alphabet === 'ipa' ? <p className=\"lk-pf-alphabet\">{s.pronunciationIpaNote}</p> : null}\n </>\n ) : null}\n\n {past(monotone, monotoneThreshold) ? (\n <p className=\"lk-pf-monotone\" role=\"note\">\n {s.pronunciationMonotone}\n </p>\n ) : null}\n\n {audioUrl === undefined ? null : playback.refused ? (\n <p className=\"lk-pf-audio-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, and this element is never played as content — it carries no controls and exists only so one word can be replayed from a button that names it\n <audio\n ref={audioRef}\n className=\"lk-pf-audio\"\n src={audioUrl}\n preload=\"metadata\"\n onError={(event) => playback.refuse(event.currentTarget)}\n />\n )}\n </section>\n );\n}\n","'use client';\n\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport {\n PronunciationFeedback as PronunciationFeedbackCore,\n type PronunciationFeedbackProps,\n} from './PronunciationFeedback.js';\n\nexport type { PronunciationFeedbackProps } from './PronunciationFeedback.js';\n\n/**\n * Public pronunciation-feedback panel: the core wrapped in\n * `ActivityErrorBoundary` so a render failure (the dev throw for evidence\n * `validateSpeechAssessment` refuses among them) degrades to an accessible\n * fallback instead of crashing the host.\n *\n * **No `activityTitle`.** Its `data` is two fields of a read-aloud item and\n * carries no title, so the production fallback is the unnamed one. Naming it\n * would mean inventing a title for a panel that never had one.\n */\nexport function PronunciationFeedback(props: PronunciationFeedbackProps) {\n const strings = useLkStrings(props.strings);\n return (\n <ActivityErrorBoundary strings={strings}>\n <PronunciationFeedbackCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { FeedbackRegion } from './chunk-RCMYEWBE.js';
|
|
3
|
-
import { ActivityErrorBoundary, isDevelopment, randomSessionId, ANONYMOUS_ACTOR, objectIdFor } from './chunk-
|
|
4
|
-
import { ActivityMedia } from './chunk-
|
|
3
|
+
import { ActivityErrorBoundary, isDevelopment, randomSessionId, ANONYMOUS_ACTOR, objectIdFor } from './chunk-ZJMTHPHY.js';
|
|
4
|
+
import { ActivityMedia } from './chunk-HA3VRF6E.js';
|
|
5
5
|
import { useActivityState } from './chunk-6N43WDVG.js';
|
|
6
|
-
import { useLkStrings } from './chunk-
|
|
6
|
+
import { useLkStrings } from './chunk-52GXJNQ3.js';
|
|
7
7
|
import { validateActivity, ActivitySchemaError, seededShuffle, score, xAPIBuilder, xapiDefinitionFor } from '@intellectif/lk-core';
|
|
8
8
|
import { useMemo, useRef, useState, useEffect, Fragment } from 'react';
|
|
9
9
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -291,5 +291,5 @@ function MultipleChoice2(props) {
|
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
export { MultipleChoice2 as MultipleChoice };
|
|
294
|
-
//# sourceMappingURL=chunk-
|
|
295
|
-
//# sourceMappingURL=chunk-
|
|
294
|
+
//# sourceMappingURL=chunk-RT5FTHUU.js.map
|
|
295
|
+
//# sourceMappingURL=chunk-RT5FTHUU.js.map
|