@intellectif/lk-react 23.0.1 → 23.1.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 +15 -0
- package/README.md +4 -4
- package/dist/{WrittenResponse-CgRrjM4S.d.ts → WrittenResponse-B7A5gHMj.d.ts} +1 -1
- package/dist/{WrittenResponse-D2K1VW_V.d.cts → WrittenResponse-BpL9p2Hn.d.cts} +1 -1
- package/dist/ai/LkAiProvider.cjs +3 -3
- package/dist/ai/LkAiProvider.d.cts +12 -2
- package/dist/ai/LkAiProvider.d.ts +12 -2
- package/dist/ai/LkAiProvider.js +1 -1
- package/dist/ai/useAiHelp.cjs +9 -7
- package/dist/ai/useAiHelp.d.cts +82 -3
- package/dist/ai/useAiHelp.d.ts +82 -3
- package/dist/ai/useAiHelp.js +2 -4
- package/dist/{chunk-VPU6UY4O.cjs → chunk-2PNSVBLQ.cjs} +5 -6
- package/dist/chunk-2PNSVBLQ.cjs.map +1 -0
- package/dist/{chunk-HZIT24BK.js → chunk-4ZZWZSWI.js} +4 -4
- package/dist/{chunk-HZIT24BK.js.map → chunk-4ZZWZSWI.js.map} +1 -1
- package/dist/{chunk-B7JU52YQ.cjs → chunk-7ALBSPYR.cjs} +34 -35
- package/dist/chunk-7ALBSPYR.cjs.map +1 -0
- package/dist/{chunk-TGTP5DKF.js → chunk-7NFY3M3W.js} +10 -10
- package/dist/{chunk-TGTP5DKF.js.map → chunk-7NFY3M3W.js.map} +1 -1
- package/dist/{chunk-E4UF4XZM.js → chunk-7SB2MJA4.js} +4 -4
- package/dist/{chunk-E4UF4XZM.js.map → chunk-7SB2MJA4.js.map} +1 -1
- package/dist/{chunk-ERV666AR.js → chunk-ABJJ3LII.js} +9 -10
- package/dist/chunk-ABJJ3LII.js.map +1 -0
- package/dist/{chunk-NFGUJ52G.cjs → chunk-BIGGXHAW.cjs} +8 -2
- package/dist/chunk-BIGGXHAW.cjs.map +1 -0
- package/dist/{chunk-O7MINPKZ.js → chunk-C7QZE5BI.js} +9 -10
- package/dist/chunk-C7QZE5BI.js.map +1 -0
- package/dist/{chunk-IYGF7PZY.cjs → chunk-CWTUWNV3.cjs} +25 -26
- package/dist/chunk-CWTUWNV3.cjs.map +1 -0
- package/dist/{chunk-4MXECBZT.js → chunk-DC4CQCK5.js} +40 -13
- package/dist/chunk-DC4CQCK5.js.map +1 -0
- package/dist/{chunk-PGAPZ6UE.cjs → chunk-DS6HN3YR.cjs} +34 -35
- package/dist/chunk-DS6HN3YR.cjs.map +1 -0
- package/dist/{chunk-U5VFJVQ2.js → chunk-FDQR4GAZ.js} +3 -4
- package/dist/chunk-FDQR4GAZ.js.map +1 -0
- package/dist/{chunk-DPNCCWOV.js → chunk-FKGMSOJF.js} +8 -9
- package/dist/chunk-FKGMSOJF.js.map +1 -0
- package/dist/{chunk-SPSKDNEE.js → chunk-G6PXJ2SA.js} +11 -12
- package/dist/chunk-G6PXJ2SA.js.map +1 -0
- package/dist/{chunk-C4PSJ3ZC.js → chunk-HNLRVRQR.js} +8 -2
- package/dist/chunk-HNLRVRQR.js.map +1 -0
- package/dist/{chunk-FISEZBLF.cjs → chunk-J3C72RPK.cjs} +7 -7
- package/dist/{chunk-FISEZBLF.cjs.map → chunk-J3C72RPK.cjs.map} +1 -1
- package/dist/{chunk-SHDSOOBF.cjs → chunk-KFLIDTDU.cjs} +25 -26
- package/dist/chunk-KFLIDTDU.cjs.map +1 -0
- package/dist/{chunk-W6UMY7PF.cjs → chunk-KHKH5LCT.cjs} +7 -8
- package/dist/chunk-KHKH5LCT.cjs.map +1 -0
- package/dist/{chunk-7F2YQWMW.cjs → chunk-LBEKRN6Y.cjs} +122 -11
- package/dist/chunk-LBEKRN6Y.cjs.map +1 -0
- package/dist/{chunk-6RIM5EQP.cjs → chunk-LPUWXIPR.cjs} +197 -12
- package/dist/chunk-LPUWXIPR.cjs.map +1 -0
- package/dist/{chunk-XS6OYYVR.cjs → chunk-MKXEM2C5.cjs} +19 -20
- package/dist/chunk-MKXEM2C5.cjs.map +1 -0
- package/dist/{chunk-CXIP7ERK.js → chunk-MXNC5UNO.js} +182 -8
- package/dist/chunk-MXNC5UNO.js.map +1 -0
- package/dist/{chunk-MXZHL4U5.cjs → chunk-NAK55BYU.cjs} +31 -32
- package/dist/chunk-NAK55BYU.cjs.map +1 -0
- package/dist/{chunk-UKUHVCZX.cjs → chunk-NDBMKGQ5.cjs} +5 -5
- package/dist/{chunk-UKUHVCZX.cjs.map → chunk-NDBMKGQ5.cjs.map} +1 -1
- package/dist/{chunk-XYJBRKBZ.js → chunk-ORT3KLGI.js} +3 -4
- package/dist/chunk-ORT3KLGI.js.map +1 -0
- package/dist/{chunk-JMEZYYCE.js → chunk-PWHECW6N.js} +11 -12
- package/dist/chunk-PWHECW6N.js.map +1 -0
- package/dist/{chunk-6GQLCCGN.js → chunk-RBS5A2DF.js} +9 -10
- package/dist/chunk-RBS5A2DF.js.map +1 -0
- package/dist/{chunk-B4JRBV56.cjs → chunk-RPWUDXG5.cjs} +6 -6
- package/dist/{chunk-B4JRBV56.cjs.map → chunk-RPWUDXG5.cjs.map} +1 -1
- package/dist/{chunk-KI6DJ5ZF.js → chunk-TCDJY3H6.js} +9 -10
- package/dist/chunk-TCDJY3H6.js.map +1 -0
- package/dist/{chunk-DZ7VLLDQ.cjs → chunk-TP5UMRAU.cjs} +66 -39
- package/dist/chunk-TP5UMRAU.cjs.map +1 -0
- package/dist/{chunk-HYOUMTPW.js → chunk-TS7J6DTY.js} +3 -3
- package/dist/{chunk-HYOUMTPW.js.map → chunk-TS7J6DTY.js.map} +1 -1
- package/dist/{chunk-OJGTJOWI.cjs → chunk-TUVENMXB.cjs} +2 -2
- package/dist/chunk-TUVENMXB.cjs.map +1 -0
- package/dist/{chunk-WZ2TBXVB.cjs → chunk-V6BA44DS.cjs} +18 -18
- package/dist/{chunk-WZ2TBXVB.cjs.map → chunk-V6BA44DS.cjs.map} +1 -1
- package/dist/{chunk-2OP5YHRR.js → chunk-XO5R73Y4.js} +120 -10
- package/dist/chunk-XO5R73Y4.js.map +1 -0
- package/dist/{chunk-QAZO3ANW.cjs → chunk-YY4PFR3Z.cjs} +34 -35
- package/dist/chunk-YY4PFR3Z.cjs.map +1 -0
- package/dist/{chunk-RX3A6KIY.js → chunk-ZPI6Q42R.js} +2 -2
- package/dist/chunk-ZPI6Q42R.js.map +1 -0
- package/dist/components/ActivityPreview.cjs +16 -18
- package/dist/components/ActivityPreview.d.cts +2 -2
- package/dist/components/ActivityPreview.d.ts +2 -2
- package/dist/components/ActivityPreview.js +15 -17
- package/dist/components/ActivitySequence.cjs +17 -19
- package/dist/components/ActivitySequence.d.cts +2 -2
- package/dist/components/ActivitySequence.d.ts +2 -2
- package/dist/components/ActivitySequence.js +16 -18
- package/dist/components/Dictation.cjs +9 -11
- package/dist/components/Dictation.d.cts +1 -1
- package/dist/components/Dictation.d.ts +1 -1
- package/dist/components/Dictation.js +8 -10
- package/dist/components/FillInTheBlanks.cjs +9 -11
- package/dist/components/FillInTheBlanks.d.cts +1 -1
- package/dist/components/FillInTheBlanks.d.ts +1 -1
- package/dist/components/FillInTheBlanks.js +8 -10
- package/dist/components/GapSelect.cjs +9 -11
- package/dist/components/GapSelect.d.cts +1 -1
- package/dist/components/GapSelect.d.ts +1 -1
- package/dist/components/GapSelect.js +8 -10
- package/dist/components/InteractiveVideo.cjs +16 -18
- package/dist/components/InteractiveVideo.d.cts +2 -2
- package/dist/components/InteractiveVideo.d.ts +2 -2
- package/dist/components/InteractiveVideo.js +14 -16
- package/dist/components/MultipleChoice.cjs +9 -11
- package/dist/components/MultipleChoice.d.cts +1 -1
- package/dist/components/MultipleChoice.d.ts +1 -1
- package/dist/components/MultipleChoice.js +8 -10
- package/dist/components/PronunciationFeedback.cjs +6 -5
- package/dist/components/PronunciationFeedback.d.cts +28 -7
- package/dist/components/PronunciationFeedback.d.ts +28 -7
- package/dist/components/PronunciationFeedback.js +5 -4
- package/dist/components/ReadAloud.cjs +9 -9
- package/dist/components/ReadAloud.d.cts +2 -2
- package/dist/components/ReadAloud.d.ts +2 -2
- package/dist/components/ReadAloud.js +8 -8
- 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 +8 -10
- package/dist/components/WrittenResponse.d.cts +3 -3
- package/dist/components/WrittenResponse.d.ts +3 -3
- package/dist/components/WrittenResponse.js +7 -9
- 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 +58 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +18 -20
- package/dist/index.js.map +1 -1
- package/dist/theme/skin.css +46 -1
- package/dist/{types-CrtIcRcK.d.ts → types-C4IsUQsK.d.ts} +16 -0
- package/dist/{types-CO0XLEN6.d.cts → types-CJgZWtl4.d.cts} +16 -0
- package/package.json +3 -3
- package/dist/chunk-2OP5YHRR.js.map +0 -1
- package/dist/chunk-4MXECBZT.js.map +0 -1
- package/dist/chunk-54DX6SLS.cjs +0 -42
- package/dist/chunk-54DX6SLS.cjs.map +0 -1
- package/dist/chunk-6GQLCCGN.js.map +0 -1
- package/dist/chunk-6RIM5EQP.cjs.map +0 -1
- package/dist/chunk-7F2YQWMW.cjs.map +0 -1
- package/dist/chunk-B7JU52YQ.cjs.map +0 -1
- package/dist/chunk-C4PSJ3ZC.js.map +0 -1
- package/dist/chunk-CXIP7ERK.js.map +0 -1
- package/dist/chunk-DPNCCWOV.js.map +0 -1
- package/dist/chunk-DXFCYNST.js +0 -35
- package/dist/chunk-DXFCYNST.js.map +0 -1
- package/dist/chunk-DZ7VLLDQ.cjs.map +0 -1
- package/dist/chunk-ERV666AR.js.map +0 -1
- package/dist/chunk-IYGF7PZY.cjs.map +0 -1
- package/dist/chunk-J2QIHZ3H.cjs +0 -27
- package/dist/chunk-J2QIHZ3H.cjs.map +0 -1
- package/dist/chunk-JMEZYYCE.js.map +0 -1
- package/dist/chunk-KI6DJ5ZF.js.map +0 -1
- package/dist/chunk-MXZHL4U5.cjs.map +0 -1
- package/dist/chunk-NFGUJ52G.cjs.map +0 -1
- package/dist/chunk-O7MINPKZ.js.map +0 -1
- package/dist/chunk-OJGTJOWI.cjs.map +0 -1
- package/dist/chunk-PGAPZ6UE.cjs.map +0 -1
- package/dist/chunk-QAZO3ANW.cjs.map +0 -1
- package/dist/chunk-RX3A6KIY.js.map +0 -1
- package/dist/chunk-SHDSOOBF.cjs.map +0 -1
- package/dist/chunk-SPSKDNEE.js.map +0 -1
- package/dist/chunk-U4KTOH7C.js +0 -22
- package/dist/chunk-U4KTOH7C.js.map +0 -1
- package/dist/chunk-U5VFJVQ2.js.map +0 -1
- package/dist/chunk-VPU6UY4O.cjs.map +0 -1
- package/dist/chunk-W6UMY7PF.cjs.map +0 -1
- package/dist/chunk-XS6OYYVR.cjs.map +0 -1
- package/dist/chunk-XYJBRKBZ.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/shared/sequence-slot.ts","../src/components/_internal.ts","../src/ai/rules.ts","../src/ai/useAiHelp.ts"],"names":["createContext","OPEN_DELIVERY_POLICY","aiSupports","aiAllowedByContent","useContext","combineDeliveryPolicies","useLearnerAi","useState","useRef","useEffect","useCallback","aiExplanationRequest","checkAiExplanation","aiHintRequest","checkAiHint","aiWritingFeedbackRequest","checkAiWritingFeedback","useMemo","aiCoachingRequest","request","checkAiCoaching"],"mappings":"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","file":"chunk-LPUWXIPR.cjs","sourcesContent":["'use client';\n\nimport type { ResolvedDeliveryPolicy, ResolvedItemScoringPolicy } from '@intellectif/lk-core';\nimport { createContext } from 'react';\nimport type { RenderMode } from '../types.js';\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 * Where a question is with its tries, as a pager deciding whether its set is\n * finished needs to know it.\n *\n * - `offered` — a graded answer short of full marks, with another try on offer.\n * The set waits for it while the learner is on that question: leaving it is\n * declining the try.\n * - `retrying` — the learner pressed \"Try again\" and has not answered again.\n * The set waits for it wherever the learner is, as it waits for any question\n * not yet answered: the grade it holds is the one being replaced.\n * - `none` — neither.\n */\nexport type TriesState = 'none' | 'offered' | 'retrying';\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 * The mode the PAPER around this question is being delivered in, read live.\n *\n * It is how the AI hooks know an exam they were not told about. A host that\n * draws its own question passes `renderMode` on to them, but nothing makes\n * it: the hooks would then default to `practice` and, under an\n * `LkAiProvider` somewhere above, offer hints on a paper of record. The set\n * around the question decides, and an `exam` here wins over anything the\n * caller passes or forgets to pass.\n */\n readonly renderMode: RenderMode;\n /**\n * The delivery policy of the PAPER around this question, read live, for the\n * same reason as the mode: whatever a host's own question was handed, the\n * paper's restrictions hold in it. See `useDeliveryPolicy`.\n */\n readonly delivery: ResolvedDeliveryPolicy;\n /**\n * The scoring policy of the PAPER around this question, read live, when the\n * paper was given one: it holds for every question in it, however drawn.\n * `undefined` when the paper has none, so a question's own applies.\n */\n readonly scoring: ResolvedItemScoringPolicy | undefined;\n /**\n * Whether the paper has closed every question's tries: it reported its set,\n * or its learner finished. A \"Try again\" after that would change a grade the\n * paper has already handed on.\n */\n readonly triesClosed: boolean;\n /** Tells the paper where this question is with its tries: see {@link TriesState}. */\n triesState(state: TriesState): void;\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","import type { ScoringDetail, XAPIActor } from '@intellectif/lk-core';\n\n/**\n * The one global this module reads by name. Declared here, not globally: this\n * package compiles without Node's types on purpose, and nothing else in it may\n * come to rely on `process`.\n */\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/**\n * Whether this is anything other than a production build — the one answer\n * every development check and the error boundary's stack trace read.\n *\n * **The literal `process.env.NODE_ENV`, spelled exactly so.** It is the one\n * expression bundlers replace at build time — Vite, webpack, esbuild, Next.js\n * and Rollup's replace plugin all substitute it — and a browser has no\n * `process` to read at run time. The reading this replaces,\n * `globalThis.process?.env?.NODE_ENV`, is not that expression, so no bundler\n * touched it, and in a browser it found no `process` and answered\n * \"development\" in every production app: learners were shown a stack trace\n * where each component's production fallback belonged. Tests running in Node\n * never saw it, because Node has a `process`.\n *\n * This package's own build must leave the literal in its output, for the\n * consumer's bundler to replace. A library build that substituted it would\n * freeze every consumer into whichever mode the library was built in.\n *\n * The `try` covers code that reaches a runtime unbundled, with no `process`\n * at all: that is development, as it always was.\n */\nexport function isDevelopment(): boolean {\n try {\n return process.env.NODE_ENV !== 'production';\n } catch {\n return true;\n }\n}\n\n/**\n * Activity components cannot know the learner's identity (Req 3.1 fixes the\n * prop set). They emit a structurally-valid statement with this anonymous\n * actor; real identity is applied by the useXAPI/LRS layer (XAPIConfig.actor).\n */\nexport const ANONYMOUS_ACTOR: XAPIActor = {\n objectType: 'Agent',\n account: { homePage: 'https://github.com/intellectif/learning-kit', name: 'anonymous' },\n};\n\n/** `data.id` is not an IRI; xAPI object ids must be. Wrap it as a URN. */\nexport function objectIdFor(id: string): string {\n return `urn:learning-kit:activity:${encodeURIComponent(id)}`;\n}\n\n/**\n * Random per-mount session id for the shuffle seed. `crypto.randomUUID` is\n * unavailable outside secure contexts (plain-http LAN/staging hosts), so a\n * Math.random fallback keeps the component from crashing there — the seed\n * only randomises presentation order, so cryptographic strength is not needed.\n */\nexport function randomSessionId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `s-${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * The `correct` flag of a detail stored before lk-core 0.3, which carries it in\n * place of an `outcome`; `undefined` on any other detail. The type stopped\n * describing it in 1.0, but a grade of record stored then still has it. On a\n * blank, a gap or a word it meant the part was right; on a multiple-choice\n * option, that the learner acted rightly on it.\n */\nexport function legacyCorrect(detail: ScoringDetail): boolean | undefined {\n const { correct } = detail as { correct?: unknown };\n return typeof correct === 'boolean' ? correct : undefined;\n}\n\n/**\n * Whether a detail marks its blank, gap or word right: its `outcome`, or on a\n * detail stored before lk-core 0.3, its `correct` flag.\n */\nexport function detailIsRight(detail: ScoringDetail): boolean {\n const outcome: unknown = detail.outcome;\n return outcome === undefined ? legacyCorrect(detail) === true : outcome === 'correct';\n}\n","/**\n * When AI help is offered, and what the SDK does with what a port answers.\n *\n * Internal: these are the rules behind {@link useAiHints} and\n * {@link useAiExplanation}, which are what a host calls. They live apart from\n * the hooks so the hooks' module exports nothing but the hooks themselves.\n */\nimport {\n type AiActivityInput,\n type AiRefusal,\n aiAllowedByContent,\n aiSupports,\n type ItemOutcome,\n type LearnerResponse,\n OPEN_DELIVERY_POLICY,\n type ResolvedDeliveryPolicy,\n} from '@intellectif/lk-core';\nimport { isDevelopment } from '../components/_internal.js';\nimport type { RenderableActivity, RenderMode } from '../components/types.js';\nimport type { LearnerAi } from './LkAiProvider.js';\n\n/** The most hints a question gives when the host names no number, and the most it may name. */\nconst DEFAULT_MAX_HINTS = 3;\nconst MAX_HINTS_CEILING = 10;\n\n/** A host's limit as a whole number from 1 to 10; anything else is the default, 3. */\nfunction limitOf(asked: unknown): number {\n return typeof asked === 'number' && Number.isInteger(asked) && asked >= 1\n ? Math.min(asked, MAX_HINTS_CEILING)\n : DEFAULT_MAX_HINTS;\n}\n\n/** `maxHints` as a whole number from 1 to 10; anything else is the default. */\nexport function hintLimit(ai: LearnerAi | undefined): number {\n return limitOf(ai?.maxHints);\n}\n\n/** `maxWritingFeedback` as a whole number from 1 to 10; anything else is the default. */\nexport function writingFeedbackLimit(ai: LearnerAi | undefined): number {\n return limitOf(ai?.maxWritingFeedback);\n}\n\n/**\n * Whether the learner may ask for feedback on a draft: `practice`, before\n * submit, on a written response that is not disabled or redacted, whose author\n * left explanations on. The paper's policy must show feedback and solutions —\n * a correction is the right form beside a wrong one — and allow AI\n * explanations, which writing feedback is: a model's words about the\n * learner's own answer.\n */\nexport function writingFeedbackOffered(input: {\n data: RenderableActivity;\n renderMode: RenderMode;\n submitted: boolean;\n disabled: boolean;\n delivery?: ResolvedDeliveryPolicy;\n}): boolean {\n const { data, renderMode, submitted, disabled } = input;\n const delivery = input.delivery ?? OPEN_DELIVERY_POLICY;\n return (\n delivery.feedback &&\n delivery.solutions &&\n delivery.ai.explanations &&\n renderMode === 'practice' &&\n !submitted &&\n !disabled &&\n data.redacted !== true &&\n aiSupports(data.type, 'writing-feedback') &&\n aiAllowedByContent(data, 'writing-feedback')\n );\n}\n\n/**\n * Whether the learner may ask for a hint: `practice`, before submit, on a\n * question that is not disabled, of a type that takes hints, whose author left\n * hints on, and whole — a redacted item is an exam's, and gives none.\n */\nexport function hintOffered(input: {\n data: RenderableActivity;\n renderMode: RenderMode;\n submitted: boolean;\n disabled: boolean;\n /** The paper's policy: hints at all, and AI hints in particular. Open when absent. */\n delivery?: ResolvedDeliveryPolicy;\n}): boolean {\n const { data, renderMode, submitted, disabled } = input;\n const delivery = input.delivery ?? OPEN_DELIVERY_POLICY;\n return (\n delivery.hints &&\n delivery.ai.hints &&\n renderMode === 'practice' &&\n !submitted &&\n !disabled &&\n data.redacted !== true &&\n aiSupports(data.type, 'hint') &&\n aiAllowedByContent(data, 'hint')\n );\n}\n\n/**\n * Whether the learner may ask for an explanation: of a graded answer — in\n * `practice` once submitted, or in `review` when the grade of record is\n * scored — of a type the SDK explains, whose author left explanations on.\n * Never in `exam`.\n */\nexport function explanationOffered(input: {\n data: RenderableActivity;\n renderMode: RenderMode;\n submitted: boolean;\n outcome: ItemOutcome | undefined;\n /**\n * The paper's policy. An explanation needs AI explanations on, and more: it\n * explains a grade, so it needs the grade shown (`feedback`), and it all but\n * always says what the right answer was, so it needs `solutions` too. On a\n * paper that hides the answer, \"Explain my answer\" would hand it over.\n */\n delivery?: ResolvedDeliveryPolicy;\n}): boolean {\n const { data, renderMode, submitted, outcome } = input;\n const delivery = input.delivery ?? OPEN_DELIVERY_POLICY;\n if (!delivery.feedback || !delivery.solutions || !delivery.ai.explanations) {\n return false;\n }\n const graded =\n (renderMode === 'practice' && submitted) ||\n (renderMode === 'review' && outcome?.status === 'scored');\n return graded && aiSupports(data.type, 'explanation') && aiAllowedByContent(data, 'explanation');\n}\n\n/**\n * Whether the learner may ask for coaching on a reading: one whose marks are\n * on screen — which a read-aloud has only once it is graded, in `practice` or\n * in `review` — never in `exam`, on a read-aloud whose author left\n * explanations on. The paper's policy must show feedback, since the marks are\n * feedback, and allow AI explanations, which coaching is: a model's words\n * about the learner's own answer. Not `solutions`: a read-aloud hides no\n * answer, and its text is on screen throughout.\n */\nexport function coachingOffered(input: {\n data: AiActivityInput;\n renderMode: RenderMode;\n /** Whether there are marks to coach: a request could be built. */\n marked: boolean;\n delivery?: ResolvedDeliveryPolicy;\n}): boolean {\n const { data, renderMode, marked } = input;\n const delivery = input.delivery ?? OPEN_DELIVERY_POLICY;\n return (\n delivery.feedback &&\n delivery.ai.explanations &&\n renderMode !== 'exam' &&\n marked &&\n aiSupports(data.type, 'pronunciation-coaching') &&\n aiAllowedByContent(data, 'pronunciation-coaching')\n );\n}\n\n/** Calls a port, turning a synchronous throw into a rejection. */\nexport function callPort<T>(call: () => Promise<T>): Promise<T> {\n try {\n return Promise.resolve(call());\n } catch (error) {\n return Promise.reject(error);\n }\n}\n\n/** Tells a developer why a port's answer was not shown. Silent in production. */\nexport function warnRefused(\n feature: 'explanation' | 'hint' | 'writing feedback' | 'pronunciation coaching',\n activityId: string,\n reason: AiRefusal,\n): void {\n if (isDevelopment()) {\n console.warn(\n `learning-kit: the AI ${feature} for \"${activityId}\" was not shown (${reason}). The learner saw \"not available\" instead.`,\n );\n }\n}\n\n/** The language to write in: the host's choice for the learner, else the interface language. */\nexport const localeOf = (\n ai: LearnerAi | undefined,\n locale: string | undefined,\n): { learnerLocale?: string } => {\n const chosen = ai?.learnerLocale ?? locale;\n return chosen === undefined ? {} : { learnerLocale: chosen };\n};\n\n/** What an explanation is about: when any of it changes, the explanation shown no longer is. */\nexport function explanationKey(\n renderMode: RenderMode,\n submitted: boolean,\n response: LearnerResponse,\n outcome: ItemOutcome | undefined,\n): string {\n let graded = '';\n if (outcome !== undefined) {\n graded = `${outcome.status}:${'score' in outcome ? outcome.score : ''}`;\n }\n return `${renderMode}|${submitted}|${graded}|${JSON.stringify(response)}`;\n}\n","'use client';\n\nimport {\n type AiCoaching,\n type AiTextResult,\n type AiWritingFeedback,\n aiCoachingRequest,\n aiExplanationRequest,\n aiHintRequest,\n aiWritingFeedbackRequest,\n checkAiCoaching,\n checkAiExplanation,\n checkAiHint,\n checkAiWritingFeedback,\n combineDeliveryPolicies,\n type DeliveryPolicy,\n type GradeRecord,\n type InteractionEvent,\n type ItemOutcome,\n type LearnerResponse,\n type ReadAloudData,\n type SpeechAssessment,\n} from '@intellectif/lk-core';\nimport { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';\nimport { SequenceSlotContext } from '../components/shared/sequence-slot.js';\nimport type { RenderableActivity, RenderMode } from '../components/types.js';\nimport { type LearnerAi, useLearnerAi } from './LkAiProvider.js';\nimport {\n callPort,\n coachingOffered,\n explanationKey,\n explanationOffered,\n hintLimit,\n hintOffered,\n localeOf,\n warnRefused,\n writingFeedbackLimit,\n writingFeedbackOffered,\n} from './rules.js';\n\n/**\n * The question AI help is being asked about, as the SDK's own components know\n * it. A host drawing its own question passes what it has; everything optional\n * defaults to what the components pass.\n */\nexport interface AiHelpSituation {\n /** The question, as `data` is given to any activity component. */\n data: RenderableActivity;\n /** The learner's answer as it stands: submitted, restored, or being written. */\n response: LearnerResponse;\n /** Whether the learner has committed that answer. */\n submitted: boolean;\n /**\n * Pass on the `renderMode` your question was given. Left out, the paper\n * around the question answers for it — a question inside `<ActivitySequence>`\n * or `<InteractiveVideo>` knows its mode — and `practice` only where there is\n * no paper at all. An `exam` around the question wins over anything passed\n * here.\n */\n renderMode?: RenderMode;\n /**\n * The AI ports to use, overriding {@link LkAiProvider}'s for this question.\n * A question inside `<ActivitySequence>` or `<InteractiveVideo>` is handed\n * the ports in force as `ai` — pass that straight in.\n */\n ai?: LearnerAi;\n /** The interface language: the default language for what a model writes. */\n locale?: string;\n /** Where `ai-hint-shown` and `ai-explanation-shown` go. */\n onInteraction?: (event: InteractionEvent) => void;\n /**\n * The delivery policy your question was handed. The paper around the\n * question holds as well — a set that switched AI hints off keeps them off\n * here, whatever is passed — so leaving it out never turns anything on.\n */\n delivery?: DeliveryPolicy | null;\n}\n\nexport interface AiExplanationInput extends AiHelpSituation {\n /** The grade of record, which `review` explains. */\n outcome?: ItemOutcome;\n}\n\nexport interface AiExplanationHelp {\n /**\n * Whether to offer the learner an explanation at all: a port is present, the\n * answer is graded, the type is one the SDK explains, and the author left\n * explanations on. False in `exam`, always.\n */\n offered: boolean;\n status: 'idle' | 'loading' | 'shown' | 'unavailable';\n /** What to show, once it has arrived and been checked. Its `text` is text, never HTML. */\n explanation: AiTextResult | null;\n /**\n * Asks the port, when the learner presses something. Does nothing while a\n * call is on its way, and nothing at all where `offered` is false — so an\n * exam cannot reach a model through this hook, whatever ports are in scope.\n *\n * One identity for the life of the question, so it is safe in an effect's\n * dependencies.\n */\n ask: () => void;\n}\n\nexport interface AiHintsInput extends AiHelpSituation {\n /** A disabled question gives no hints, as it takes no answer. */\n disabled?: boolean;\n}\n\nexport interface AiHintsHelp {\n /**\n * Whether the learner may ask for hints here at all: a port is present, the\n * mode is `practice`, the answer is not in, the question is not disabled or\n * redacted, the type takes hints and the author left them on. It stays true\n * once `used` reaches `limit` — there are no more to give, but the question\n * is still one that gives them.\n */\n offered: boolean;\n status: 'idle' | 'loading' | 'unavailable';\n /** Every hint shown for this question, in the order they were given. */\n hints: readonly AiTextResult[];\n /** The host's `maxHints`, as a whole number from 1 to 10 (3 by default). */\n limit: number;\n /** How many hints have been shown. A refused hint is not counted. */\n used: number;\n /**\n * Asks for the next hint. Does nothing at the limit, while a call is on its\n * way, or where `offered` is false. One identity for the life of the\n * question.\n */\n ask: () => void;\n}\n\n/**\n * The mode a question is answered in: what the caller says, and what the paper\n * around it says.\n *\n * A host drawing its own question is given `renderMode` like every other prop\n * and is expected to pass it on, but nothing makes it — and a hook that took\n * `practice` on trust would offer hints on a paper of record, as soon as an\n * `LkAiProvider` sat anywhere above the pager. So an `exam` around the question\n * wins, and where the caller says nothing the paper answers for it. A question\n * standing on its own belongs to no paper, and is `practice` as before.\n */\nfunction modeInForce(asked: RenderMode | undefined, around: RenderMode | undefined): RenderMode {\n if (around === 'exam') {\n return 'exam';\n }\n return asked ?? around ?? 'practice';\n}\n\n/**\n * What the record of help a learner was shown carries beside the fact of it:\n * who wrote it, and what the call cost — both only where the port sent them.\n *\n * The text is deliberately absent. A host that wants to keep what a learner\n * read has it in its own port; an interaction is a record of what happened, and\n * is kept for every learner and every question.\n */\nfunction shownPayload(result: Pick<AiTextResult, 'provenance' | 'usage'>): Record<string, unknown> {\n return {\n ...(result.provenance !== undefined ? { provenance: result.provenance } : {}),\n ...(result.usage !== undefined ? { usage: result.usage } : {}),\n };\n}\n\ntype ExplanationState =\n | { key: string; status: 'idle' | 'loading' | 'unavailable' }\n | { key: string; status: 'shown'; result: AiTextResult };\n\n/**\n * \"Explain my answer\", without the SDK's own button and panel: the rules, the\n * call, the checks and the record, for a question you draw yourself.\n *\n * It is what `<MultipleChoice>` and the others use, so a host's own question\n * offers help on exactly the same terms: never in `exam`, never where the\n * item's author switched explanations off, and never an explanation whose\n * verdict contradicts the grade the SDK computed.\n *\n * ```tsx\n * const { offered, status, explanation, ask } = useAiExplanation({\n * data, response, submitted, renderMode, outcome, ai: question.ai,\n * });\n * if (!offered) return null;\n * return explanation === null ? (\n * <button type=\"button\" onClick={ask} aria-busy={status === 'loading'}>Explain my answer</button>\n * ) : (\n * <section>\n * <p>{explanation.text}</p>\n * <p>Written by AI. It can make mistakes.</p>\n * </section>\n * );\n * ```\n *\n * **Say who wrote it.** The SDK's own panel carries \"Written by AI. It can make\n * mistakes.\"; a page that drops that line passes a model's words off as the\n * course's.\n *\n * When the answer or its grade changes, the explanation is dropped and a call\n * still on its way is abandoned — a late explanation never lands beside an\n * answer it was not written for — as it is when the caller unmounts.\n */\nexport function useAiExplanation(input: AiExplanationInput): AiExplanationHelp {\n const { data, response, submitted, outcome } = input;\n const around = useContext(SequenceSlotContext);\n const renderMode = modeInForce(input.renderMode, around?.renderMode);\n const delivery = combineDeliveryPolicies(input.delivery, around?.delivery);\n const ai = useLearnerAi(input.ai);\n const resetKey = explanationKey(renderMode, submitted, response, outcome);\n const [state, setState] = useState<ExplanationState>({ key: resetKey, status: 'idle' });\n const run = useRef(0);\n const controller = useRef<AbortController | null>(null);\n const current: ExplanationState =\n state.key === resetKey ? state : { key: resetKey, status: 'idle' };\n const offered =\n ai?.explain !== undefined &&\n explanationOffered({ data, renderMode, submitted, outcome, delivery });\n\n // What `ask` reads, as of this render, so `ask` itself can keep one identity\n // for the life of the question. The state goes in too: an event handler runs\n // after the render that set it, so the ref is what the learner last saw.\n const latest = useRef({ input, renderMode, ai, resetKey, offered, current });\n latest.current = { input, renderMode, ai, resetKey, offered, current };\n\n // Another answer, or the caller going away: abandon the call in flight.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the key is the trigger — its cleanup runs when the answer changes\n useEffect(() => {\n return () => {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n };\n }, [resetKey]);\n\n const ask = useCallback((): void => {\n const now = latest.current;\n const port = now.ai?.explain;\n if (port === undefined || !now.offered || now.current.status === 'loading') {\n return;\n }\n const key = now.resetKey;\n const request = aiExplanationRequest({\n data: now.input.data,\n response: now.input.response,\n ...(now.renderMode === 'review' && now.input.outcome !== undefined\n ? { outcome: now.input.outcome }\n : {}),\n ...localeOf(now.ai, now.input.locale),\n });\n if (request === null) {\n setState({ key, status: 'unavailable' });\n return;\n }\n run.current += 1;\n const mine = run.current;\n controller.current?.abort();\n const abort = new AbortController();\n controller.current = abort;\n setState({ key, status: 'loading' });\n callPort(() => port(request, { signal: abort.signal })).then(\n (raw) => {\n if (run.current !== mine) {\n return;\n }\n const checked = checkAiExplanation(raw, request);\n if (!checked.ok) {\n warnRefused('explanation', now.input.data.id, checked.refusal);\n setState({ key, status: 'unavailable' });\n latest.current.input.onInteraction?.({\n type: 'ai-help-refused',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: { feature: 'explanation', reason: checked.refusal },\n });\n return;\n }\n setState({ key, status: 'shown', result: checked.result });\n latest.current.input.onInteraction?.({\n type: 'ai-explanation-shown',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: shownPayload(checked.result),\n });\n },\n () => {\n if (run.current === mine) {\n setState({ key, status: 'unavailable' });\n }\n },\n );\n }, []);\n\n return {\n offered,\n status: current.status,\n explanation: current.status === 'shown' ? current.result : null,\n ask,\n };\n}\n\ninterface HintState {\n key: string;\n hints: AiTextResult[];\n status: 'idle' | 'loading' | 'unavailable';\n}\n\n/**\n * \"Get a hint\", without the SDK's own button and list: the rules, the call, the\n * checks and the record, for a question you draw yourself.\n *\n * It is what `<MultipleChoice>` and the others use, so a host's own question\n * gives hints on exactly the same terms: only in `practice` before submit, up\n * to the host's limit, never on a redacted item or a type the SDK does not hint\n * for, and never a hint that gives the answer away — which is refused rather\n * than shown, and not counted.\n *\n * ```tsx\n * const { offered, status, hints, used, limit, ask } = useAiHints({\n * data, response, submitted, renderMode, ai: question.ai,\n * });\n * ```\n *\n * **Say who wrote them**, as the SDK's own list does: \"Written by AI. It can\n * make mistakes.\"\n *\n * Keep the hints on screen after submit — the learner can see what they were\n * given — while `offered` turns false. Another question starts with none.\n */\nexport function useAiHints(input: AiHintsInput): AiHintsHelp {\n const { data, submitted } = input;\n const around = useContext(SequenceSlotContext);\n const renderMode = modeInForce(input.renderMode, around?.renderMode);\n const delivery = combineDeliveryPolicies(input.delivery, around?.delivery);\n const disabled = input.disabled === true;\n const ai = useLearnerAi(input.ai);\n const resetKey = data.id;\n const [state, setState] = useState<HintState>({ key: resetKey, hints: [], status: 'idle' });\n const run = useRef(0);\n const controller = useRef<AbortController | null>(null);\n const current: HintState =\n state.key === resetKey ? state : { key: resetKey, hints: [], status: 'idle' };\n const offered =\n ai?.hint !== undefined && hintOffered({ data, renderMode, submitted, disabled, delivery });\n const limit = hintLimit(ai);\n\n const latest = useRef({ input, renderMode, ai, offered, limit, current });\n latest.current = { input, renderMode, ai, offered, limit, current };\n\n // Another question, or the caller going away: abandon the call in flight.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the key is the trigger — its cleanup runs when the question changes\n useEffect(() => {\n return () => {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n };\n }, [resetKey]);\n\n // Submitted, or disabled, while a hint was on its way: it would arrive for an\n // answer that can no longer use it.\n useEffect(() => {\n if (!offered) {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n setState((previous) =>\n previous.status === 'loading' ? { ...previous, status: 'idle' } : previous,\n );\n }\n }, [offered]);\n\n const ask = useCallback((): void => {\n const now = latest.current;\n const port = now.ai?.hint;\n const held = now.current;\n if (\n port === undefined ||\n !now.offered ||\n held.status === 'loading' ||\n held.hints.length >= now.limit\n ) {\n return;\n }\n const key = held.key;\n const request = aiHintRequest({\n data: now.input.data,\n response: now.input.response,\n previousHints: held.hints.map((hint) => hint.text),\n ...localeOf(now.ai, now.input.locale),\n });\n if (request === null) {\n setState({ ...held, status: 'unavailable' });\n return;\n }\n run.current += 1;\n const mine = run.current;\n controller.current?.abort();\n const abort = new AbortController();\n controller.current = abort;\n setState({ ...held, status: 'loading' });\n callPort(() => port(request, { signal: abort.signal })).then(\n (raw) => {\n if (run.current !== mine) {\n return;\n }\n const checked = checkAiHint(raw, request);\n if (!checked.ok) {\n warnRefused('hint', now.input.data.id, checked.refusal);\n setState((shown) => ({ ...shown, status: 'unavailable' }));\n latest.current.input.onInteraction?.({\n type: 'ai-help-refused',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: {\n feature: 'hint',\n reason: checked.refusal,\n hintNumber: request.hintNumber,\n },\n });\n return;\n }\n setState((shown) => ({\n ...shown,\n hints: shown.key === key ? [...shown.hints, checked.result] : shown.hints,\n status: 'idle',\n }));\n latest.current.input.onInteraction?.({\n type: 'ai-hint-shown',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: { hintNumber: request.hintNumber, ...shownPayload(checked.result) },\n });\n },\n () => {\n if (run.current === mine) {\n setState((shown) => ({ ...shown, status: 'unavailable' }));\n }\n },\n );\n }, []);\n\n return {\n offered,\n status: current.status,\n hints: current.hints,\n limit,\n used: current.hints.length,\n ask,\n };\n}\n\nexport interface AiWritingFeedbackInput extends AiHelpSituation {\n /** A disabled question takes no feedback, as it takes no answer. */\n disabled?: boolean;\n}\n\nexport interface AiWritingFeedbackHelp {\n /**\n * Whether the learner may ask for feedback here at all: a port is present,\n * the mode is `practice`, the answer is not in, the question is a written\n * response that is not disabled or redacted, its author left explanations on,\n * and the paper shows feedback and solutions and allows AI explanations. It\n * stays true once `used` reaches `limit`.\n */\n offered: boolean;\n status: 'idle' | 'loading' | 'unavailable';\n /** Every piece of feedback shown for this question, oldest first. */\n feedback: readonly AiWritingFeedback[];\n /** The latest, or `null` before any. Its `text` is text, never HTML. */\n latest: AiWritingFeedback | null;\n /**\n * Whether `latest` is about the draft as it stands. Once the learner revises,\n * its corrections point at words that may no longer be there: say so.\n */\n current: boolean;\n /** The host's `maxWritingFeedback`, as a whole number from 1 to 10 (3 by default). */\n limit: number;\n /** How many times feedback has been shown. A refused reply is not counted. */\n used: number;\n /**\n * Asks for feedback on the draft as it stands. Does nothing at the limit,\n * while a call is on its way, on an empty draft, or where `offered` is false.\n * One identity for the life of the question.\n */\n ask: () => void;\n}\n\ninterface WritingFeedbackState {\n key: string;\n shown: { feedback: AiWritingFeedback; about: string }[];\n status: 'idle' | 'loading' | 'unavailable';\n}\n\n/** The text of a written response; another type's response is no draft at all. */\nconst draftOf = (response: LearnerResponse): string =>\n response.type === 'written-response' ? response.text : '';\n\n/**\n * \"Get feedback on my draft\", without the SDK's own button and panel: the\n * rules, the call, the checks and the record, for a written response you draw\n * yourself.\n *\n * It is what `<WrittenResponse>` uses, so a host's own essay box gets feedback\n * on the same terms: only in `practice` before submit, up to the host's limit,\n * and never feedback that corrects words the learner did not write — which is\n * refused rather than shown, and not counted.\n *\n * ```tsx\n * const { offered, status, latest, current, used, limit, ask } = useAiWritingFeedback({\n * data, response, submitted, renderMode, ai: question.ai,\n * });\n * ```\n *\n * **Say who wrote it**, as the SDK's own panel does: \"Written by AI. It can make\n * mistakes.\" And show `indicativeScore` as what it is — an indication, not a\n * grade.\n */\nexport function useAiWritingFeedback(input: AiWritingFeedbackInput): AiWritingFeedbackHelp {\n const { data, submitted, response } = input;\n const around = useContext(SequenceSlotContext);\n const renderMode = modeInForce(input.renderMode, around?.renderMode);\n const delivery = combineDeliveryPolicies(input.delivery, around?.delivery);\n const disabled = input.disabled === true;\n const ai = useLearnerAi(input.ai);\n const resetKey = data.id;\n const [state, setState] = useState<WritingFeedbackState>({\n key: resetKey,\n shown: [],\n status: 'idle',\n });\n const run = useRef(0);\n const controller = useRef<AbortController | null>(null);\n const current: WritingFeedbackState =\n state.key === resetKey ? state : { key: resetKey, shown: [], status: 'idle' };\n const offered =\n ai?.writingFeedback !== undefined &&\n writingFeedbackOffered({ data, renderMode, submitted, disabled, delivery });\n const limit = writingFeedbackLimit(ai);\n\n const latest = useRef({ input, ai, offered, limit, current });\n latest.current = { input, ai, offered, limit, current };\n\n // Another question, or the caller going away: abandon the call in flight.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the key is the trigger — its cleanup runs when the question changes\n useEffect(() => {\n return () => {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n };\n }, [resetKey]);\n\n // Submitted, or disabled, while feedback was on its way: it would arrive for\n // a draft that can no longer be revised.\n useEffect(() => {\n if (!offered) {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n setState((previous) =>\n previous.status === 'loading' ? { ...previous, status: 'idle' } : previous,\n );\n }\n }, [offered]);\n\n const ask = useCallback((): void => {\n const now = latest.current;\n const port = now.ai?.writingFeedback;\n const held = now.current;\n if (\n port === undefined ||\n !now.offered ||\n held.status === 'loading' ||\n held.shown.length >= now.limit\n ) {\n return;\n }\n const key = held.key;\n const about = draftOf(now.input.response);\n const request = aiWritingFeedbackRequest({\n data: now.input.data,\n response: now.input.response,\n previousFeedback: held.shown.map((one) => one.feedback.text),\n ...localeOf(now.ai, now.input.locale),\n });\n if (request === null) {\n // An empty draft: nothing to give feedback on, and nothing went wrong.\n return;\n }\n run.current += 1;\n const mine = run.current;\n controller.current?.abort();\n const abort = new AbortController();\n controller.current = abort;\n setState({ ...held, status: 'loading' });\n callPort(() => port(request, { signal: abort.signal })).then(\n (raw) => {\n if (run.current !== mine) {\n return;\n }\n const checked = checkAiWritingFeedback(raw, request);\n if (!checked.ok) {\n warnRefused('writing feedback', now.input.data.id, checked.refusal);\n setState((was) => ({ ...was, status: 'unavailable' }));\n latest.current.input.onInteraction?.({\n type: 'ai-help-refused',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: {\n feature: 'writing-feedback',\n reason: checked.refusal,\n draftNumber: request.draftNumber,\n },\n });\n return;\n }\n const { feedback } = checked;\n setState((was) => ({\n ...was,\n shown: was.key === key ? [...was.shown, { feedback, about }] : was.shown,\n status: 'idle',\n }));\n latest.current.input.onInteraction?.({\n type: 'ai-writing-feedback-shown',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: {\n draftNumber: request.draftNumber,\n corrections: feedback.corrections.length,\n ...(feedback.indicativeScore !== null\n ? { indicativeScore: feedback.indicativeScore }\n : {}),\n ...shownPayload(feedback),\n },\n });\n },\n () => {\n if (run.current === mine) {\n setState((was) => ({ ...was, status: 'unavailable' }));\n }\n },\n );\n }, []);\n\n const last = current.shown[current.shown.length - 1];\n return {\n offered,\n status: current.status,\n feedback: current.shown.map((one) => one.feedback),\n latest: last?.feedback ?? null,\n current: last !== undefined && last.about === draftOf(response),\n limit,\n used: current.shown.length,\n ask,\n };\n}\n\nexport interface AiCoachingInput {\n /**\n * The read-aloud item, as `data` is given to `<ReadAloud>` — or as much of\n * one as coaching reads: its id, title, text and language, and the author's\n * `instructions` and `ai` settings where it has them.\n */\n data: Pick<ReadAloudData, 'id' | 'title' | 'referenceText' | 'locale'> &\n Partial<Pick<ReadAloudData, 'instructions' | 'ai'>>;\n /**\n * The speech engine's assessment of the take, as the learner is shown it.\n * When given, it is the marks — each word's, with its sounds and what each\n * was heard as — and the only ones: see `aiCoachingRequest` in lk-core.\n */\n assessment?: SpeechAssessment | null;\n /**\n * The grade of record. Its score is context for the model; without an\n * assessment, its word details are the marks, and they carry no sounds.\n */\n grade?: GradeRecord | null;\n /**\n * Pass on the `renderMode` your reading was given. Coaching is never offered\n * in `exam`, and an `exam` around the reading wins over anything passed here.\n */\n renderMode?: RenderMode;\n /** The AI ports to use, overriding {@link LkAiProvider}'s for this reading. */\n ai?: LearnerAi;\n /** The interface language: the default language for what a model writes. */\n locale?: string;\n /** Where `ai-coaching-shown` and `ai-help-refused` go. */\n onInteraction?: (event: InteractionEvent) => void;\n /**\n * The delivery policy your reading was handed. Coaching needs feedback shown\n * and AI explanations allowed; the paper around the reading holds as well.\n */\n delivery?: DeliveryPolicy | null;\n}\n\nexport interface AiCoachingHelp {\n /**\n * Whether to offer coaching at all: a `pronunciationCoaching` port is\n * present, there are marks to coach, the mode is not `exam`, the item's\n * author left explanations on, and the paper shows feedback and allows AI\n * explanations.\n */\n offered: boolean;\n status: 'idle' | 'loading' | 'shown' | 'unavailable';\n /**\n * What to show, once it has arrived and been checked: the coaching's text,\n * and the words it works on in reading order, each with its tip and, where\n * the engine reported one, the sound. All of it is text, never HTML.\n */\n coaching: AiCoaching | null;\n /**\n * Asks the port, when the learner presses something. Does nothing while a\n * call is on its way, once coaching is shown — one per reading — or where\n * `offered` is false. One identity for the life of the reading.\n */\n ask: () => void;\n}\n\ntype CoachingState =\n | { key: string; status: 'idle' | 'loading' | 'unavailable' }\n | { key: string; status: 'shown'; coaching: AiCoaching };\n\n/**\n * \"Coach me on this reading\", without the SDK's own button and panel: the\n * rules, the call, the checks and the record, for marks you draw yourself.\n *\n * It is what `<PronunciationFeedback>` and `<ReadAloud>` use, so a host's own\n * marks get coaching on the same terms: never in `exam`, never where the\n * item's author switched explanations off, and never coaching on a word the\n * engine did not mark or a sound it did not report — which is refused whole.\n *\n * ```tsx\n * const { offered, status, coaching, ask } = useAiCoaching({\n * data: item, assessment, grade, renderMode, ai,\n * });\n * ```\n *\n * **Say who wrote it**, as the SDK's own panel does: \"Written by AI. It can make\n * mistakes.\" The marks beside it are the engine's; the coaching is a model's.\n *\n * Another reading — other marks, another text — drops the coaching and\n * abandons a call still on its way, as the caller unmounting does.\n */\nexport function useAiCoaching(input: AiCoachingInput): AiCoachingHelp {\n const { data, assessment, grade } = input;\n const around = useContext(SequenceSlotContext);\n const renderMode = modeInForce(input.renderMode, around?.renderMode);\n const delivery = combineDeliveryPolicies(input.delivery, around?.delivery);\n const ai = useLearnerAi(input.ai);\n const { learnerLocale } = localeOf(ai, input.locale);\n const { id, title, referenceText, locale: itemLocale, instructions, ai: permissions } = data;\n\n // Built as the learner sees the marks, and rebuilt only when they change: the\n // fields, not the object, so a caller that spreads a fresh `data` on every\n // render does not realign the reading on every render.\n const request = useMemo(() => {\n const item = {\n type: 'read-aloud',\n id,\n title,\n referenceText,\n locale: itemLocale,\n instructions,\n ai: permissions,\n };\n return aiCoachingRequest({\n data: item,\n ...(assessment !== undefined && assessment !== null ? { assessment } : {}),\n ...(grade !== undefined && grade !== null ? { grade } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n });\n }, [\n id,\n title,\n referenceText,\n itemLocale,\n instructions,\n permissions,\n assessment,\n grade,\n learnerLocale,\n ]);\n // The reading the coaching is about: the take, and its marks. Another take is\n // another reading even where the engine marked it the same, and coaching\n // still on its way for the last one must not land on it. By content, not by\n // object, so a caller that rebuilds the same assessment keeps its coaching.\n const take = typeof assessment?.recordingKey === 'string' ? assessment.recordingKey : null;\n const resetKey = useMemo(\n () => (request === null ? '' : JSON.stringify([take, request])),\n [take, request],\n );\n const [state, setState] = useState<CoachingState>({ key: resetKey, status: 'idle' });\n const run = useRef(0);\n const controller = useRef<AbortController | null>(null);\n const current: CoachingState = state.key === resetKey ? state : { key: resetKey, status: 'idle' };\n const offered =\n ai?.pronunciationCoaching !== undefined &&\n coachingOffered({\n data: { type: 'read-aloud', id, title, ai: permissions },\n renderMode,\n marked: request !== null,\n delivery,\n });\n\n const latest = useRef({ input, ai, request, resetKey, offered, current });\n latest.current = { input, ai, request, resetKey, offered, current };\n\n // Another reading, or the caller going away: abandon the call in flight.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the key is the trigger — its cleanup runs when the marks change\n useEffect(() => {\n return () => {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n };\n }, [resetKey]);\n\n // No longer offered — the paper switched AI off, or the reading is now an\n // exam's — while coaching was on its way: it would land where none is allowed.\n useEffect(() => {\n if (!offered) {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n setState((previous) =>\n previous.status === 'loading' ? { key: previous.key, status: 'idle' } : previous,\n );\n }\n }, [offered]);\n\n const ask = useCallback((): void => {\n const now = latest.current;\n const port = now.ai?.pronunciationCoaching;\n const request = now.request;\n if (\n port === undefined ||\n !now.offered ||\n request === null ||\n now.current.status === 'loading' ||\n now.current.status === 'shown'\n ) {\n return;\n }\n const key = now.resetKey;\n const activityId = request.facts.activityId;\n run.current += 1;\n const mine = run.current;\n controller.current?.abort();\n const abort = new AbortController();\n controller.current = abort;\n setState({ key, status: 'loading' });\n callPort(() => port(request, { signal: abort.signal })).then(\n (raw) => {\n if (run.current !== mine) {\n return;\n }\n const checked = checkAiCoaching(raw, request);\n if (!checked.ok) {\n warnRefused('pronunciation coaching', activityId, checked.refusal);\n setState({ key, status: 'unavailable' });\n latest.current.input.onInteraction?.({\n type: 'ai-help-refused',\n activityId,\n timestamp: Date.now(),\n payload: { feature: 'pronunciation-coaching', reason: checked.refusal },\n });\n return;\n }\n const { coaching } = checked;\n setState({ key, status: 'shown', coaching });\n latest.current.input.onInteraction?.({\n type: 'ai-coaching-shown',\n activityId,\n timestamp: Date.now(),\n payload: { words: coaching.words.length, ...shownPayload(coaching) },\n });\n },\n () => {\n if (run.current === mine) {\n setState({ key, status: 'unavailable' });\n }\n },\n );\n }, []);\n\n return {\n offered,\n status: offered ? current.status : 'idle',\n coaching: offered && current.status === 'shown' ? current.coaching : null,\n ask,\n };\n}\n"]}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var chunk54DX6SLS_cjs = require('./chunk-54DX6SLS.cjs');
|
|
9
|
-
var chunkUKUHVCZX_cjs = require('./chunk-UKUHVCZX.cjs');
|
|
4
|
+
var chunk2PNSVBLQ_cjs = require('./chunk-2PNSVBLQ.cjs');
|
|
5
|
+
var chunkJ3C72RPK_cjs = require('./chunk-J3C72RPK.cjs');
|
|
6
|
+
var chunkLPUWXIPR_cjs = require('./chunk-LPUWXIPR.cjs');
|
|
7
|
+
var chunkNDBMKGQ5_cjs = require('./chunk-NDBMKGQ5.cjs');
|
|
10
8
|
var chunkXOR4MPEN_cjs = require('./chunk-XOR4MPEN.cjs');
|
|
11
|
-
var
|
|
9
|
+
var chunkBIGGXHAW_cjs = require('./chunk-BIGGXHAW.cjs');
|
|
10
|
+
var chunkTUVENMXB_cjs = require('./chunk-TUVENMXB.cjs');
|
|
12
11
|
var lkCore = require('@intellectif/lk-core');
|
|
13
12
|
var react = require('react');
|
|
14
13
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -172,11 +171,11 @@ function WrittenResponse({
|
|
|
172
171
|
delivery,
|
|
173
172
|
ai: aiProp
|
|
174
173
|
}) {
|
|
175
|
-
const s =
|
|
176
|
-
const policy =
|
|
177
|
-
const ai =
|
|
174
|
+
const s = chunkBIGGXHAW_cjs.useLkStrings(strings);
|
|
175
|
+
const policy = chunk2PNSVBLQ_cjs.useDeliveryPolicy(delivery);
|
|
176
|
+
const ai = chunkTUVENMXB_cjs.useLearnerAi(aiProp);
|
|
178
177
|
const devError = react.useMemo(() => {
|
|
179
|
-
if (!
|
|
178
|
+
if (!chunkLPUWXIPR_cjs.isDevelopment()) {
|
|
180
179
|
return null;
|
|
181
180
|
}
|
|
182
181
|
const result = lkCore.validateActivity("written-response", data);
|
|
@@ -213,7 +212,7 @@ function WrittenResponse({
|
|
|
213
212
|
reset(defaultSubmittedRef.current === true ? "completed" : "idle");
|
|
214
213
|
}, [data, reset]);
|
|
215
214
|
const draft = isControlled ? textOf(value) : innerText;
|
|
216
|
-
const writingFeedback =
|
|
215
|
+
const writingFeedback = chunkLPUWXIPR_cjs.useAiWritingFeedback({
|
|
217
216
|
data,
|
|
218
217
|
response: { type: "written-response", text: draft, wordCount: lkCore.countWords(draft) },
|
|
219
218
|
submitted: state === "completed",
|
|
@@ -278,9 +277,9 @@ function WrittenResponse({
|
|
|
278
277
|
complete();
|
|
279
278
|
const timeSpent = getTimeSpent();
|
|
280
279
|
const xapiStatement = lkCore.xAPIBuilder.buildSubmittedStatement({
|
|
281
|
-
actor:
|
|
280
|
+
actor: chunkLPUWXIPR_cjs.ANONYMOUS_ACTOR,
|
|
282
281
|
object: {
|
|
283
|
-
id:
|
|
282
|
+
id: chunkLPUWXIPR_cjs.objectIdFor(data.id),
|
|
284
283
|
name: { [data.locale ?? "en-US"]: data.title },
|
|
285
284
|
type: "http://adlnet.gov/expapi/activities/cmi.interaction",
|
|
286
285
|
interactionType: "long-fill-in"
|
|
@@ -322,7 +321,7 @@ function WrittenResponse({
|
|
|
322
321
|
onSubmit: handleSubmit,
|
|
323
322
|
children: [
|
|
324
323
|
data.media ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
325
|
-
|
|
324
|
+
chunkNDBMKGQ5_cjs.ActivityMedia,
|
|
326
325
|
{
|
|
327
326
|
media: data.media,
|
|
328
327
|
renderMode,
|
|
@@ -382,16 +381,16 @@ function WrittenResponse({
|
|
|
382
381
|
children: s.submit
|
|
383
382
|
}
|
|
384
383
|
),
|
|
385
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
384
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunk2PNSVBLQ_cjs.FeedbackRegion, { id: `${data.id}-feedback`, children: isReview ? outcome && (policy.feedback || !chunk2PNSVBLQ_cjs.outcomeShowsMarks(outcome)) ? /* @__PURE__ */ jsxRuntime.jsx(OutcomeSummary, { outcome, s }) : null : summary })
|
|
386
385
|
]
|
|
387
386
|
}
|
|
388
387
|
);
|
|
389
388
|
}
|
|
390
389
|
function WrittenResponse2(props) {
|
|
391
|
-
const strings =
|
|
392
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
390
|
+
const strings = chunkBIGGXHAW_cjs.useLkStrings(props.strings);
|
|
391
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkJ3C72RPK_cjs.ActivityErrorBoundary, { activityTitle: props.data?.title, strings, children: /* @__PURE__ */ jsxRuntime.jsx(WrittenResponse, { ...props }) });
|
|
393
392
|
}
|
|
394
393
|
|
|
395
394
|
exports.WrittenResponse = WrittenResponse2;
|
|
396
|
-
//# sourceMappingURL=chunk-
|
|
397
|
-
//# sourceMappingURL=chunk-
|
|
395
|
+
//# sourceMappingURL=chunk-MKXEM2C5.cjs.map
|
|
396
|
+
//# sourceMappingURL=chunk-MKXEM2C5.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/WrittenResponse/WrittenResponse.tsx","../src/components/WrittenResponse/index.tsx"],"names":["jsxs","jsx","Fragment","useRef","useEffect","useLkStrings","useDeliveryPolicy","useLearnerAi","useMemo","isDevelopment","validateActivity","ActivitySchemaError","useActivityState","useState","useAiWritingFeedback","countWords","evaluate","xAPIBuilder","ANONYMOUS_ACTOR","objectIdFor","ActivityMedia","FeedbackRegion","outcomeShowsMarks","WrittenResponse","ActivityErrorBoundary"],"mappings":"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","file":"chunk-MKXEM2C5.cjs","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n type CriterionScore,\n countWords,\n type DeliveryPolicy,\n evaluate,\n type GradeRecord,\n type InlineCorrection,\n type InteractionEvent,\n type ItemOutcome,\n type LearnerResponse,\n type ThemeTokens,\n validateActivity,\n type WrittenResponseData,\n type XAPIStatement,\n xAPIBuilder,\n} from '@intellectif/lk-core';\nimport { type CSSProperties, useEffect, useMemo, useRef, useState } from 'react';\nimport { type LearnerAi, useLearnerAi } from '../../ai/LkAiProvider.js';\nimport { type AiWritingFeedbackHelp, useAiWritingFeedback } from '../../ai/useAiHelp.js';\nimport { useActivityState } from '../../hooks/useActivityState.js';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport type { LkStrings, LkStringsOverride } from '../../i18n/strings.js';\nimport { ANONYMOUS_ACTOR, isDevelopment, objectIdFor } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { outcomeShowsMarks, useDeliveryPolicy } from '../shared/delivery.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport type {\n HtmlSanitizer,\n MediaBudgetBinding,\n MediaTransportStrings,\n Renderable,\n RenderMode,\n} from '../types.js';\n\n/**\n * Payload delivered when the learner submits a written response. Grading is\n * DEFERRED (asynchronous AI/human grading), so there is no score here — and\n * deliberately no fake one: an ungraded submission must never be shown or\n * stored as a 0. The consumer persists `text` and grades it out-of-band.\n */\nexport interface WrittenResponseSubmission {\n /** The submitted text, verbatim. */\n text: string;\n /** Word count of `text`, recomputed with the canonical `countWords()`. */\n wordCount: number;\n /** Whether `wordCount` falls within the activity's `[minWords, maxWords]`. */\n withinWordBounds: boolean;\n /** Time in milliseconds from first interaction to submission. */\n timeSpent: number;\n /** The SUBMITTED-verb xAPI statement built for this submission (no score fields). */\n xapiStatement: XAPIStatement;\n}\n\n/**\n * Props for {@link WrittenResponse}. Mirrors `ActivityProps` except for the\n * completion callback: a deferred-grading activity completes with a\n * {@link WrittenResponseSubmission} (`onSubmitted`), not a scored\n * `ActivityResult` (`onComplete`) — deviation from Req 22.8 recorded in the\n * roadmap (§3.1): fabricating a score of 0 for ungraded work is the exact\n * bug this type exists to fix. Everything else — controlled value, render\n * mode, server outcome, rich text — follows the shared `ActivityProps`\n * contract verbatim.\n */\nexport interface WrittenResponseProps {\n /** Activity content. Accepts a `redact()` projection in `exam` mode. */\n data: Renderable<WrittenResponseData>;\n /**\n * Called on submit with the ungraded submission (text, recomputed word\n * count, bounds flag, timing, xAPI statement). Fires in `practice` and\n * `exam` mode, after `onSubmit`; never in `review`.\n *\n * Optional since lk-react 2.1.0 so a `review`-mode render (which has no submit\n * control) and an exam runner that only wants the raw response through\n * `onSubmit` do not have to pass a no-op. Supply it in `practice`/`exam`\n * unless `onSubmit` already captures everything you persist.\n */\n onSubmitted?: (submission: WrittenResponseSubmission) => void;\n /**\n * Called on submit with the raw learner response and no grade, BEFORE\n * `onSubmitted`. This is the only submit callback an exam runner needs:\n * the server grades the text and returns an {@link ItemOutcome} later.\n */\n onSubmit?: (response: LearnerResponse) => void;\n /**\n * Controlled value: the learner's current response. When present the\n * textarea renders from it (never from internal state) and every keystroke\n * goes out through `onChange` — restore an in-progress attempt, autosave a\n * delta, or drive a review render with it.\n */\n value?: LearnerResponse;\n /** Initial response for an uncontrolled component (ignored when `value` is set). */\n defaultValue?: LearnerResponse;\n /**\n * Mount as already submitted — read at mount only. Without it a resumed\n * attempt reopens an essay the learner had already submitted as editable\n * and re-submittable.\n */\n defaultSubmitted?: boolean;\n /** Fires on every change to the learner's response. Required for a controlled component. */\n onChange?: (response: LearnerResponse) => void;\n /**\n * Presentation mode, default `practice`. `practice` and `exam` are nearly\n * identical here — a written response is never graded client-side, so\n * there is no local score to withhold; `exam` additionally never calls\n * `evaluate()` and renders happily from a `redact()` projection.\n * `review` is read-only and shows the server's `outcome`.\n */\n renderMode?: RenderMode;\n /**\n * Server-computed outcome, rendered by `review` mode: `deferred` shows a\n * \"not graded yet\" affordance, `scored` shows the returned grade and\n * feedback. Ignored in other modes — this component never manufactures an\n * outcome of its own.\n */\n outcome?: ItemOutcome;\n /** Renders `data.promptHtml` when provided. See `HtmlSanitizer`. */\n sanitizeHtml?: HtmlSanitizer;\n /** Binds this activity's own `data.media` to a play budget. See {@link MediaBudgetBinding}. */\n mediaBudget?: MediaBudgetBinding;\n /** Translations for the audio transport chrome. See {@link MediaTransportStrings}. */\n mediaStrings?: Partial<MediaTransportStrings>;\n /** Overrides the SDK's chrome text for this activity. See {@link LkIntlProvider}. */\n strings?: LkStringsOverride;\n onInteraction?: (event: InteractionEvent) => void;\n /** Per-instance token overrides, applied as inline CSS vars on the root. */\n theme?: Partial<ThemeTokens>;\n locale?: string;\n disabled?: boolean;\n /**\n * The delivery policy. An essay is graded later, so `feedback` reaches it —\n * `false` reads a returned grade back as nothing in `review` — and so does\n * AI feedback on a draft, which needs `feedback`, `solutions` and\n * `ai.explanations`. See `DeliveryPolicy` in lk-core.\n */\n delivery?: DeliveryPolicy | null;\n /**\n * The host's AI ports, overriding `LkAiProvider`'s. With a `writingFeedback`\n * port, a learner in `practice` can ask for feedback on a draft before\n * submitting it — up to `maxWritingFeedback` times — and revise. Every\n * correction in it quotes the learner's own words, or the SDK refuses it.\n * Never in `exam` or `review`. See {@link LearnerAi}.\n */\n ai?: LearnerAi;\n}\n\n/**\n * Reads the text out of a learner response. Another activity type's response\n * shape (the `value`/`defaultValue` props take the `LearnerResponse` union,\n * so a generic runner can hand any of them over) reads as empty rather than\n * throwing mid-exam.\n */\nfunction textOf(response: LearnerResponse | undefined): string {\n return response !== undefined && response.type === 'written-response' ? response.text : '';\n}\n\n/**\n * One criterion's score as a percentage of its OWN maximum.\n *\n * `CriterionScore.maxScore` defaults to 1 — the SDK's scaled convention — but\n * a grader may work out of 100, or out of 9 for a CEFR band, and\n * `gradeFromRubric` deliberately stores those judgements verbatim so a grade\n * stays auditable in the units the grader actually used. It normalises when it\n * computes the total; anything DISPLAYING a criterion has to normalise too, or\n * a perfectly ordinary 82/100 renders as \"8200%\". This is the same guard the\n * overall score already applies against `outcome.maxScore`.\n */\nfunction criterionPercent(criterion: CriterionScore): string {\n const max = criterion.maxScore !== undefined && criterion.maxScore > 0 ? criterion.maxScore : 1;\n return `${Math.round(((criterion.score as number) / max) * 100)}%`;\n}\n\n/**\n * Pairs each correction with a list key that is unique by construction. The\n * same mistake made twice carries the same `original`/`corrected` pair, so\n * the grader's `range` is what tells the occurrences apart. `range` is\n * optional and nothing validates it, so a correction without one — or one\n * whose range the grader already used — is keyed by its position instead.\n * Only position keys start with `#`, so the two kinds never collide.\n */\nfunction keyedCorrections(\n corrections: readonly InlineCorrection[],\n): { key: string; correction: InlineCorrection }[] {\n const used = new Set<string>();\n return corrections.map((correction, index) => {\n const anchored = correction.range\n ? `${correction.range.start}-${correction.range.end}:${correction.corrected}`\n : undefined;\n const key = anchored !== undefined && !used.has(anchored) ? anchored : `#${index}`;\n used.add(key);\n return { key, correction };\n });\n}\n\n/**\n * Renders the server-computed outcome in `review` mode. The grade shown here\n * always came back from the asynchronous grader — nothing on this path scores,\n * infers, or defaults a grade. A `written-response` outcome carries no\n * per-item `details` (there are no options or blanks to mark), so the render\n * is the scaled score, the pass state, and the grader's feedback.\n */\nfunction CriteriaList({ criteria, s }: { criteria: readonly CriterionScore[]; s: LkStrings }) {\n return (\n <ul className=\"lk-wr-criteria\">\n {criteria.map((criterion) => (\n <li\n className=\"lk-wr-criterion\"\n key={criterion.name}\n data-na={String(criterion.notApplicable === true)}\n >\n <span className=\"lk-wr-criterion-name\">{criterion.name}</span>\n {criterion.notApplicable === true ? (\n <span className=\"lk-wr-criterion-score\">{s.notApplicable}</span>\n ) : (\n <span className=\"lk-wr-criterion-score\">\n {criterion.band ??\n (typeof criterion.score === 'number' ? criterionPercent(criterion) : '')}\n </span>\n )}\n {criterion.comment ? (\n <span className=\"lk-wr-criterion-comment\">{criterion.comment}</span>\n ) : null}\n </li>\n ))}\n </ul>\n );\n}\n\nfunction CorrectionsList({\n corrections,\n label,\n}: {\n corrections: readonly InlineCorrection[];\n label?: string;\n}) {\n return (\n <ul className=\"lk-wr-corrections\" {...(label !== undefined ? { 'aria-label': label } : {})}>\n {keyedCorrections(corrections).map(({ key, correction }) => (\n <li className=\"lk-wr-correction\" key={key}>\n <del className=\"lk-wr-correction-original\">{correction.original}</del>{' '}\n <ins className=\"lk-wr-correction-corrected\">{correction.corrected}</ins>\n {correction.explanation ? (\n <span className=\"lk-wr-correction-explanation\">{correction.explanation}</span>\n ) : null}\n </li>\n ))}\n </ul>\n );\n}\n\nfunction GradeBody({ grade, s }: { grade: GradeRecord; s: LkStrings }) {\n return (\n <>\n {grade.feedback ? <p className=\"lk-wr-grade-feedback\">{grade.feedback}</p> : null}\n {grade.criteria && grade.criteria.length > 0 ? (\n <CriteriaList criteria={grade.criteria} s={s} />\n ) : null}\n {grade.corrections && grade.corrections.length > 0 ? (\n <CorrectionsList corrections={grade.corrections} />\n ) : null}\n {grade.requiresHumanReview === true ? (\n <p className=\"lk-wr-review-flag\">{s.awaitingHumanReview}</p>\n ) : null}\n </>\n );\n}\n\n/**\n * \"Get feedback on my draft\", and the feedback: what a model said, the\n * corrections it proposed — each quoting the learner's own words, or the SDK\n * would have refused it — its comment on each criterion, and the rubric's\n * total as an indication, never a grade. A revision since the feedback is\n * said, because its corrections may point at words that are gone.\n */\nfunction DraftFeedback({\n help,\n draft,\n disabled,\n s,\n}: {\n help: AiWritingFeedbackHelp;\n draft: string;\n disabled: boolean;\n s: LkStrings;\n}) {\n const buttonRef = useRef<HTMLButtonElement>(null);\n const panelRef = useRef<HTMLElement>(null);\n const refocus = useRef(false);\n const latest = help.latest;\n\n // The feedback lands beside the button that asked; focus follows it there,\n // so a screen reader reads it from its heading rather than all at once.\n useEffect(() => {\n if (latest !== null && refocus.current) {\n refocus.current = false;\n panelRef.current?.focus();\n }\n }, [latest]);\n useEffect(() => {\n if (help.status === 'unavailable') {\n refocus.current = false;\n }\n }, [help.status]);\n\n if (!help.offered && help.used === 0) {\n return null;\n }\n const loading = help.status === 'loading';\n const empty = draft.trim() === '';\n const ask = (): void => {\n refocus.current =\n typeof document !== 'undefined' && document.activeElement === buttonRef.current;\n help.ask();\n };\n return (\n <div className=\"lk-ai lk-wr-ai\" data-state={help.status}>\n <div aria-live=\"polite\">\n {latest !== null ? (\n <section\n ref={panelRef}\n className=\"lk-ai-panel\"\n tabIndex={-1}\n aria-label={s.aiWritingFeedbackHeading}\n data-current={String(help.current)}\n >\n <p className=\"lk-ai-heading\">{s.aiWritingFeedbackHeading}</p>\n {help.current ? null : <p className=\"lk-ai-note\">{s.aiWritingFeedbackOutdated}</p>}\n <p className=\"lk-ai-text\">{latest.text}</p>\n {latest.corrections.length > 0 ? (\n <CorrectionsList corrections={latest.corrections} label={s.aiCorrections} />\n ) : null}\n {latest.criteria.length > 0 ? <CriteriaList criteria={latest.criteria} s={s} /> : null}\n {latest.indicativeScore !== null ? (\n <p className=\"lk-wr-indicative\">\n {s.aiIndicativeScore(Math.round(latest.indicativeScore * 100))}\n </p>\n ) : null}\n <p className=\"lk-ai-notice\">{s.aiNotice}</p>\n </section>\n ) : null}\n {help.status === 'unavailable' ? (\n <p className=\"lk-ai-unavailable\">{s.aiWritingFeedbackUnavailable}</p>\n ) : null}\n </div>\n {help.offered ? (\n help.used < help.limit ? (\n <button\n ref={buttonRef}\n type=\"button\"\n className=\"lk-ai-button\"\n aria-busy={loading || undefined}\n // aria-disabled rather than disabled: focus stays on the button.\n aria-disabled={loading || empty || disabled || undefined}\n onClick={ask}\n >\n {loading ? s.aiWritingFeedbackLoading : s.aiWritingFeedback}\n </button>\n ) : (\n <p className=\"lk-ai-note\">{s.aiNoMoreWritingFeedback}</p>\n )\n ) : null}\n </div>\n );\n}\n\n/**\n * Renders the server-computed outcome in `review` mode. The grade shown here\n * always came back from the grader — nothing on this path scores, infers, or\n * defaults a grade.\n */\nfunction OutcomeSummary({ outcome, s }: { outcome: ItemOutcome; s: LkStrings }) {\n if (outcome.status === 'scored' || outcome.status === 'graded') {\n // `score` is scaled [0–1] against `maxScore`; normalising by `maxScore`\n // is a no-op for the canonical maxScore of 1 and keeps an unscaled\n // grader payload (8.5 / 10) from rendering as 850%.\n const percent =\n outcome.maxScore > 0\n ? Math.round((outcome.score / outcome.maxScore) * 100)\n : Math.round(outcome.score * 100);\n return (\n <div\n className=\"lk-wr-outcome\"\n data-status={outcome.status}\n data-passed={String(outcome.passed)}\n >\n <p className=\"lk-wr-grade\">{s.scoreAnnouncement(percent, outcome.passed)}</p>\n {outcome.status === 'graded' ? (\n <GradeBody grade={outcome.grade} s={s} />\n ) : outcome.feedback ? (\n <p className=\"lk-wr-grade-feedback\">{outcome.feedback}</p>\n ) : null}\n </div>\n );\n }\n if (outcome.status === 'deferred') {\n return (\n <div className=\"lk-wr-outcome\" data-status=\"deferred\">\n <p className=\"lk-wr-grade\">{s.awaitingGrade}</p>\n </div>\n );\n }\n // `unscorable` reasons are developer-facing strings; surface the state, not\n // the reason, and leave it on the element for diagnostics.\n return (\n <div className=\"lk-wr-outcome\" data-status=\"unscorable\" data-reason={outcome.reason}>\n <p className=\"lk-wr-grade\">{s.couldNotBeGraded}</p>\n </div>\n );\n}\n\nexport function WrittenResponse({\n data,\n onSubmitted,\n onSubmit,\n value,\n defaultValue,\n defaultSubmitted,\n onChange,\n renderMode = 'practice',\n outcome,\n sanitizeHtml,\n mediaBudget,\n mediaStrings,\n strings,\n onInteraction,\n theme,\n locale,\n disabled,\n delivery,\n ai: aiProp,\n}: WrittenResponseProps) {\n // Dev-only boundary validation (Req 2.3), same convention as MC/FIB. The\n // content schema is loose, so a `redact()` projection validates too.\n const s = useLkStrings(strings);\n // An essay is graded later, so `feedback` is the one setting that reaches\n // it: whether a returned grade — the score, each criterion, the corrections\n // — is read back in review.\n const policy = useDeliveryPolicy(delivery);\n const ai = useLearnerAi(aiProp);\n\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n const result = validateActivity('written-response', data);\n return result.success ? null : new ActivitySchemaError('written-response', result.errors);\n }, [data]);\n\n const { state, start, complete, getTimeSpent, reset } = useActivityState(\n defaultSubmitted === true ? 'completed' : 'idle',\n );\n const isControlled = value !== undefined;\n const [innerText, setInnerText] = useState<string>(() => textOf(defaultValue));\n const [summary, setSummary] = useState<string | null>(null);\n\n /**\n * Rich text is rendered ONLY through a caller-supplied sanitiser, and only\n * when the author actually shipped the HTML sidecar. Without both, the\n * plain-text prompt is rendered as an escaped React child.\n */\n const promptHtml = useMemo(() => {\n if (sanitizeHtml === undefined || typeof data.promptHtml !== 'string') {\n return null;\n }\n return sanitizeHtml(data.promptHtml);\n }, [data.promptHtml, sanitizeHtml]);\n\n // Latest seed text, kept in a ref so `defaultValue` is NOT a reset trigger:\n // callers pass an object literal, whose identity changes every render.\n const defaultTextRef = useRef<string>(textOf(defaultValue));\n useEffect(() => {\n defaultTextRef.current = textOf(defaultValue);\n }, [defaultValue]);\n\n // Reset on data-prop change (Req 3.7): back to the seed (empty when there\n // is none — the v1 behaviour), never the previous activity's draft.\n // Mirror `defaultSubmitted` so the reset below returns to the SEEDED state.\n // Resetting unconditionally to idle unlocked an item the learner had already\n // committed — which made `defaultSubmitted` a no-op here, since this effect\n // runs right after the first paint.\n const defaultSubmittedRef = useRef(defaultSubmitted);\n useEffect(() => {\n defaultSubmittedRef.current = defaultSubmitted;\n }, [defaultSubmitted]);\n\n // Identity guard so the MOUNT run is a no-op. Without it this effect fires\n // after the first paint and undoes every seed it was just given; with it,\n // only a real `data` change resets. A parent that rebuilds structurally\n // identical entries in render (`activities={raw.map(redact)}`) must not\n // unlock or revert a restored answer — the pager already remounts a slot\n // whose activity actually changed, via its key.\n const lastDataRef = useRef(data);\n useEffect(() => {\n if (lastDataRef.current === data) {\n return;\n }\n lastDataRef.current = data;\n setInnerText(defaultTextRef.current);\n setSummary(null);\n reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [data, reset]);\n\n // Feedback on a draft is a hook, so it is read here, before the throws below.\n const draft = isControlled ? textOf(value) : innerText;\n const writingFeedback = useAiWritingFeedback({\n data,\n response: { type: 'written-response', text: draft, wordCount: countWords(draft) },\n submitted: state === 'completed',\n renderMode,\n delivery: policy,\n disabled: disabled === true,\n ...(ai !== undefined ? { ai } : {}),\n ...(locale !== undefined ? { locale } : {}),\n ...(onInteraction !== undefined ? { onInteraction } : {}),\n });\n\n if (devError) {\n throw devError;\n }\n\n const isExam = renderMode === 'exam';\n const isReview = renderMode === 'review';\n\n const text = isControlled ? textOf(value) : innerText;\n const submitted = state === 'completed';\n const inactive = disabled === true || submitted || isReview;\n const wordCount = countWords(text);\n const withinBounds = wordCount >= data.minWords && wordCount <= data.maxWords;\n\n const handleChange = (next: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n // A controlled component renders from `props.value` only; the caller's\n // state is the single source of truth for the text.\n if (!isControlled) {\n setInnerText(next);\n }\n const nextWordCount = countWords(next);\n onChange?.({ type: 'written-response', text: next, wordCount: nextWordCount });\n onInteraction?.({\n type: 'text-changed',\n activityId: data.id,\n timestamp: Date.now(),\n payload: { wordCount: nextWordCount },\n });\n };\n\n // Practice reveals nothing an essay could leak, but it DOES run the local\n // submit path and emit a practice xAPI statement for work the server is\n // supposed to grade. MultipleChoice and FillInTheBlanks have refused\n // redacted data in `practice` since 0.5.0; this component silently accepted\n // it, so an all-essay redacted paper mounted without `renderMode` looked\n // entirely healthy end to end. Fail at render, like its siblings.\n if (data.redacted === true && renderMode === 'practice') {\n throw new Error(\n `Written Response \"${data.id}\" received redacted activity data in renderMode \"practice\". ` +\n 'Practice runs the local submit path and emits a practice-mode xAPI statement, so an ' +\n 'exam item wired this way is graded nowhere and looks fine while it happens. ' +\n 'Render redacted data with renderMode=\"exam\" (server grades) or \"review\" (pass `outcome`).',\n );\n }\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive || text.trim().length === 0) {\n return;\n }\n let finalWordCount = wordCount;\n let finalWithinBounds = withinBounds;\n // `exam` mode must not call evaluate(): the component does not grade, and\n // a redacted projection carries no scoring input anyway. The two facts\n // evaluate() returns for a deferred type (recomputed word count, bounds)\n // are exactly what is computed above, so the payload is identical either\n // way; practice keeps going through core so the numbers stay canonical.\n if (!isExam) {\n const itemOutcome = evaluate(data, { type: 'written-response', text, wordCount });\n const partial = itemOutcome.status === 'deferred' ? itemOutcome.partial : undefined;\n if (typeof partial?.wordCount === 'number') {\n finalWordCount = partial.wordCount;\n }\n if (typeof partial?.withinWordBounds === 'boolean') {\n finalWithinBounds = partial.withinWordBounds;\n }\n }\n complete();\n const timeSpent = getTimeSpent();\n const xapiStatement = xAPIBuilder.buildSubmittedStatement({\n actor: ANONYMOUS_ACTOR,\n object: {\n id: objectIdFor(data.id),\n name: { [data.locale ?? 'en-US']: data.title },\n type: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n interactionType: 'long-fill-in',\n },\n timeSpentMs: timeSpent,\n response: text,\n resultExtensions: {\n 'urn:learning-kit:extension:word-count': finalWordCount,\n 'urn:learning-kit:extension:within-word-bounds': finalWithinBounds,\n },\n });\n // Raw response first (an exam runner persists this), then the fuller\n // ungraded submission — mirroring `onSubmit` before `onComplete`.\n onSubmit?.({ type: 'written-response', text, wordCount: finalWordCount });\n onSubmitted?.({\n text,\n wordCount: finalWordCount,\n withinWordBounds: finalWithinBounds,\n timeSpent,\n xapiStatement,\n });\n setSummary(s.responseSubmitted);\n onInteraction?.({\n type: 'submitted',\n activityId: data.id,\n timestamp: Date.now(),\n payload: { wordCount: finalWordCount, withinWordBounds: finalWithinBounds },\n });\n };\n\n const promptId = `${data.id}-prompt`;\n const counterId = `${data.id}-counter`;\n const boundsLabel = s.wordBounds(data.minWords, data.maxWords);\n\n return (\n <form\n className=\"lk-wr\"\n aria-label={data.title}\n lang={locale}\n data-render-mode={renderMode}\n style={theme as CSSProperties | undefined}\n onSubmit={handleSubmit}\n >\n {data.media ? (\n <ActivityMedia\n media={data.media}\n renderMode={renderMode}\n {...(mediaBudget !== undefined ? { mediaBudget } : {})}\n {...(mediaStrings !== undefined ? { mediaStrings } : {})}\n {...(strings !== undefined ? { strings } : {})}\n {...(onInteraction !== undefined ? { onInteraction } : {})}\n {...(locale !== undefined ? { locale } : {})}\n />\n ) : null}\n {promptHtml === null ? (\n <p className=\"lk-wr-prompt\" id={promptId}>\n {data.prompt}\n </p>\n ) : (\n // A div, not a p: sanitised author HTML may contain block content.\n <div\n className=\"lk-wr-prompt\"\n id={promptId}\n // biome-ignore lint/security/noDangerouslySetInnerHtml: rendered only through the caller-supplied HtmlSanitizer — the SDK never injects unsanitised markup\n dangerouslySetInnerHTML={{ __html: promptHtml }}\n />\n )}\n <textarea\n className=\"lk-wr-textarea\"\n aria-labelledby={promptId}\n aria-describedby={counterId}\n value={text}\n onChange={(event) => handleChange(event.target.value)}\n // `review` uses readOnly rather than disabled: the submitted essay\n // must stay focusable, selectable and scrollable for a screen reader\n // and a keyboard, while remaining uneditable.\n disabled={!isReview && inactive}\n readOnly={isReview}\n rows={8}\n />\n <div\n className=\"lk-wr-counter\"\n id={counterId}\n aria-live=\"polite\"\n data-within-bounds={withinBounds}\n >\n {s.wordCount(wordCount)} ({boundsLabel})\n </div>\n {ai?.writingFeedback !== undefined ? (\n <DraftFeedback help={writingFeedback} draft={text} disabled={disabled === true} s={s} />\n ) : null}\n {isReview ? null : (\n <button\n type=\"submit\"\n className=\"lk-wr-submit\"\n disabled={inactive || text.trim().length === 0}\n >\n {s.submit}\n </button>\n )}\n <FeedbackRegion id={`${data.id}-feedback`}>\n {isReview ? (\n outcome && (policy.feedback || !outcomeShowsMarks(outcome)) ? (\n <OutcomeSummary outcome={outcome} s={s} />\n ) : null\n ) : (\n summary\n )}\n </FeedbackRegion>\n </form>\n );\n}\n","'use client';\n\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport {\n WrittenResponse as WrittenResponseCore,\n type WrittenResponseProps,\n} from './WrittenResponse.js';\n\nexport type { HtmlSanitizer, Renderable, RenderMode } from '../types.js';\nexport type { WrittenResponseProps, WrittenResponseSubmission } from './WrittenResponse.js';\n\n/**\n * Public Written Response activity: a labelled textarea with a live word\n * counter for free-text writing graded asynchronously (AI or human). The core\n * is wrapped in `ActivityErrorBoundary` so a render failure (incl. the dev\n * schema-validation throw) degrades to an accessible fallback.\n */\nexport function WrittenResponse(props: WrittenResponseProps) {\n // Read here rather than in the boundary: a class component cannot call a\n // hook, and the fallback it renders is learner-facing text like any other.\n const strings = useLkStrings(props.strings);\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title} strings={strings}>\n <WrittenResponseCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
|
|
@@ -1,10 +1,55 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useLearnerAi } from './chunk-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { combineDeliveryPolicies, aiExplanationRequest, checkAiExplanation, aiHintRequest, checkAiHint, aiWritingFeedbackRequest, checkAiWritingFeedback, OPEN_DELIVERY_POLICY, aiSupports, aiAllowedByContent } from '@intellectif/lk-core';
|
|
6
|
-
import { useContext, useState, useRef, useEffect, useCallback } from 'react';
|
|
2
|
+
import { useLearnerAi } from './chunk-ZPI6Q42R.js';
|
|
3
|
+
import { combineDeliveryPolicies, aiExplanationRequest, checkAiExplanation, aiHintRequest, checkAiHint, aiWritingFeedbackRequest, checkAiWritingFeedback, aiCoachingRequest, checkAiCoaching, OPEN_DELIVERY_POLICY, aiSupports, aiAllowedByContent } from '@intellectif/lk-core';
|
|
4
|
+
import { createContext, useContext, useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
7
5
|
|
|
6
|
+
var SequenceSlotContext = createContext(null);
|
|
7
|
+
var lastTake = 0;
|
|
8
|
+
function mintTake() {
|
|
9
|
+
lastTake += 1;
|
|
10
|
+
return lastTake;
|
|
11
|
+
}
|
|
12
|
+
var takes = /* @__PURE__ */ new WeakMap();
|
|
13
|
+
function stampTake(value, take) {
|
|
14
|
+
takes.set(value, take);
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
function takeOf(value) {
|
|
18
|
+
return typeof value === "object" && value !== null ? takes.get(value) : void 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/components/_internal.ts
|
|
22
|
+
function isDevelopment() {
|
|
23
|
+
try {
|
|
24
|
+
return process.env.NODE_ENV !== "production";
|
|
25
|
+
} catch {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
var ANONYMOUS_ACTOR = {
|
|
30
|
+
objectType: "Agent",
|
|
31
|
+
account: { homePage: "https://github.com/intellectif/learning-kit", name: "anonymous" }
|
|
32
|
+
};
|
|
33
|
+
function objectIdFor(id) {
|
|
34
|
+
return `urn:learning-kit:activity:${encodeURIComponent(id)}`;
|
|
35
|
+
}
|
|
36
|
+
function randomSessionId() {
|
|
37
|
+
const c = globalThis.crypto;
|
|
38
|
+
if (c?.randomUUID) {
|
|
39
|
+
return c.randomUUID();
|
|
40
|
+
}
|
|
41
|
+
return `s-${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;
|
|
42
|
+
}
|
|
43
|
+
function legacyCorrect(detail) {
|
|
44
|
+
const { correct } = detail;
|
|
45
|
+
return typeof correct === "boolean" ? correct : void 0;
|
|
46
|
+
}
|
|
47
|
+
function detailIsRight(detail) {
|
|
48
|
+
const outcome = detail.outcome;
|
|
49
|
+
return outcome === void 0 ? legacyCorrect(detail) === true : outcome === "correct";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/ai/rules.ts
|
|
8
53
|
var DEFAULT_MAX_HINTS = 3;
|
|
9
54
|
var MAX_HINTS_CEILING = 10;
|
|
10
55
|
function limitOf(asked) {
|
|
@@ -35,6 +80,11 @@ function explanationOffered(input) {
|
|
|
35
80
|
const graded = renderMode === "practice" && submitted || renderMode === "review" && outcome?.status === "scored";
|
|
36
81
|
return graded && aiSupports(data.type, "explanation") && aiAllowedByContent(data, "explanation");
|
|
37
82
|
}
|
|
83
|
+
function coachingOffered(input) {
|
|
84
|
+
const { data, renderMode, marked } = input;
|
|
85
|
+
const delivery = input.delivery ?? OPEN_DELIVERY_POLICY;
|
|
86
|
+
return delivery.feedback && delivery.ai.explanations && renderMode !== "exam" && marked && aiSupports(data.type, "pronunciation-coaching") && aiAllowedByContent(data, "pronunciation-coaching");
|
|
87
|
+
}
|
|
38
88
|
function callPort(call) {
|
|
39
89
|
try {
|
|
40
90
|
return Promise.resolve(call());
|
|
@@ -383,7 +433,131 @@ function useAiWritingFeedback(input) {
|
|
|
383
433
|
ask
|
|
384
434
|
};
|
|
385
435
|
}
|
|
436
|
+
function useAiCoaching(input) {
|
|
437
|
+
const { data, assessment, grade } = input;
|
|
438
|
+
const around = useContext(SequenceSlotContext);
|
|
439
|
+
const renderMode = modeInForce(input.renderMode, around?.renderMode);
|
|
440
|
+
const delivery = combineDeliveryPolicies(input.delivery, around?.delivery);
|
|
441
|
+
const ai = useLearnerAi(input.ai);
|
|
442
|
+
const { learnerLocale } = localeOf(ai, input.locale);
|
|
443
|
+
const { id, title, referenceText, locale: itemLocale, instructions, ai: permissions } = data;
|
|
444
|
+
const request = useMemo(() => {
|
|
445
|
+
const item = {
|
|
446
|
+
type: "read-aloud",
|
|
447
|
+
id,
|
|
448
|
+
title,
|
|
449
|
+
referenceText,
|
|
450
|
+
locale: itemLocale,
|
|
451
|
+
instructions,
|
|
452
|
+
ai: permissions
|
|
453
|
+
};
|
|
454
|
+
return aiCoachingRequest({
|
|
455
|
+
data: item,
|
|
456
|
+
...assessment !== void 0 && assessment !== null ? { assessment } : {},
|
|
457
|
+
...grade !== void 0 && grade !== null ? { grade } : {},
|
|
458
|
+
...learnerLocale !== void 0 ? { learnerLocale } : {}
|
|
459
|
+
});
|
|
460
|
+
}, [
|
|
461
|
+
id,
|
|
462
|
+
title,
|
|
463
|
+
referenceText,
|
|
464
|
+
itemLocale,
|
|
465
|
+
instructions,
|
|
466
|
+
permissions,
|
|
467
|
+
assessment,
|
|
468
|
+
grade,
|
|
469
|
+
learnerLocale
|
|
470
|
+
]);
|
|
471
|
+
const take = typeof assessment?.recordingKey === "string" ? assessment.recordingKey : null;
|
|
472
|
+
const resetKey = useMemo(
|
|
473
|
+
() => request === null ? "" : JSON.stringify([take, request]),
|
|
474
|
+
[take, request]
|
|
475
|
+
);
|
|
476
|
+
const [state, setState] = useState({ key: resetKey, status: "idle" });
|
|
477
|
+
const run = useRef(0);
|
|
478
|
+
const controller = useRef(null);
|
|
479
|
+
const current = state.key === resetKey ? state : { key: resetKey, status: "idle" };
|
|
480
|
+
const offered = ai?.pronunciationCoaching !== void 0 && coachingOffered({
|
|
481
|
+
data: { type: "read-aloud", id, title, ai: permissions },
|
|
482
|
+
renderMode,
|
|
483
|
+
marked: request !== null,
|
|
484
|
+
delivery
|
|
485
|
+
});
|
|
486
|
+
const latest = useRef({ input, ai, request, resetKey, offered, current });
|
|
487
|
+
latest.current = { input, ai, request, resetKey, offered, current };
|
|
488
|
+
useEffect(() => {
|
|
489
|
+
return () => {
|
|
490
|
+
run.current += 1;
|
|
491
|
+
controller.current?.abort();
|
|
492
|
+
controller.current = null;
|
|
493
|
+
};
|
|
494
|
+
}, [resetKey]);
|
|
495
|
+
useEffect(() => {
|
|
496
|
+
if (!offered) {
|
|
497
|
+
run.current += 1;
|
|
498
|
+
controller.current?.abort();
|
|
499
|
+
controller.current = null;
|
|
500
|
+
setState(
|
|
501
|
+
(previous) => previous.status === "loading" ? { key: previous.key, status: "idle" } : previous
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
}, [offered]);
|
|
505
|
+
const ask = useCallback(() => {
|
|
506
|
+
const now = latest.current;
|
|
507
|
+
const port = now.ai?.pronunciationCoaching;
|
|
508
|
+
const request2 = now.request;
|
|
509
|
+
if (port === void 0 || !now.offered || request2 === null || now.current.status === "loading" || now.current.status === "shown") {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const key = now.resetKey;
|
|
513
|
+
const activityId = request2.facts.activityId;
|
|
514
|
+
run.current += 1;
|
|
515
|
+
const mine = run.current;
|
|
516
|
+
controller.current?.abort();
|
|
517
|
+
const abort = new AbortController();
|
|
518
|
+
controller.current = abort;
|
|
519
|
+
setState({ key, status: "loading" });
|
|
520
|
+
callPort(() => port(request2, { signal: abort.signal })).then(
|
|
521
|
+
(raw) => {
|
|
522
|
+
if (run.current !== mine) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const checked = checkAiCoaching(raw, request2);
|
|
526
|
+
if (!checked.ok) {
|
|
527
|
+
warnRefused("pronunciation coaching", activityId, checked.refusal);
|
|
528
|
+
setState({ key, status: "unavailable" });
|
|
529
|
+
latest.current.input.onInteraction?.({
|
|
530
|
+
type: "ai-help-refused",
|
|
531
|
+
activityId,
|
|
532
|
+
timestamp: Date.now(),
|
|
533
|
+
payload: { feature: "pronunciation-coaching", reason: checked.refusal }
|
|
534
|
+
});
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const { coaching } = checked;
|
|
538
|
+
setState({ key, status: "shown", coaching });
|
|
539
|
+
latest.current.input.onInteraction?.({
|
|
540
|
+
type: "ai-coaching-shown",
|
|
541
|
+
activityId,
|
|
542
|
+
timestamp: Date.now(),
|
|
543
|
+
payload: { words: coaching.words.length, ...shownPayload(coaching) }
|
|
544
|
+
});
|
|
545
|
+
},
|
|
546
|
+
() => {
|
|
547
|
+
if (run.current === mine) {
|
|
548
|
+
setState({ key, status: "unavailable" });
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
);
|
|
552
|
+
}, []);
|
|
553
|
+
return {
|
|
554
|
+
offered,
|
|
555
|
+
status: offered ? current.status : "idle",
|
|
556
|
+
coaching: offered && current.status === "shown" ? current.coaching : null,
|
|
557
|
+
ask
|
|
558
|
+
};
|
|
559
|
+
}
|
|
386
560
|
|
|
387
|
-
export { useAiExplanation, useAiHints, useAiWritingFeedback };
|
|
388
|
-
//# sourceMappingURL=chunk-
|
|
389
|
-
//# sourceMappingURL=chunk-
|
|
561
|
+
export { ANONYMOUS_ACTOR, SequenceSlotContext, detailIsRight, isDevelopment, legacyCorrect, mintTake, objectIdFor, randomSessionId, stampTake, takeOf, useAiCoaching, useAiExplanation, useAiHints, useAiWritingFeedback };
|
|
562
|
+
//# sourceMappingURL=chunk-MXNC5UNO.js.map
|
|
563
|
+
//# sourceMappingURL=chunk-MXNC5UNO.js.map
|