@intellectif/lk-react 18.0.0 → 18.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 +44 -0
- package/README.md +3 -1
- package/dist/{WrittenResponse-B4kY-U0v.d.ts → WrittenResponse-CeHY5u3W.d.ts} +1 -1
- package/dist/{WrittenResponse-Dl3zah22.d.cts → WrittenResponse-DT_Yqbq1.d.cts} +1 -1
- package/dist/ai/useAiHelp.cjs +20 -0
- package/dist/ai/useAiHelp.cjs.map +1 -0
- package/dist/ai/useAiHelp.d.cts +146 -0
- package/dist/ai/useAiHelp.d.ts +146 -0
- package/dist/ai/useAiHelp.js +7 -0
- package/dist/ai/useAiHelp.js.map +1 -0
- package/dist/{chunk-Z5XY66PO.cjs → chunk-3SDCALS3.cjs} +16 -16
- package/dist/chunk-3SDCALS3.cjs.map +1 -0
- package/dist/{chunk-QA42VDTA.cjs → chunk-3SJ7TVL6.cjs} +12 -11
- package/dist/chunk-3SJ7TVL6.cjs.map +1 -0
- package/dist/{chunk-V5BTVGPA.js → chunk-4NERCBOW.js} +4 -3
- package/dist/chunk-4NERCBOW.js.map +1 -0
- package/dist/{chunk-UYQBEPNZ.js → chunk-6NY7OTOO.js} +15 -8
- package/dist/chunk-6NY7OTOO.js.map +1 -0
- package/dist/{chunk-RZ52SEYV.cjs → chunk-7GYAUA56.cjs} +10 -9
- package/dist/chunk-7GYAUA56.cjs.map +1 -0
- package/dist/{chunk-BAKLJKFZ.cjs → chunk-APZ6JFN6.cjs} +4 -29
- package/dist/chunk-APZ6JFN6.cjs.map +1 -0
- package/dist/chunk-BURYHHBM.js +114 -0
- package/dist/chunk-BURYHHBM.js.map +1 -0
- package/dist/{chunk-H63O5HPW.cjs → chunk-CQNKZ435.cjs} +8 -7
- package/dist/chunk-CQNKZ435.cjs.map +1 -0
- package/dist/chunk-EANGKAKG.cjs +32 -0
- package/dist/chunk-EANGKAKG.cjs.map +1 -0
- package/dist/{chunk-YTGRXD23.js → chunk-FEOFX4QW.js} +4 -25
- package/dist/chunk-FEOFX4QW.js.map +1 -0
- package/dist/{chunk-QJW3K6JN.js → chunk-G7AGM2Y7.js} +4 -3
- package/dist/chunk-G7AGM2Y7.js.map +1 -0
- package/dist/{chunk-HZMILQYO.js → chunk-GM4PXDZ2.js} +5 -4
- package/dist/chunk-GM4PXDZ2.js.map +1 -0
- package/dist/{chunk-QHK6PTUR.js → chunk-IKV2I7KT.js} +5 -4
- package/dist/chunk-IKV2I7KT.js.map +1 -0
- package/dist/{chunk-TI23GIFM.cjs → chunk-JH77MAON.cjs} +12 -11
- package/dist/chunk-JH77MAON.cjs.map +1 -0
- package/dist/{chunk-6DTW5T3Z.js → chunk-JSRKES2Z.js} +5 -4
- package/dist/chunk-JSRKES2Z.js.map +1 -0
- package/dist/chunk-JVWLXVYG.cjs +27 -0
- package/dist/chunk-JVWLXVYG.cjs.map +1 -0
- package/dist/{chunk-UQIRDE52.cjs → chunk-LX2X4KC7.cjs} +6 -5
- package/dist/chunk-LX2X4KC7.cjs.map +1 -0
- package/dist/{chunk-KWKB74KL.js → chunk-MKWBLNTD.js} +198 -44
- package/dist/chunk-MKWBLNTD.js.map +1 -0
- package/dist/chunk-NWGQ7JML.cjs +117 -0
- package/dist/chunk-NWGQ7JML.cjs.map +1 -0
- package/dist/{chunk-6RRFH7C4.cjs → chunk-NZM3M5IR.cjs} +39 -54
- package/dist/chunk-NZM3M5IR.cjs.map +1 -0
- package/dist/chunk-PPFVTXGJ.js +27 -0
- package/dist/chunk-PPFVTXGJ.js.map +1 -0
- package/dist/{chunk-3TJSFPO4.cjs → chunk-PROKRNL2.cjs} +26 -19
- package/dist/chunk-PROKRNL2.cjs.map +1 -0
- package/dist/chunk-TEAVPUMX.js +22 -0
- package/dist/chunk-TEAVPUMX.js.map +1 -0
- package/dist/{chunk-ALH6TUX2.js → chunk-THHFIP4N.js} +5 -4
- package/dist/chunk-THHFIP4N.js.map +1 -0
- package/dist/chunk-TL3GVPBO.cjs +234 -0
- package/dist/chunk-TL3GVPBO.cjs.map +1 -0
- package/dist/{chunk-XEGBGKHV.cjs → chunk-TXMP5AOW.cjs} +206 -52
- package/dist/chunk-TXMP5AOW.cjs.map +1 -0
- package/dist/{chunk-KP57YCYQ.js → chunk-UXV3V5NH.js} +23 -35
- package/dist/chunk-UXV3V5NH.js.map +1 -0
- package/dist/{chunk-H3IRVX7J.js → chunk-WSPVQR4O.js} +9 -9
- package/dist/chunk-WSPVQR4O.js.map +1 -0
- package/dist/{chunk-JKMBFHER.cjs → chunk-X4YU6BX7.cjs} +12 -11
- package/dist/chunk-X4YU6BX7.cjs.map +1 -0
- package/dist/chunk-YGJE7RSW.js +231 -0
- package/dist/chunk-YGJE7RSW.js.map +1 -0
- package/dist/components/ActivityPreview.cjs +14 -11
- package/dist/components/ActivityPreview.d.cts +7 -3
- package/dist/components/ActivityPreview.d.ts +7 -3
- package/dist/components/ActivityPreview.js +13 -10
- package/dist/components/ActivitySequence.cjs +14 -11
- package/dist/components/ActivitySequence.d.cts +75 -11
- package/dist/components/ActivitySequence.d.ts +75 -11
- package/dist/components/ActivitySequence.js +13 -10
- package/dist/components/Dictation.cjs +7 -4
- package/dist/components/Dictation.d.cts +1 -1
- package/dist/components/Dictation.d.ts +1 -1
- package/dist/components/Dictation.js +6 -3
- package/dist/components/FillInTheBlanks.cjs +7 -4
- package/dist/components/FillInTheBlanks.d.cts +1 -1
- package/dist/components/FillInTheBlanks.d.ts +1 -1
- package/dist/components/FillInTheBlanks.js +6 -3
- package/dist/components/GapSelect.cjs +7 -4
- package/dist/components/GapSelect.d.cts +1 -1
- package/dist/components/GapSelect.d.ts +1 -1
- package/dist/components/GapSelect.js +6 -3
- package/dist/components/InteractiveVideo.cjs +14 -11
- package/dist/components/InteractiveVideo.d.cts +2 -2
- package/dist/components/InteractiveVideo.d.ts +2 -2
- package/dist/components/InteractiveVideo.js +12 -9
- package/dist/components/MultipleChoice.cjs +7 -4
- package/dist/components/MultipleChoice.d.cts +1 -1
- package/dist/components/MultipleChoice.d.ts +1 -1
- package/dist/components/MultipleChoice.js +6 -3
- package/dist/components/PronunciationFeedback.cjs +4 -3
- package/dist/components/PronunciationFeedback.d.cts +1 -1
- package/dist/components/PronunciationFeedback.d.ts +1 -1
- package/dist/components/PronunciationFeedback.js +3 -2
- package/dist/components/ReadAloud.cjs +6 -4
- package/dist/components/ReadAloud.d.cts +2 -2
- package/dist/components/ReadAloud.d.ts +2 -2
- package/dist/components/ReadAloud.js +5 -3
- package/dist/components/StimulusPanel.d.cts +1 -1
- package/dist/components/StimulusPanel.d.ts +1 -1
- package/dist/components/WrittenResponse.cjs +4 -3
- package/dist/components/WrittenResponse.d.cts +3 -3
- package/dist/components/WrittenResponse.d.ts +3 -3
- package/dist/components/WrittenResponse.js +3 -2
- package/dist/i18n/LkIntlProvider.d.cts +2 -2
- package/dist/i18n/LkIntlProvider.d.ts +2 -2
- package/dist/index.cjs +34 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +15 -12
- package/dist/index.js.map +1 -1
- package/dist/{strings-PCYKWCwd.d.ts → types-BY-4S5Rd.d.ts} +761 -761
- package/dist/{strings-BOPKhnS-.d.cts → types-DtPYG1hh.d.cts} +761 -761
- package/package.json +11 -1
- package/dist/chunk-3TJSFPO4.cjs.map +0 -1
- package/dist/chunk-5F4Q6JT7.cjs +0 -289
- package/dist/chunk-5F4Q6JT7.cjs.map +0 -1
- package/dist/chunk-6DTW5T3Z.js.map +0 -1
- package/dist/chunk-6RRFH7C4.cjs.map +0 -1
- package/dist/chunk-ALH6TUX2.js.map +0 -1
- package/dist/chunk-BAKLJKFZ.cjs.map +0 -1
- package/dist/chunk-GR33G5QM.js +0 -286
- package/dist/chunk-GR33G5QM.js.map +0 -1
- package/dist/chunk-H3IRVX7J.js.map +0 -1
- package/dist/chunk-H63O5HPW.cjs.map +0 -1
- package/dist/chunk-HZMILQYO.js.map +0 -1
- package/dist/chunk-JKMBFHER.cjs.map +0 -1
- package/dist/chunk-KP57YCYQ.js.map +0 -1
- package/dist/chunk-KWKB74KL.js.map +0 -1
- package/dist/chunk-QA42VDTA.cjs.map +0 -1
- package/dist/chunk-QHK6PTUR.js.map +0 -1
- package/dist/chunk-QJW3K6JN.js.map +0 -1
- package/dist/chunk-RZ52SEYV.cjs.map +0 -1
- package/dist/chunk-TI23GIFM.cjs.map +0 -1
- package/dist/chunk-UQIRDE52.cjs.map +0 -1
- package/dist/chunk-UYQBEPNZ.js.map +0 -1
- package/dist/chunk-V5BTVGPA.js.map +0 -1
- package/dist/chunk-XEGBGKHV.cjs.map +0 -1
- package/dist/chunk-YTGRXD23.js.map +0 -1
- package/dist/chunk-Z5XY66PO.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# @intellectif/lk-react
|
|
2
2
|
|
|
3
|
+
## 18.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 50c3285: A question a host draws inside `<ActivitySequence>` gets everything one the SDK draws has, and the AI
|
|
8
|
+
rules are available without the SDK's buttons.
|
|
9
|
+
|
|
10
|
+
- **`renderers` overrides get a `question` prop** (`SequenceQuestion`), the pager's half of the
|
|
11
|
+
contract, as `renderQuestion` gave one to `<InteractiveVideo>` in 16.1.0:
|
|
12
|
+
- `active` — whether this is the question on screen. **Stop the microphone, any timer and any
|
|
13
|
+
speech when it turns `false`:** the pager pauses the media inside the question and stops the
|
|
14
|
+
recorders the SDK started, but cannot reach one it did not create;
|
|
15
|
+
- `setPending(true/false)` — work of your own is on its way (an upload, a save, a judgement).
|
|
16
|
+
`onFinished` and `onComplete` wait for it, so a set is never reported a moment before the last
|
|
17
|
+
answer settles. Call it with `false` however the work ends, a failure included;
|
|
18
|
+
- `portalContainer` — a node inside this question's own pane. Portal a popover into it rather than
|
|
19
|
+
into `document.body`, and it is hidden when the learner pages away and painted with the question
|
|
20
|
+
in fullscreen;
|
|
21
|
+
- `clear()` — the learner withdrew their answer: the slot holds nothing again, and the set is
|
|
22
|
+
unfinished until it is answered again;
|
|
23
|
+
- `emit(type, payload?)` — an interaction, with this question's `activityId` and the time filled
|
|
24
|
+
in;
|
|
25
|
+
- `slot` — the identity to store the answer against, the same object `onSubmit` is handed.
|
|
26
|
+
- **Every call keeps one identity for the life of the set**, `onSubmit` and `onComplete` included, so
|
|
27
|
+
a renderer may hold them in an effect and a host that re-renders on every keystroke does not re-run
|
|
28
|
+
it.
|
|
29
|
+
- **New: `useAiHints` and `useAiExplanation`** (`@intellectif/lk-react/ai/useAiHelp`, also on the
|
|
30
|
+
root) — AI help for a question you draw yourself: the same rules, facts, checks and records the
|
|
31
|
+
bundled components use, with your own markup. `ask` does nothing where the rules say no, so a page
|
|
32
|
+
drawing its own button reaches no model in an `exam`. The SDK's own components are built on these
|
|
33
|
+
hooks, so there is one implementation of the rules and not two.
|
|
34
|
+
- **The paper decides the mode.** A question inside `<ActivitySequence>` or `<InteractiveVideo>` now
|
|
35
|
+
knows the mode it is being delivered in, so the hooks refuse help on an `exam` paper even when the
|
|
36
|
+
host does not pass `renderMode` to them, or passes `practice` — which a component that defaults it
|
|
37
|
+
can do inside an exam. Without that, a host's own question under an `LkAiProvider` could reach a
|
|
38
|
+
model on a paper of record.
|
|
39
|
+
|
|
40
|
+
`ActivityRenderer` is now `ComponentType<ActivityProps & { question?: SequenceQuestion }>`. A
|
|
41
|
+
renderer written as `ComponentType<ActivityProps>` still fits and simply ignores the new prop; only
|
|
42
|
+
assigning an `ActivityRenderer` _back_ to a `ComponentType<ActivityProps>` variable needs a widened
|
|
43
|
+
annotation.
|
|
44
|
+
|
|
45
|
+
See `docs/ai.md` and the `renderers` documentation on `<ActivitySequence>`.
|
|
46
|
+
|
|
3
47
|
## 18.0.0
|
|
4
48
|
|
|
5
49
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -82,7 +82,8 @@ export function Demo() {
|
|
|
82
82
|
- **`shuffleSeed`** — one seed drives question order *and* each item's option order, so a review render reproduces exactly the arrangement the learner sat. Shuffling in `exam` / `review` mode **requires** it.
|
|
83
83
|
- **`<StimulusPanel>`** — a shared passage / recording / image (an item group's stimulus) as a landmark region; the sequence uses it, and a custom runner can too.
|
|
84
84
|
- **`<ActivityPreview>`** — an editor's preview. Renders a draft in any `renderMode` with a simulated `response` (marked with `evaluate()` in `review`), and a translatable notice — or your `fallback`, handed the issues — while `validateDraft` says the draft is not finished. Nothing it renders is recorded, and a recording's play limit is counted in memory only.
|
|
85
|
-
- **Custom activity types** — `renderers={{ 'my-type': MyRenderer }}` on the sequence; a key matching a built-in overrides it, so you can replace a bundled renderer without forking the sequencer.
|
|
85
|
+
- **Custom activity types** — `renderers={{ 'my-type': MyRenderer }}` on the sequence; a key matching a built-in overrides it, so you can replace a bundled renderer without forking the sequencer. A renderer is given the same contract the SDK's own components have: a `question` prop that says when it is the question on screen (so it can stop its microphone), holds the set while its own work is in flight, gives it somewhere to portal a popover, and reports what the learner did through calls that keep one identity.
|
|
86
|
+
- **AI help in a question you draw** — `useAiHints` and `useAiExplanation` give a host's own renderer the rules, the facts and the refusals the bundled components use, without their buttons. `ask` does nothing where the rules say no, so an exam reaches no model however the page asks.
|
|
86
87
|
- **Rich text, opt-in** — `sanitizeHtml` renders author-supplied `questionHtml` / `promptHtml`. The SDK ships **no** sanitiser and injects no HTML without one; without it the escaped plain-text field is used. (`FillInTheBlanks` ignores `passageHtml` by design — its passage hosts the answer inputs.)
|
|
87
88
|
- **Media per question** — optional `image` / `audio` / `video` / `embed` (YouTube/Vimeo iframe) above the question; alt-text required for `image`/`embed` (WCAG).
|
|
88
89
|
- **Playback policy for listening papers** — an audio recording can declare `maxPlays`, `seek: 'none'` and `rate: 'fixed'`. The SDK then renders its own accessible transport (44px targets, a live plays-remaining status, an exhausted button that stays focusable and says why) and enforces the policy from the element's own events, so a hardware media key goes through the budget too. Wire `mediaBudget` on `<ActivitySequence>` to make a play budget survive a refresh.
|
|
@@ -116,6 +117,7 @@ export function Demo() {
|
|
|
116
117
|
| `@intellectif/lk-react/hooks/useSpeechRecorder` | Microphone capture to 16 kHz mono WAV, and `CAPTURE_PROCESSOR_SOURCE` for a self-hosted worklet |
|
|
117
118
|
| `@intellectif/lk-react/hooks/useXAPI` | LRS delivery (retry, never-throws) |
|
|
118
119
|
| `@intellectif/lk-react/ai/LkAiProvider` | `<LkAiProvider>`, `useLearnerAi`, and the `LearnerAi` type |
|
|
120
|
+
| `@intellectif/lk-react/ai/useAiHelp` | `useAiHints`, `useAiExplanation` — the AI rules without the SDK's own buttons |
|
|
119
121
|
| `@intellectif/lk-react/i18n/LkIntlProvider` | `<LkIntlProvider>`, `useLkStrings`, `useLkDirection`, `DEFAULT_STRINGS`, `mergeStrings`, `directionForLocale` |
|
|
120
122
|
| `@intellectif/lk-react/theme/ThemeProvider` | `<ThemeProvider>`, `darkTheme`, `useTheme`, `createTailwindTheme` |
|
|
121
123
|
| `@intellectif/lk-react/theme/defaults.css` | Tokens (required) |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { XAPIStatement, WrittenResponseData, LearnerResponse, ItemOutcome, InteractionEvent, ThemeTokens } from '@intellectif/lk-core';
|
|
2
|
-
import { f as Renderable, e as RenderMode, H as HtmlSanitizer, M as MediaBudgetBinding, b as MediaTransportStrings, a as LkStringsOverride } from './
|
|
2
|
+
import { f as Renderable, e as RenderMode, H as HtmlSanitizer, M as MediaBudgetBinding, b as MediaTransportStrings, a as LkStringsOverride } from './types-BY-4S5Rd.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Payload delivered when the learner submits a written response. Grading is
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { XAPIStatement, WrittenResponseData, LearnerResponse, ItemOutcome, InteractionEvent, ThemeTokens } from '@intellectif/lk-core';
|
|
2
|
-
import { f as Renderable, e as RenderMode, H as HtmlSanitizer, M as MediaBudgetBinding, b as MediaTransportStrings, a as LkStringsOverride } from './
|
|
2
|
+
import { f as Renderable, e as RenderMode, H as HtmlSanitizer, M as MediaBudgetBinding, b as MediaTransportStrings, a as LkStringsOverride } from './types-DtPYG1hh.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Payload delivered when the learner submits a written response. Grading is
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var chunkTL3GVPBO_cjs = require('../chunk-TL3GVPBO.cjs');
|
|
5
|
+
require('../chunk-ZDYNAWDZ.cjs');
|
|
6
|
+
require('../chunk-JVWLXVYG.cjs');
|
|
7
|
+
require('../chunk-EANGKAKG.cjs');
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Object.defineProperty(exports, "useAiExplanation", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () { return chunkTL3GVPBO_cjs.useAiExplanation; }
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(exports, "useAiHints", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return chunkTL3GVPBO_cjs.useAiHints; }
|
|
18
|
+
});
|
|
19
|
+
//# sourceMappingURL=useAiHelp.cjs.map
|
|
20
|
+
//# sourceMappingURL=useAiHelp.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"useAiHelp.cjs"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { AiTextResult, LearnerResponse, InteractionEvent, ItemOutcome } from '@intellectif/lk-core';
|
|
2
|
+
import { g as RenderableActivity, e as RenderMode } from '../types-DtPYG1hh.cjs';
|
|
3
|
+
import { LearnerAi } from './LkAiProvider.cjs';
|
|
4
|
+
import '../machine-CuwKqWXC.cjs';
|
|
5
|
+
import 'react';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The question AI help is being asked about, as the SDK's own components know
|
|
9
|
+
* it. A host drawing its own question passes what it has; everything optional
|
|
10
|
+
* defaults to what the components pass.
|
|
11
|
+
*/
|
|
12
|
+
interface AiHelpSituation {
|
|
13
|
+
/** The question, as `data` is given to any activity component. */
|
|
14
|
+
data: RenderableActivity;
|
|
15
|
+
/** The learner's answer as it stands: submitted, restored, or being written. */
|
|
16
|
+
response: LearnerResponse;
|
|
17
|
+
/** Whether the learner has committed that answer. */
|
|
18
|
+
submitted: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Pass on the `renderMode` your question was given. Left out, the paper
|
|
21
|
+
* around the question answers for it — a question inside `<ActivitySequence>`
|
|
22
|
+
* or `<InteractiveVideo>` knows its mode — and `practice` only where there is
|
|
23
|
+
* no paper at all. An `exam` around the question wins over anything passed
|
|
24
|
+
* here.
|
|
25
|
+
*/
|
|
26
|
+
renderMode?: RenderMode;
|
|
27
|
+
/**
|
|
28
|
+
* The AI ports to use, overriding {@link LkAiProvider}'s for this question.
|
|
29
|
+
* A question inside `<ActivitySequence>` or `<InteractiveVideo>` is handed
|
|
30
|
+
* the ports in force as `ai` — pass that straight in.
|
|
31
|
+
*/
|
|
32
|
+
ai?: LearnerAi;
|
|
33
|
+
/** The interface language: the default language for what a model writes. */
|
|
34
|
+
locale?: string;
|
|
35
|
+
/** Where `ai-hint-shown` and `ai-explanation-shown` go. */
|
|
36
|
+
onInteraction?: (event: InteractionEvent) => void;
|
|
37
|
+
}
|
|
38
|
+
interface AiExplanationInput extends AiHelpSituation {
|
|
39
|
+
/** The grade of record, which `review` explains. */
|
|
40
|
+
outcome?: ItemOutcome;
|
|
41
|
+
}
|
|
42
|
+
interface AiExplanationHelp {
|
|
43
|
+
/**
|
|
44
|
+
* Whether to offer the learner an explanation at all: a port is present, the
|
|
45
|
+
* answer is graded, the type is one the SDK explains, and the author left
|
|
46
|
+
* explanations on. False in `exam`, always.
|
|
47
|
+
*/
|
|
48
|
+
offered: boolean;
|
|
49
|
+
status: 'idle' | 'loading' | 'shown' | 'unavailable';
|
|
50
|
+
/** What to show, once it has arrived and been checked. Its `text` is text, never HTML. */
|
|
51
|
+
explanation: AiTextResult | null;
|
|
52
|
+
/**
|
|
53
|
+
* Asks the port, when the learner presses something. Does nothing while a
|
|
54
|
+
* call is on its way, and nothing at all where `offered` is false — so an
|
|
55
|
+
* exam cannot reach a model through this hook, whatever ports are in scope.
|
|
56
|
+
*
|
|
57
|
+
* One identity for the life of the question, so it is safe in an effect's
|
|
58
|
+
* dependencies.
|
|
59
|
+
*/
|
|
60
|
+
ask: () => void;
|
|
61
|
+
}
|
|
62
|
+
interface AiHintsInput extends AiHelpSituation {
|
|
63
|
+
/** A disabled question gives no hints, as it takes no answer. */
|
|
64
|
+
disabled?: boolean;
|
|
65
|
+
}
|
|
66
|
+
interface AiHintsHelp {
|
|
67
|
+
/**
|
|
68
|
+
* Whether the learner may ask for hints here at all: a port is present, the
|
|
69
|
+
* mode is `practice`, the answer is not in, the question is not disabled or
|
|
70
|
+
* redacted, the type takes hints and the author left them on. It stays true
|
|
71
|
+
* once `used` reaches `limit` — there are no more to give, but the question
|
|
72
|
+
* is still one that gives them.
|
|
73
|
+
*/
|
|
74
|
+
offered: boolean;
|
|
75
|
+
status: 'idle' | 'loading' | 'unavailable';
|
|
76
|
+
/** Every hint shown for this question, in the order they were given. */
|
|
77
|
+
hints: readonly AiTextResult[];
|
|
78
|
+
/** The host's `maxHints`, as a whole number from 1 to 10 (3 by default). */
|
|
79
|
+
limit: number;
|
|
80
|
+
/** How many hints have been shown. A refused hint is not counted. */
|
|
81
|
+
used: number;
|
|
82
|
+
/**
|
|
83
|
+
* Asks for the next hint. Does nothing at the limit, while a call is on its
|
|
84
|
+
* way, or where `offered` is false. One identity for the life of the
|
|
85
|
+
* question.
|
|
86
|
+
*/
|
|
87
|
+
ask: () => void;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* "Explain my answer", without the SDK's own button and panel: the rules, the
|
|
91
|
+
* call, the checks and the record, for a question you draw yourself.
|
|
92
|
+
*
|
|
93
|
+
* It is what `<MultipleChoice>` and the others use, so a host's own question
|
|
94
|
+
* offers help on exactly the same terms: never in `exam`, never where the
|
|
95
|
+
* item's author switched explanations off, and never an explanation whose
|
|
96
|
+
* verdict contradicts the grade the SDK computed.
|
|
97
|
+
*
|
|
98
|
+
* ```tsx
|
|
99
|
+
* const { offered, status, explanation, ask } = useAiExplanation({
|
|
100
|
+
* data, response, submitted, renderMode, outcome, ai: question.ai,
|
|
101
|
+
* });
|
|
102
|
+
* if (!offered) return null;
|
|
103
|
+
* return explanation === null ? (
|
|
104
|
+
* <button type="button" onClick={ask} aria-busy={status === 'loading'}>Explain my answer</button>
|
|
105
|
+
* ) : (
|
|
106
|
+
* <section>
|
|
107
|
+
* <p>{explanation.text}</p>
|
|
108
|
+
* <p>Written by AI. It can make mistakes.</p>
|
|
109
|
+
* </section>
|
|
110
|
+
* );
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* **Say who wrote it.** The SDK's own panel carries "Written by AI. It can make
|
|
114
|
+
* mistakes."; a page that drops that line passes a model's words off as the
|
|
115
|
+
* course's.
|
|
116
|
+
*
|
|
117
|
+
* When the answer or its grade changes, the explanation is dropped and a call
|
|
118
|
+
* still on its way is abandoned — a late explanation never lands beside an
|
|
119
|
+
* answer it was not written for — as it is when the caller unmounts.
|
|
120
|
+
*/
|
|
121
|
+
declare function useAiExplanation(input: AiExplanationInput): AiExplanationHelp;
|
|
122
|
+
/**
|
|
123
|
+
* "Get a hint", without the SDK's own button and list: the rules, the call, the
|
|
124
|
+
* checks and the record, for a question you draw yourself.
|
|
125
|
+
*
|
|
126
|
+
* It is what `<MultipleChoice>` and the others use, so a host's own question
|
|
127
|
+
* gives hints on exactly the same terms: only in `practice` before submit, up
|
|
128
|
+
* to the host's limit, never on a redacted item or a type the SDK does not hint
|
|
129
|
+
* for, and never a hint that gives the answer away — which is refused rather
|
|
130
|
+
* than shown, and not counted.
|
|
131
|
+
*
|
|
132
|
+
* ```tsx
|
|
133
|
+
* const { offered, status, hints, used, limit, ask } = useAiHints({
|
|
134
|
+
* data, response, submitted, renderMode, ai: question.ai,
|
|
135
|
+
* });
|
|
136
|
+
* ```
|
|
137
|
+
*
|
|
138
|
+
* **Say who wrote them**, as the SDK's own list does: "Written by AI. It can
|
|
139
|
+
* make mistakes."
|
|
140
|
+
*
|
|
141
|
+
* Keep the hints on screen after submit — the learner can see what they were
|
|
142
|
+
* given — while `offered` turns false. Another question starts with none.
|
|
143
|
+
*/
|
|
144
|
+
declare function useAiHints(input: AiHintsInput): AiHintsHelp;
|
|
145
|
+
|
|
146
|
+
export { type AiExplanationHelp, type AiExplanationInput, type AiHelpSituation, type AiHintsHelp, type AiHintsInput, useAiExplanation, useAiHints };
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { AiTextResult, LearnerResponse, InteractionEvent, ItemOutcome } from '@intellectif/lk-core';
|
|
2
|
+
import { g as RenderableActivity, e as RenderMode } from '../types-BY-4S5Rd.js';
|
|
3
|
+
import { LearnerAi } from './LkAiProvider.js';
|
|
4
|
+
import '../machine-CuwKqWXC.js';
|
|
5
|
+
import 'react';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The question AI help is being asked about, as the SDK's own components know
|
|
9
|
+
* it. A host drawing its own question passes what it has; everything optional
|
|
10
|
+
* defaults to what the components pass.
|
|
11
|
+
*/
|
|
12
|
+
interface AiHelpSituation {
|
|
13
|
+
/** The question, as `data` is given to any activity component. */
|
|
14
|
+
data: RenderableActivity;
|
|
15
|
+
/** The learner's answer as it stands: submitted, restored, or being written. */
|
|
16
|
+
response: LearnerResponse;
|
|
17
|
+
/** Whether the learner has committed that answer. */
|
|
18
|
+
submitted: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Pass on the `renderMode` your question was given. Left out, the paper
|
|
21
|
+
* around the question answers for it — a question inside `<ActivitySequence>`
|
|
22
|
+
* or `<InteractiveVideo>` knows its mode — and `practice` only where there is
|
|
23
|
+
* no paper at all. An `exam` around the question wins over anything passed
|
|
24
|
+
* here.
|
|
25
|
+
*/
|
|
26
|
+
renderMode?: RenderMode;
|
|
27
|
+
/**
|
|
28
|
+
* The AI ports to use, overriding {@link LkAiProvider}'s for this question.
|
|
29
|
+
* A question inside `<ActivitySequence>` or `<InteractiveVideo>` is handed
|
|
30
|
+
* the ports in force as `ai` — pass that straight in.
|
|
31
|
+
*/
|
|
32
|
+
ai?: LearnerAi;
|
|
33
|
+
/** The interface language: the default language for what a model writes. */
|
|
34
|
+
locale?: string;
|
|
35
|
+
/** Where `ai-hint-shown` and `ai-explanation-shown` go. */
|
|
36
|
+
onInteraction?: (event: InteractionEvent) => void;
|
|
37
|
+
}
|
|
38
|
+
interface AiExplanationInput extends AiHelpSituation {
|
|
39
|
+
/** The grade of record, which `review` explains. */
|
|
40
|
+
outcome?: ItemOutcome;
|
|
41
|
+
}
|
|
42
|
+
interface AiExplanationHelp {
|
|
43
|
+
/**
|
|
44
|
+
* Whether to offer the learner an explanation at all: a port is present, the
|
|
45
|
+
* answer is graded, the type is one the SDK explains, and the author left
|
|
46
|
+
* explanations on. False in `exam`, always.
|
|
47
|
+
*/
|
|
48
|
+
offered: boolean;
|
|
49
|
+
status: 'idle' | 'loading' | 'shown' | 'unavailable';
|
|
50
|
+
/** What to show, once it has arrived and been checked. Its `text` is text, never HTML. */
|
|
51
|
+
explanation: AiTextResult | null;
|
|
52
|
+
/**
|
|
53
|
+
* Asks the port, when the learner presses something. Does nothing while a
|
|
54
|
+
* call is on its way, and nothing at all where `offered` is false — so an
|
|
55
|
+
* exam cannot reach a model through this hook, whatever ports are in scope.
|
|
56
|
+
*
|
|
57
|
+
* One identity for the life of the question, so it is safe in an effect's
|
|
58
|
+
* dependencies.
|
|
59
|
+
*/
|
|
60
|
+
ask: () => void;
|
|
61
|
+
}
|
|
62
|
+
interface AiHintsInput extends AiHelpSituation {
|
|
63
|
+
/** A disabled question gives no hints, as it takes no answer. */
|
|
64
|
+
disabled?: boolean;
|
|
65
|
+
}
|
|
66
|
+
interface AiHintsHelp {
|
|
67
|
+
/**
|
|
68
|
+
* Whether the learner may ask for hints here at all: a port is present, the
|
|
69
|
+
* mode is `practice`, the answer is not in, the question is not disabled or
|
|
70
|
+
* redacted, the type takes hints and the author left them on. It stays true
|
|
71
|
+
* once `used` reaches `limit` — there are no more to give, but the question
|
|
72
|
+
* is still one that gives them.
|
|
73
|
+
*/
|
|
74
|
+
offered: boolean;
|
|
75
|
+
status: 'idle' | 'loading' | 'unavailable';
|
|
76
|
+
/** Every hint shown for this question, in the order they were given. */
|
|
77
|
+
hints: readonly AiTextResult[];
|
|
78
|
+
/** The host's `maxHints`, as a whole number from 1 to 10 (3 by default). */
|
|
79
|
+
limit: number;
|
|
80
|
+
/** How many hints have been shown. A refused hint is not counted. */
|
|
81
|
+
used: number;
|
|
82
|
+
/**
|
|
83
|
+
* Asks for the next hint. Does nothing at the limit, while a call is on its
|
|
84
|
+
* way, or where `offered` is false. One identity for the life of the
|
|
85
|
+
* question.
|
|
86
|
+
*/
|
|
87
|
+
ask: () => void;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* "Explain my answer", without the SDK's own button and panel: the rules, the
|
|
91
|
+
* call, the checks and the record, for a question you draw yourself.
|
|
92
|
+
*
|
|
93
|
+
* It is what `<MultipleChoice>` and the others use, so a host's own question
|
|
94
|
+
* offers help on exactly the same terms: never in `exam`, never where the
|
|
95
|
+
* item's author switched explanations off, and never an explanation whose
|
|
96
|
+
* verdict contradicts the grade the SDK computed.
|
|
97
|
+
*
|
|
98
|
+
* ```tsx
|
|
99
|
+
* const { offered, status, explanation, ask } = useAiExplanation({
|
|
100
|
+
* data, response, submitted, renderMode, outcome, ai: question.ai,
|
|
101
|
+
* });
|
|
102
|
+
* if (!offered) return null;
|
|
103
|
+
* return explanation === null ? (
|
|
104
|
+
* <button type="button" onClick={ask} aria-busy={status === 'loading'}>Explain my answer</button>
|
|
105
|
+
* ) : (
|
|
106
|
+
* <section>
|
|
107
|
+
* <p>{explanation.text}</p>
|
|
108
|
+
* <p>Written by AI. It can make mistakes.</p>
|
|
109
|
+
* </section>
|
|
110
|
+
* );
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* **Say who wrote it.** The SDK's own panel carries "Written by AI. It can make
|
|
114
|
+
* mistakes."; a page that drops that line passes a model's words off as the
|
|
115
|
+
* course's.
|
|
116
|
+
*
|
|
117
|
+
* When the answer or its grade changes, the explanation is dropped and a call
|
|
118
|
+
* still on its way is abandoned — a late explanation never lands beside an
|
|
119
|
+
* answer it was not written for — as it is when the caller unmounts.
|
|
120
|
+
*/
|
|
121
|
+
declare function useAiExplanation(input: AiExplanationInput): AiExplanationHelp;
|
|
122
|
+
/**
|
|
123
|
+
* "Get a hint", without the SDK's own button and list: the rules, the call, the
|
|
124
|
+
* checks and the record, for a question you draw yourself.
|
|
125
|
+
*
|
|
126
|
+
* It is what `<MultipleChoice>` and the others use, so a host's own question
|
|
127
|
+
* gives hints on exactly the same terms: only in `practice` before submit, up
|
|
128
|
+
* to the host's limit, never on a redacted item or a type the SDK does not hint
|
|
129
|
+
* for, and never a hint that gives the answer away — which is refused rather
|
|
130
|
+
* than shown, and not counted.
|
|
131
|
+
*
|
|
132
|
+
* ```tsx
|
|
133
|
+
* const { offered, status, hints, used, limit, ask } = useAiHints({
|
|
134
|
+
* data, response, submitted, renderMode, ai: question.ai,
|
|
135
|
+
* });
|
|
136
|
+
* ```
|
|
137
|
+
*
|
|
138
|
+
* **Say who wrote them**, as the SDK's own list does: "Written by AI. It can
|
|
139
|
+
* make mistakes."
|
|
140
|
+
*
|
|
141
|
+
* Keep the hints on screen after submit — the learner can see what they were
|
|
142
|
+
* given — while `offered` turns false. Another question starts with none.
|
|
143
|
+
*/
|
|
144
|
+
declare function useAiHints(input: AiHintsInput): AiHintsHelp;
|
|
145
|
+
|
|
146
|
+
export { type AiExplanationHelp, type AiExplanationInput, type AiHelpSituation, type AiHintsHelp, type AiHintsInput, useAiExplanation, useAiHints };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"useAiHelp.js"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
var
|
|
4
|
+
var chunkCQNKZ435_cjs = require('./chunk-CQNKZ435.cjs');
|
|
5
|
+
var chunkX4YU6BX7_cjs = require('./chunk-X4YU6BX7.cjs');
|
|
6
|
+
var chunkJH77MAON_cjs = require('./chunk-JH77MAON.cjs');
|
|
7
|
+
var chunk3SJ7TVL6_cjs = require('./chunk-3SJ7TVL6.cjs');
|
|
8
|
+
var chunk7GYAUA56_cjs = require('./chunk-7GYAUA56.cjs');
|
|
9
|
+
var chunkNZM3M5IR_cjs = require('./chunk-NZM3M5IR.cjs');
|
|
10
|
+
var chunkAPZ6JFN6_cjs = require('./chunk-APZ6JFN6.cjs');
|
|
11
11
|
var chunkAHGB5HH6_cjs = require('./chunk-AHGB5HH6.cjs');
|
|
12
12
|
var lkCore = require('@intellectif/lk-core');
|
|
13
13
|
var react = require('react');
|
|
@@ -106,7 +106,7 @@ function ActivityPreview({
|
|
|
106
106
|
const data = result.data;
|
|
107
107
|
const key = `${renderMode}:${responseKey}:${recordingKeyOf(data)}`;
|
|
108
108
|
if (marking?.kind === "failed") {
|
|
109
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
109
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkAPZ6JFN6_cjs.ActivityErrorBoundary, { activityTitle: data.title, strings: s, children: /* @__PURE__ */ jsxRuntime.jsx(Rethrow, { error: marking.error }) }, key);
|
|
110
110
|
}
|
|
111
111
|
const shared = {
|
|
112
112
|
renderMode,
|
|
@@ -133,7 +133,7 @@ function ActivityPreview({
|
|
|
133
133
|
switch (data.type) {
|
|
134
134
|
case "multiple-choice":
|
|
135
135
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
136
|
-
|
|
136
|
+
chunkX4YU6BX7_cjs.MultipleChoice,
|
|
137
137
|
{
|
|
138
138
|
data,
|
|
139
139
|
shuffleSeed: shuffleSeed ?? PREVIEW_SHUFFLE_SEED,
|
|
@@ -142,10 +142,10 @@ function ActivityPreview({
|
|
|
142
142
|
key
|
|
143
143
|
);
|
|
144
144
|
case "fill-in-the-blanks":
|
|
145
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
145
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkJH77MAON_cjs.FillInTheBlanks, { data, ...shared }, key);
|
|
146
146
|
case "gap-select":
|
|
147
147
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
148
|
-
|
|
148
|
+
chunk3SJ7TVL6_cjs.GapSelect,
|
|
149
149
|
{
|
|
150
150
|
data,
|
|
151
151
|
shuffleSeed: shuffleSeed ?? PREVIEW_SHUFFLE_SEED,
|
|
@@ -154,11 +154,11 @@ function ActivityPreview({
|
|
|
154
154
|
key
|
|
155
155
|
);
|
|
156
156
|
case "written-response":
|
|
157
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
157
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkCQNKZ435_cjs.WrittenResponse, { data, ...shared }, key);
|
|
158
158
|
case "dictation":
|
|
159
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
159
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunk7GYAUA56_cjs.Dictation, { data, ...shared }, key);
|
|
160
160
|
case "read-aloud":
|
|
161
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
161
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkNZM3M5IR_cjs.ReadAloud, { data, recordingBinding: PREVIEW_RECORDING, ...shared }, key);
|
|
162
162
|
default:
|
|
163
163
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lk-preview-unsupported", role: "note", children: s.unsupportedActivity });
|
|
164
164
|
}
|
|
@@ -232,5 +232,5 @@ function contentKeyOf(value, enclosing = /* @__PURE__ */ new Set()) {
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
exports.ActivityPreview = ActivityPreview;
|
|
235
|
-
//# sourceMappingURL=chunk-
|
|
236
|
-
//# sourceMappingURL=chunk-
|
|
235
|
+
//# sourceMappingURL=chunk-3SDCALS3.cjs.map
|
|
236
|
+
//# sourceMappingURL=chunk-3SDCALS3.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ActivityPreview/ActivityPreview.tsx"],"names":["useLkStrings","useMemo","validateDraft","evaluate","slotMediaKey","jsx","Fragment","ActivityErrorBoundary","MultipleChoice","FillInTheBlanks","GapSelect","WrittenResponse","Dictation","ReadAloud"],"mappings":"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","file":"chunk-3SDCALS3.cjs","sourcesContent":["'use client';\n\nimport {\n type ActivityType,\n type DraftNotComplete,\n evaluate,\n type ItemOutcome,\n type LearnerResponse,\n slotMediaKey,\n type ThemeTokens,\n validateDraft,\n} from '@intellectif/lk-core';\nimport { type ReactNode, useMemo } from 'react';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport type { LkStringsOverride } from '../../i18n/strings.js';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport type { ActivityRenderer } from '../ActivitySequence/index.js';\nimport { Dictation } from '../Dictation/index.js';\nimport { FillInTheBlanks } from '../FillInTheBlanks/index.js';\nimport { GapSelect } from '../GapSelect/index.js';\nimport { MultipleChoice } from '../MultipleChoice/index.js';\nimport { ReadAloud } from '../ReadAloud/index.js';\nimport type { RecordingBinding } from '../ReadAloud/ReadAloud.js';\nimport type { HtmlSanitizer, MediaBudgetBinding, RenderMode } from '../types.js';\nimport { WrittenResponse } from '../WrittenResponse/index.js';\n\n/**\n * The option-shuffle seed a preview uses when none is given. Fixed, so a\n * shuffled question holds still while its author edits it instead of\n * re-ordering on every remount.\n */\nconst PREVIEW_SHUFFLE_SEED = 'lk-activity-preview';\n\n/**\n * The slot a previewed activity stands in for. A recording's play budget is\n * keyed to it and lives only in this mount: the preview counts plays so an\n * author hears the limit a learner gets, and records none of them.\n */\nconst PREVIEW_SLOT_ID = 'preview';\n\n/** Numbers the takes a preview mints keys for, so two are never the same take. */\nlet previewTakeCount = 0;\n\n/**\n * The store a previewed read-aloud records into: it keys the take so the whole\n * flow — record, play it back, submit — runs for the author, and keeps it\n * nowhere, like every other thing a preview does not record.\n *\n * It judges nothing, deliberately. `assess` is optional precisely so this can\n * exist: an author trying a reading out has no assessor to point at, and a\n * preview that demanded one would refuse to render the most common case. The\n * default `practice` mode then shows the \"assessment is not available\" notice,\n * which is the truth about this binding rather than a failure.\n */\nconst PREVIEW_RECORDING: RecordingBinding = {\n upload: async (take) => {\n previewTakeCount += 1;\n return {\n key: `${PREVIEW_SLOT_ID}-take-${previewTakeCount}`,\n mimeType: take.mimeType,\n durationMs: take.durationMs,\n };\n },\n};\n\nexport interface ActivityPreviewProps {\n /**\n * The activity as an editor holds it right now, finished or not. It is checked\n * with `validateDraft` before anything renders — by content, on every render —\n * so a half-written question never reaches a component that assumes a valid\n * one.\n */\n draft: unknown;\n /** The mode to preview in. Defaults to `practice`. */\n renderMode?: RenderMode;\n /**\n * A learner response to show as if a learner had given it. In `practice` and\n * `exam` it seeds the answer, which stays editable; in `review` it is the\n * submitted answer, marked by `evaluate()` unless `outcome` is given.\n */\n response?: LearnerResponse;\n /**\n * The outcome `review` marks the response with, instead of the one\n * `evaluate()` computes — which is how to preview a returned grade on a written\n * response, since the SDK cannot score one. Ignored in other modes.\n */\n outcome?: ItemOutcome;\n /**\n * Rendered in place of the activity while the draft is not complete, with the\n * issues `validateDraft` found — so an editor can list them in its own words.\n * The issue messages are English, and a preview that rendered them would put\n * untranslatable text on screen. Without it, the preview shows the\n * `previewIncomplete` or `previewInvalid` string.\n */\n fallback?: (result: DraftNotComplete) => ReactNode;\n /**\n * Renderers for activity types beyond the built-ins, keyed by `type`, as on\n * `<ActivitySequence>`. An entry receives the shared prop contract and\n * nothing else, so — like the shuffle seed below — the in-memory recording\n * binding a previewed read-aloud is given does not reach one. Nor does the\n * `question` a sequence hands a renderer: a preview is one question with no\n * set around it, so there is no slot to report to and nothing to page away\n * from. A renderer that uses it must treat it as optional, which its type\n * already says.\n */\n renderers?: Readonly<Record<string, ActivityRenderer>>;\n /**\n * Seeds `<MultipleChoice>`'s option shuffle. Defaults to a fixed seed, so the\n * order holds while options are edited; adding or removing one deals them\n * again, because the shuffle orders by position. A `renderers` entry is given\n * no seed, as on `<ActivitySequence>`.\n */\n shuffleSeed?: string;\n /** Renders author-supplied rich text. See `HtmlSanitizer`. */\n sanitizeHtml?: HtmlSanitizer;\n /** Overrides the SDK's chrome text, for the preview's notice and the activity alike. */\n strings?: LkStringsOverride;\n /** Per-instance token overrides, passed to the activity. */\n theme?: Partial<ThemeTokens>;\n /** BCP 47 tag of the interface language, passed to the activity. */\n locale?: string;\n}\n\ntype Marking = { kind: 'marked'; outcome: ItemOutcome } | { kind: 'failed'; error: Error };\n\n/**\n * Renders an activity from an editor's draft, in any render mode, with a\n * simulated learner response — and renders a notice instead while the draft is\n * not an activity yet.\n *\n * Nothing it renders is recorded: no callback is wired, so a practice attempt in\n * a preview scores locally and goes nowhere, and a recording's play limit is\n * counted in memory only.\n *\n * A draft with no string `type`, a type nobody registered, and a `response` of a\n * different type from the draft are wiring errors, and throw.\n */\nexport function ActivityPreview({\n draft,\n renderMode = 'practice',\n response,\n outcome,\n fallback,\n renderers,\n shuffleSeed,\n sanitizeHtml,\n strings,\n theme,\n locale,\n}: ActivityPreviewProps) {\n const s = useLkStrings(strings);\n const type = isRecord(draft) && typeof draft.type === 'string' ? draft.type : undefined;\n\n // Validated on the draft's CONTENT, not its identity. The activity components\n // restart whenever `data` changes identity, and an editor that rebuilds its\n // payload on every change would otherwise restart the question an author is\n // trying out each time anything else on the page was edited. The key is read\n // on every render, so a draft changed in place is checked again too.\n const contentKey = contentKeyOf(draft);\n // biome-ignore lint/correctness/useExhaustiveDependencies: the draft's content (contentKey) is the trigger, not its identity\n const result = useMemo(\n () => (type === undefined ? undefined : validateDraft(type as ActivityType, draft)),\n [type, contentKey],\n );\n const activity = result?.status === 'complete' ? result.data : undefined;\n const responseFits = response === undefined || response.type === type;\n const responseKey = response === undefined ? 'none' : contentKeyOf(response);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: the response's content (responseKey) is the trigger, not its identity\n const marking = useMemo((): Marking | undefined => {\n if (renderMode !== 'review' || activity === undefined) {\n return undefined;\n }\n if (outcome !== undefined) {\n return { kind: 'marked', outcome };\n }\n if (response === undefined || !responseFits) {\n return undefined;\n }\n try {\n return { kind: 'marked', outcome: evaluate(activity, response) };\n } catch (thrown) {\n // Scored here, during this component's own render, where no error boundary\n // inside the activity can catch it — so it is handed to one below.\n return {\n kind: 'failed',\n error: thrown instanceof Error ? thrown : new Error(String(thrown)),\n };\n }\n }, [renderMode, activity, outcome, responseKey, responseFits]);\n\n const activityId = activity?.id;\n const mediaBudget = useMemo<MediaBudgetBinding>(\n () => ({\n key: slotMediaKey(PREVIEW_SLOT_ID),\n slotId: PREVIEW_SLOT_ID,\n index: 0,\n ...(activityId !== undefined ? { activityId } : {}),\n }),\n [activityId],\n );\n\n // All hooks are called before these throws, so hook order stays stable.\n if (type === undefined || result === undefined) {\n throw new Error(\n '<ActivityPreview> needs a draft object with a string `type`, to know which activity to check and render.',\n );\n }\n // Checked against the draft's own type, whatever state the draft is in. A\n // mis-wired response is a wiring error however unfinished the draft is, and\n // waiting for the draft to be complete would throw on the very edit that\n // completes it.\n if (response !== undefined && !responseFits) {\n throw new Error(\n `<ActivityPreview> was given a \"${response.type}\" response for a \"${type}\" activity.`,\n );\n }\n if (result.status !== 'complete') {\n if (fallback !== undefined) {\n return <>{fallback(result)}</>;\n }\n return (\n <div\n className=\"lk-preview-unavailable\"\n role=\"note\"\n data-status={result.status}\n {...(locale !== undefined ? { lang: locale } : {})}\n >\n {result.status === 'invalid' ? s.previewInvalid : s.previewIncomplete}\n </div>\n );\n }\n const data = result.data;\n\n // A different simulated response, or a different mode, mounts the activity\n // afresh, play count included: a seeded answer is read at mount, and a practice\n // attempt the author submitted must not carry over into an exam preview. So\n // does a different recording or playback policy, which is a different listening\n // budget — the transport reads its count once, when it mounts.\n const key = `${renderMode}:${responseKey}:${recordingKeyOf(data)}`;\n\n if (marking?.kind === 'failed') {\n // The response is one the scorer cannot read, or a registered scorer threw.\n // Shown as the activity's error fallback, as a render failure would be.\n return (\n <ActivityErrorBoundary key={key} activityTitle={data.title} strings={s}>\n <Rethrow error={marking.error} />\n </ActivityErrorBoundary>\n );\n }\n\n const shared = {\n renderMode,\n // Without a binding a budgeted recording counts nothing in `practice`, and in\n // `exam` the media component refuses to render at all — rightly, on a real\n // paper, where nothing would persist the count. A preview persists nothing by\n // design, so it keeps the count in memory and says so.\n mediaBudget,\n ...(sanitizeHtml !== undefined ? { sanitizeHtml } : {}),\n ...(strings !== undefined ? { strings } : {}),\n ...(theme !== undefined ? { theme } : {}),\n ...(locale !== undefined ? { locale } : {}),\n // Seeded rather than controlled, in every mode, exactly as `<ActivitySequence>`\n // hands a restored answer to its slots — so a renderer written for the\n // sequence reads it here too. `review` is read-only either way, and `key`\n // above re-seeds whenever the response changes.\n ...(response !== undefined ? { defaultValue: response } : {}),\n ...(marking !== undefined ? { outcome: marking.outcome } : {}),\n };\n\n const Custom = renderers?.[data.type];\n if (Custom !== undefined) {\n return <Custom key={key} data={data} {...shared} />;\n }\n switch (data.type) {\n case 'multiple-choice':\n return (\n <MultipleChoice\n key={key}\n data={data}\n shuffleSeed={shuffleSeed ?? PREVIEW_SHUFFLE_SEED}\n {...shared}\n />\n );\n case 'fill-in-the-blanks':\n return <FillInTheBlanks key={key} data={data} {...shared} />;\n case 'gap-select':\n return (\n <GapSelect\n key={key}\n data={data}\n shuffleSeed={shuffleSeed ?? PREVIEW_SHUFFLE_SEED}\n {...shared}\n />\n );\n case 'written-response':\n return <WrittenResponse key={key} data={data} {...shared} />;\n case 'dictation':\n return <Dictation key={key} data={data} {...shared} />;\n case 'read-aloud':\n return <ReadAloud key={key} data={data} recordingBinding={PREVIEW_RECORDING} {...shared} />;\n default:\n return (\n <div className=\"lk-preview-unsupported\" role=\"note\">\n {s.unsupportedActivity}\n </div>\n );\n }\n}\n\n/** Throws `error` while rendering, so the error boundary around it shows its fallback. */\nfunction Rethrow({ error }: { error: Error }): never {\n throw error;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * The recording and its playback policy — not its description, which changes\n * no budget — plus the slower file a dictation or a read-aloud carries, which\n * follows the same policy and must remount when it is swapped for another.\n */\nfunction recordingKeyOf(data: unknown): string {\n const media = isRecord(data) ? data.media : undefined;\n const slowMedia = isRecord(data) ? data.slowMedia : undefined;\n const slowUrl = isRecord(slowMedia) ? slowMedia.url : undefined;\n if (!isRecord(media) && slowUrl === undefined) {\n return '';\n }\n return contentKeyOf([\n isRecord(media) ? media.type : undefined,\n isRecord(media) ? media.url : undefined,\n isRecord(media) ? media.playback : undefined,\n slowUrl,\n ]);\n}\n\nconst identities = new WeakMap<object, number>();\nlet lastIdentity = 0;\n\n/** A key for a value compared by identity: the same object always gets the same one. */\nfunction identityKeyOf(value: object): string {\n let identity = identities.get(value);\n if (identity === undefined) {\n lastIdentity += 1;\n identity = lastIdentity;\n identities.set(value, identity);\n }\n return `#${identity}`;\n}\n\n/**\n * A string that changes exactly when a value's content does.\n *\n * Object keys are sorted, because key order is not content: a draft saved to a\n * JSON column comes back with its keys reordered. Every value is written in a\n * form no other value shares — JSON would write a non-finite number as `null`\n * and drop `undefined`, and those decide different issues. What JSON does not\n * describe — an instance of a class, a function, a reference back to an\n * enclosing object — is compared by identity, and a symbol by its description.\n */\nfunction contentKeyOf(value: unknown, enclosing: Set<object> = new Set()): string {\n switch (typeof value) {\n case 'string':\n return JSON.stringify(value);\n case 'number':\n // `String` keeps NaN, the infinities and every other number apart; only -0\n // needs saying.\n return Object.is(value, -0) ? '-0' : String(value);\n case 'bigint':\n return `${value}n`;\n case 'boolean':\n case 'undefined':\n return String(value);\n case 'symbol':\n return `Symbol(${JSON.stringify(value.description ?? null)})`;\n case 'function':\n return identityKeyOf(value);\n }\n // Every other `typeof` has returned above: this is `null`, or an object.\n if (value === null || typeof value !== 'object') {\n return 'null';\n }\n const prototype = Object.getPrototypeOf(value);\n const plain = Array.isArray(value) || prototype === Object.prototype || prototype === null;\n if (!plain || enclosing.has(value)) {\n return identityKeyOf(value);\n }\n enclosing.add(value);\n try {\n if (Array.isArray(value)) {\n return `[${Array.from(value, (item) => contentKeyOf(item, enclosing)).join(',')}]`;\n }\n const record = value as Record<string, unknown>;\n const fields = Object.keys(record)\n .sort()\n .map((field) => `${JSON.stringify(field)}:${contentKeyOf(record[field], enclosing)}`);\n return `{${fields.join(',')}}`;\n } finally {\n enclosing.delete(value);\n }\n}\n"]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var chunkNWGQ7JML_cjs = require('./chunk-NWGQ7JML.cjs');
|
|
5
5
|
var chunkZDYNAWDZ_cjs = require('./chunk-ZDYNAWDZ.cjs');
|
|
6
6
|
var chunkBRPEB43C_cjs = require('./chunk-BRPEB43C.cjs');
|
|
7
|
-
var
|
|
7
|
+
var chunkAPZ6JFN6_cjs = require('./chunk-APZ6JFN6.cjs');
|
|
8
|
+
var chunkEANGKAKG_cjs = require('./chunk-EANGKAKG.cjs');
|
|
8
9
|
var chunkL7WY5YFQ_cjs = require('./chunk-L7WY5YFQ.cjs');
|
|
9
10
|
var chunkXOR4MPEN_cjs = require('./chunk-XOR4MPEN.cjs');
|
|
10
11
|
var chunkAHGB5HH6_cjs = require('./chunk-AHGB5HH6.cjs');
|
|
@@ -78,7 +79,7 @@ function GapSelect({
|
|
|
78
79
|
const s = chunkAHGB5HH6_cjs.useLkStrings(strings);
|
|
79
80
|
const ai = chunkZDYNAWDZ_cjs.useLearnerAi(aiProp);
|
|
80
81
|
const devError = react.useMemo(() => {
|
|
81
|
-
if (!
|
|
82
|
+
if (!chunkEANGKAKG_cjs.isDevelopment()) {
|
|
82
83
|
return null;
|
|
83
84
|
}
|
|
84
85
|
if (data.redacted === true) {
|
|
@@ -128,7 +129,7 @@ function GapSelect({
|
|
|
128
129
|
const sessionIdRef = react.useRef(null);
|
|
129
130
|
const choicesByGap = react.useMemo(() => {
|
|
130
131
|
const map = /* @__PURE__ */ new Map();
|
|
131
|
-
const seedSource = shuffleSeed ?? (sessionIdRef.current ??=
|
|
132
|
+
const seedSource = shuffleSeed ?? (sessionIdRef.current ??= chunkEANGKAKG_cjs.randomSessionId());
|
|
132
133
|
for (const gap of data.gaps) {
|
|
133
134
|
const choices = choicesFor(data.banks, gap);
|
|
134
135
|
map.set(
|
|
@@ -183,9 +184,9 @@ function GapSelect({
|
|
|
183
184
|
complete();
|
|
184
185
|
const timeSpent = getTimeSpent();
|
|
185
186
|
const xapiStatement = lkCore.xAPIBuilder.buildAnsweredStatement({
|
|
186
|
-
actor:
|
|
187
|
+
actor: chunkEANGKAKG_cjs.ANONYMOUS_ACTOR,
|
|
187
188
|
object: {
|
|
188
|
-
id:
|
|
189
|
+
id: chunkEANGKAKG_cjs.objectIdFor(data.id),
|
|
189
190
|
name: { [data.locale ?? "en-US"]: data.title },
|
|
190
191
|
...lkCore.xapiDefinitionFor(data)
|
|
191
192
|
},
|
|
@@ -299,7 +300,7 @@ function GapSelect({
|
|
|
299
300
|
] }, seg.id);
|
|
300
301
|
}) }),
|
|
301
302
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
302
|
-
|
|
303
|
+
chunkNWGQ7JML_cjs.AiHints,
|
|
303
304
|
{
|
|
304
305
|
ai,
|
|
305
306
|
data,
|
|
@@ -326,7 +327,7 @@ function GapSelect({
|
|
|
326
327
|
) : null,
|
|
327
328
|
/* @__PURE__ */ jsxRuntime.jsx(chunkBRPEB43C_cjs.FeedbackRegion, { id: `${data.id}-feedback`, children: isReview ? reviewSummary : summary }),
|
|
328
329
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
329
|
-
|
|
330
|
+
chunkNWGQ7JML_cjs.AiExplanation,
|
|
330
331
|
{
|
|
331
332
|
ai,
|
|
332
333
|
data,
|
|
@@ -345,9 +346,9 @@ function GapSelect({
|
|
|
345
346
|
}
|
|
346
347
|
function GapSelect2(props) {
|
|
347
348
|
const strings = chunkAHGB5HH6_cjs.useLkStrings(props.strings);
|
|
348
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
349
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkAPZ6JFN6_cjs.ActivityErrorBoundary, { activityTitle: props.data?.title, strings, children: /* @__PURE__ */ jsxRuntime.jsx(GapSelect, { ...props }) });
|
|
349
350
|
}
|
|
350
351
|
|
|
351
352
|
exports.GapSelect = GapSelect2;
|
|
352
|
-
//# sourceMappingURL=chunk-
|
|
353
|
-
//# sourceMappingURL=chunk-
|
|
353
|
+
//# sourceMappingURL=chunk-3SJ7TVL6.cjs.map
|
|
354
|
+
//# sourceMappingURL=chunk-3SJ7TVL6.cjs.map
|