@intellectif/lk-react 18.0.0 → 19.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +4 -2
  3. package/dist/{WrittenResponse-B4kY-U0v.d.ts → WrittenResponse-CeHY5u3W.d.ts} +1 -1
  4. package/dist/{WrittenResponse-Dl3zah22.d.cts → WrittenResponse-DT_Yqbq1.d.cts} +1 -1
  5. package/dist/ai/useAiHelp.cjs +20 -0
  6. package/dist/ai/useAiHelp.cjs.map +1 -0
  7. package/dist/ai/useAiHelp.d.cts +146 -0
  8. package/dist/ai/useAiHelp.d.ts +146 -0
  9. package/dist/ai/useAiHelp.js +7 -0
  10. package/dist/ai/useAiHelp.js.map +1 -0
  11. package/dist/{chunk-XEGBGKHV.cjs → chunk-2X2NZF46.cjs} +206 -52
  12. package/dist/chunk-2X2NZF46.cjs.map +1 -0
  13. package/dist/{chunk-V5BTVGPA.js → chunk-4NERCBOW.js} +4 -3
  14. package/dist/chunk-4NERCBOW.js.map +1 -0
  15. package/dist/{chunk-Z5XY66PO.cjs → chunk-55W2472T.cjs} +16 -16
  16. package/dist/chunk-55W2472T.cjs.map +1 -0
  17. package/dist/{chunk-HZMILQYO.js → chunk-5QCZZBOX.js} +5 -4
  18. package/dist/chunk-5QCZZBOX.js.map +1 -0
  19. package/dist/{chunk-H3IRVX7J.js → chunk-7NNMDZUU.js} +9 -9
  20. package/dist/chunk-7NNMDZUU.js.map +1 -0
  21. package/dist/{chunk-BAKLJKFZ.cjs → chunk-APZ6JFN6.cjs} +4 -29
  22. package/dist/chunk-APZ6JFN6.cjs.map +1 -0
  23. package/dist/{chunk-H63O5HPW.cjs → chunk-CQNKZ435.cjs} +8 -7
  24. package/dist/chunk-CQNKZ435.cjs.map +1 -0
  25. package/dist/chunk-EANGKAKG.cjs +32 -0
  26. package/dist/chunk-EANGKAKG.cjs.map +1 -0
  27. package/dist/{chunk-YTGRXD23.js → chunk-FEOFX4QW.js} +4 -25
  28. package/dist/chunk-FEOFX4QW.js.map +1 -0
  29. package/dist/{chunk-QJW3K6JN.js → chunk-G7AGM2Y7.js} +4 -3
  30. package/dist/chunk-G7AGM2Y7.js.map +1 -0
  31. package/dist/chunk-JVWLXVYG.cjs +27 -0
  32. package/dist/chunk-JVWLXVYG.cjs.map +1 -0
  33. package/dist/chunk-KGDGYRBL.cjs +117 -0
  34. package/dist/chunk-KGDGYRBL.cjs.map +1 -0
  35. package/dist/{chunk-UYQBEPNZ.js → chunk-KOFA54C7.js} +15 -8
  36. package/dist/chunk-KOFA54C7.js.map +1 -0
  37. package/dist/{chunk-UQIRDE52.cjs → chunk-LX2X4KC7.cjs} +6 -5
  38. package/dist/chunk-LX2X4KC7.cjs.map +1 -0
  39. package/dist/{chunk-RZ52SEYV.cjs → chunk-NGFTSRKO.cjs} +10 -9
  40. package/dist/chunk-NGFTSRKO.cjs.map +1 -0
  41. package/dist/{chunk-ALH6TUX2.js → chunk-NLTU5JFD.js} +5 -4
  42. package/dist/chunk-NLTU5JFD.js.map +1 -0
  43. package/dist/{chunk-6RRFH7C4.cjs → chunk-NZM3M5IR.cjs} +39 -54
  44. package/dist/chunk-NZM3M5IR.cjs.map +1 -0
  45. package/dist/{chunk-KWKB74KL.js → chunk-OWV7VRMQ.js} +198 -44
  46. package/dist/chunk-OWV7VRMQ.js.map +1 -0
  47. package/dist/chunk-PPFVTXGJ.js +27 -0
  48. package/dist/chunk-PPFVTXGJ.js.map +1 -0
  49. package/dist/chunk-QRT2W5N5.js +114 -0
  50. package/dist/chunk-QRT2W5N5.js.map +1 -0
  51. package/dist/{chunk-QHK6PTUR.js → chunk-SEQTGOOU.js} +5 -4
  52. package/dist/chunk-SEQTGOOU.js.map +1 -0
  53. package/dist/chunk-SXU26SFO.cjs +253 -0
  54. package/dist/chunk-SXU26SFO.cjs.map +1 -0
  55. package/dist/chunk-TEAVPUMX.js +22 -0
  56. package/dist/chunk-TEAVPUMX.js.map +1 -0
  57. package/dist/{chunk-KP57YCYQ.js → chunk-UXV3V5NH.js} +23 -35
  58. package/dist/chunk-UXV3V5NH.js.map +1 -0
  59. package/dist/{chunk-3TJSFPO4.cjs → chunk-WKA6FUR4.cjs} +26 -19
  60. package/dist/chunk-WKA6FUR4.cjs.map +1 -0
  61. package/dist/{chunk-QA42VDTA.cjs → chunk-XAIJNGTU.cjs} +12 -11
  62. package/dist/chunk-XAIJNGTU.cjs.map +1 -0
  63. package/dist/{chunk-6DTW5T3Z.js → chunk-XHF376ES.js} +5 -4
  64. package/dist/chunk-XHF376ES.js.map +1 -0
  65. package/dist/{chunk-TI23GIFM.cjs → chunk-XUQ4KRTT.cjs} +12 -11
  66. package/dist/chunk-XUQ4KRTT.cjs.map +1 -0
  67. package/dist/chunk-YIYMBCPV.js +250 -0
  68. package/dist/chunk-YIYMBCPV.js.map +1 -0
  69. package/dist/{chunk-JKMBFHER.cjs → chunk-ZME5FMED.cjs} +12 -11
  70. package/dist/chunk-ZME5FMED.cjs.map +1 -0
  71. package/dist/components/ActivityPreview.cjs +14 -11
  72. package/dist/components/ActivityPreview.d.cts +7 -3
  73. package/dist/components/ActivityPreview.d.ts +7 -3
  74. package/dist/components/ActivityPreview.js +13 -10
  75. package/dist/components/ActivitySequence.cjs +14 -11
  76. package/dist/components/ActivitySequence.d.cts +75 -11
  77. package/dist/components/ActivitySequence.d.ts +75 -11
  78. package/dist/components/ActivitySequence.js +13 -10
  79. package/dist/components/Dictation.cjs +7 -4
  80. package/dist/components/Dictation.d.cts +1 -1
  81. package/dist/components/Dictation.d.ts +1 -1
  82. package/dist/components/Dictation.js +6 -3
  83. package/dist/components/FillInTheBlanks.cjs +7 -4
  84. package/dist/components/FillInTheBlanks.d.cts +1 -1
  85. package/dist/components/FillInTheBlanks.d.ts +1 -1
  86. package/dist/components/FillInTheBlanks.js +6 -3
  87. package/dist/components/GapSelect.cjs +7 -4
  88. package/dist/components/GapSelect.d.cts +1 -1
  89. package/dist/components/GapSelect.d.ts +1 -1
  90. package/dist/components/GapSelect.js +6 -3
  91. package/dist/components/InteractiveVideo.cjs +14 -11
  92. package/dist/components/InteractiveVideo.d.cts +2 -2
  93. package/dist/components/InteractiveVideo.d.ts +2 -2
  94. package/dist/components/InteractiveVideo.js +12 -9
  95. package/dist/components/MultipleChoice.cjs +7 -4
  96. package/dist/components/MultipleChoice.d.cts +1 -1
  97. package/dist/components/MultipleChoice.d.ts +1 -1
  98. package/dist/components/MultipleChoice.js +6 -3
  99. package/dist/components/PronunciationFeedback.cjs +4 -3
  100. package/dist/components/PronunciationFeedback.d.cts +1 -1
  101. package/dist/components/PronunciationFeedback.d.ts +1 -1
  102. package/dist/components/PronunciationFeedback.js +3 -2
  103. package/dist/components/ReadAloud.cjs +6 -4
  104. package/dist/components/ReadAloud.d.cts +2 -2
  105. package/dist/components/ReadAloud.d.ts +2 -2
  106. package/dist/components/ReadAloud.js +5 -3
  107. package/dist/components/StimulusPanel.d.cts +1 -1
  108. package/dist/components/StimulusPanel.d.ts +1 -1
  109. package/dist/components/WrittenResponse.cjs +4 -3
  110. package/dist/components/WrittenResponse.d.cts +3 -3
  111. package/dist/components/WrittenResponse.d.ts +3 -3
  112. package/dist/components/WrittenResponse.js +3 -2
  113. package/dist/i18n/LkIntlProvider.d.cts +2 -2
  114. package/dist/i18n/LkIntlProvider.d.ts +2 -2
  115. package/dist/index.cjs +34 -23
  116. package/dist/index.cjs.map +1 -1
  117. package/dist/index.d.cts +4 -3
  118. package/dist/index.d.ts +4 -3
  119. package/dist/index.js +15 -12
  120. package/dist/index.js.map +1 -1
  121. package/dist/{strings-PCYKWCwd.d.ts → types-BY-4S5Rd.d.ts} +761 -761
  122. package/dist/{strings-BOPKhnS-.d.cts → types-DtPYG1hh.d.cts} +761 -761
  123. package/package.json +13 -3
  124. package/dist/chunk-3TJSFPO4.cjs.map +0 -1
  125. package/dist/chunk-5F4Q6JT7.cjs +0 -289
  126. package/dist/chunk-5F4Q6JT7.cjs.map +0 -1
  127. package/dist/chunk-6DTW5T3Z.js.map +0 -1
  128. package/dist/chunk-6RRFH7C4.cjs.map +0 -1
  129. package/dist/chunk-ALH6TUX2.js.map +0 -1
  130. package/dist/chunk-BAKLJKFZ.cjs.map +0 -1
  131. package/dist/chunk-GR33G5QM.js +0 -286
  132. package/dist/chunk-GR33G5QM.js.map +0 -1
  133. package/dist/chunk-H3IRVX7J.js.map +0 -1
  134. package/dist/chunk-H63O5HPW.cjs.map +0 -1
  135. package/dist/chunk-HZMILQYO.js.map +0 -1
  136. package/dist/chunk-JKMBFHER.cjs.map +0 -1
  137. package/dist/chunk-KP57YCYQ.js.map +0 -1
  138. package/dist/chunk-KWKB74KL.js.map +0 -1
  139. package/dist/chunk-QA42VDTA.cjs.map +0 -1
  140. package/dist/chunk-QHK6PTUR.js.map +0 -1
  141. package/dist/chunk-QJW3K6JN.js.map +0 -1
  142. package/dist/chunk-RZ52SEYV.cjs.map +0 -1
  143. package/dist/chunk-TI23GIFM.cjs.map +0 -1
  144. package/dist/chunk-UQIRDE52.cjs.map +0 -1
  145. package/dist/chunk-UYQBEPNZ.js.map +0 -1
  146. package/dist/chunk-V5BTVGPA.js.map +0 -1
  147. package/dist/chunk-XEGBGKHV.cjs.map +0 -1
  148. package/dist/chunk-YTGRXD23.js.map +0 -1
  149. package/dist/chunk-Z5XY66PO.cjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,79 @@
1
1
  # @intellectif/lk-react
2
2
 
3
+ ## 19.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - d576382: AI help now reports what it cost and what was refused.
8
+
9
+ - **`ai-hint-shown` and `ai-explanation-shown` carry `usage`** when your port sent it, beside the
10
+ `provenance` they already carried. What a learner was shown and what it cost are then one record,
11
+ keyed to the attempt — no second channel to reconcile.
12
+ - **`ai-help-refused` is new**, from the same one implementation the components and the hooks share
13
+ (`useAiHints`, `useAiExplanation`). It fires where a learner is told help is not available, with
14
+ `feature` (`hint` or `explanation`), `reason` (`reveals-answer`, `contradicts-grade`, `malformed`,
15
+ `empty`, `too-long`) and, for a hint, `hintNumber`. **The text is never carried:** a hint refused
16
+ for revealing the answer contains the answer, and interactions are logged.
17
+
18
+ A host that keeps every interaction will start seeing this kind. It is additive — nothing that
19
+ was emitted before has changed — but a `switch` over `InteractionKind` with no default will not
20
+ know it.
21
+
22
+ **Major** because the peer range moves to `@intellectif/lk-core@^0.19.0`, whose `ai-check` entry
23
+ point and `AiTextResult.usage` come with it. No React API was removed or renamed, and a host that
24
+ passes no AI port sees nothing new.
25
+
26
+ See `docs/ai.md`.
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies [d576382]
31
+ - @intellectif/lk-core@0.19.0
32
+
33
+ ## 18.1.0
34
+
35
+ ### Minor Changes
36
+
37
+ - 50c3285: A question a host draws inside `<ActivitySequence>` gets everything one the SDK draws has, and the AI
38
+ rules are available without the SDK's buttons.
39
+
40
+ - **`renderers` overrides get a `question` prop** (`SequenceQuestion`), the pager's half of the
41
+ contract, as `renderQuestion` gave one to `<InteractiveVideo>` in 16.1.0:
42
+ - `active` — whether this is the question on screen. **Stop the microphone, any timer and any
43
+ speech when it turns `false`:** the pager pauses the media inside the question and stops the
44
+ recorders the SDK started, but cannot reach one it did not create;
45
+ - `setPending(true/false)` — work of your own is on its way (an upload, a save, a judgement).
46
+ `onFinished` and `onComplete` wait for it, so a set is never reported a moment before the last
47
+ answer settles. Call it with `false` however the work ends, a failure included;
48
+ - `portalContainer` — a node inside this question's own pane. Portal a popover into it rather than
49
+ into `document.body`, and it is hidden when the learner pages away and painted with the question
50
+ in fullscreen;
51
+ - `clear()` — the learner withdrew their answer: the slot holds nothing again, and the set is
52
+ unfinished until it is answered again;
53
+ - `emit(type, payload?)` — an interaction, with this question's `activityId` and the time filled
54
+ in;
55
+ - `slot` — the identity to store the answer against, the same object `onSubmit` is handed.
56
+ - **Every call keeps one identity for the life of the set**, `onSubmit` and `onComplete` included, so
57
+ a renderer may hold them in an effect and a host that re-renders on every keystroke does not re-run
58
+ it.
59
+ - **New: `useAiHints` and `useAiExplanation`** (`@intellectif/lk-react/ai/useAiHelp`, also on the
60
+ root) — AI help for a question you draw yourself: the same rules, facts, checks and records the
61
+ bundled components use, with your own markup. `ask` does nothing where the rules say no, so a page
62
+ drawing its own button reaches no model in an `exam`. The SDK's own components are built on these
63
+ hooks, so there is one implementation of the rules and not two.
64
+ - **The paper decides the mode.** A question inside `<ActivitySequence>` or `<InteractiveVideo>` now
65
+ knows the mode it is being delivered in, so the hooks refuse help on an `exam` paper even when the
66
+ host does not pass `renderMode` to them, or passes `practice` — which a component that defaults it
67
+ can do inside an exam. Without that, a host's own question under an `LkAiProvider` could reach a
68
+ model on a paper of record.
69
+
70
+ `ActivityRenderer` is now `ComponentType<ActivityProps & { question?: SequenceQuestion }>`. A
71
+ renderer written as `ComponentType<ActivityProps>` still fits and simply ignores the new prop; only
72
+ assigning an `ActivityRenderer` _back_ to a `ComponentType<ActivityProps>` variable needs a widened
73
+ annotation.
74
+
75
+ See `docs/ai.md` and the `renderers` documentation on `<ActivitySequence>`.
76
+
3
77
  ## 18.0.0
4
78
 
5
79
  ### Patch Changes
package/README.md CHANGED
@@ -73,7 +73,7 @@ export function Demo() {
73
73
  - **`<PronunciationFeedback>`** — the marks on their own, for a review screen that has evidence but no activity: the graded dimensions (accuracy, fluency, completeness, prosody — one the engine did not measure reads "not assessed", **never 0%**), and every word marked `correct` / `mispronounced` / `omitted` / `inserted` with a hidden sentence per word for screen readers and a glyph plus a text decoration for everyone else. A word opens onto its syllables, its sounds, what was heard instead, and a button that plays just that word. Evidence it cannot trust is checked before it is aligned, so in production a malformed assessment costs the word list — never the grade beside it.
74
74
  - **`<WrittenResponse>`** — free-text writing with a live word counter and bounds messaging, graded **asynchronously**: it emits an ungraded submission (never a fake zero) and a SUBMITTED-verb xAPI statement, and renders a returned `GradeRecord` in `review` mode with per-criterion scores and inline corrections.
75
75
  - **`<InteractiveVideo>`** — a video that stops at each quiz and asks its questions over itself, then plays on. **A formative tool**, for lessons and practice — deliver official summative exams with `<ActivitySequence>`. One player: a progress bar that marks every quiz and previews the caption under the pointer, speed, volume, captions drawn from WebVTT it parses itself (through a `captionsLoader` when the file needs the learner's credentials) — in one language or two at once, a second line under the first for a learner matching what they hear to what it means — a contents and transcript panel, picture-in-picture, fullscreen and a keyboard map with a shortcut sheet. A quiz covers the video and grows the player rather than scrolling inside it, every question of an opened quiz stays mounted so a rewind loses no answer, the controls are `inert` while it is open, and a **required** quiz holds both playback and seeking until it is answered. Answers report with the slot the question sits in and the quiz it belongs to, and `onProgress` gives a resume point that is never past an unanswered required quiz. A host with renderers of its own draws any question itself through `renderQuestion` — the video still counts it, tells it when it leaves the screen so it can stop its microphone, and holds Finish while its answer is still on its way. See the [interactive video guide](https://github.com/intellectif/learning-kit/blob/main/docs/interactive-video.md).
76
- - **AI help for learners** — "Explain my answer" after grading and "Get a hint" before submit, in `<MultipleChoice>`, `<FillInTheBlanks>`, `<GapSelect>` and `<Dictation>` (explanations only), through ports **you** supply with `<LkAiProvider>` or an `ai` prop (also on `<ActivitySequence>` and `<InteractiveVideo>`). The SDK never calls a model or holds a key: it builds the facts your model is given, refuses an explanation that contradicts the grade and a hint that contains the answer, marks everything a model wrote, and drops a late answer. Never in `exam`. See the [AI guide](https://github.com/intellectif/learning-kit/blob/main/docs/ai.md).
76
+ - **AI help for learners** — "Explain my answer" after grading and "Get a hint" before submit, in `<MultipleChoice>`, `<FillInTheBlanks>`, `<GapSelect>` and `<Dictation>` (explanations only), through ports **you** supply with `<LkAiProvider>` or an `ai` prop (also on `<ActivitySequence>` and `<InteractiveVideo>`). The SDK never calls a model or holds a key: it builds the facts your model is given, refuses an explanation that contradicts the grade and a hint that contains the answer, marks everything a model wrote, and drops a late answer. Never in `exam`. What was shown reaches `onInteraction` with the model that wrote it and what the call cost; what was refused reaches it with the reason, and never the text. See the [AI guide](https://github.com/intellectif/learning-kit/blob/main/docs/ai.md).
77
77
  - **`<ActivitySequence>`** — in-place "question set" pager (Previous/Next, "Question X of N", no scrolling, focus-managed). Accepts item groups and keeps their stimulus beside every question; `onSubmit` reports every raw answer with its `slotId`, which is the only response channel an `exam` sequence has. `onFinished` is the completion signal for a set that mixes scored and deferred-graded items, where `onComplete` can never fire. A read-aloud slot takes its storage from `recordingBinding`, whose methods are told which slot a take belongs to, and a `review` its per-word marks from `assessments`, keyed by `slotId` — there is a [worked example](https://github.com/intellectif/learning-kit/blob/main/docs/speech-assessment.md#in-a-question-set).
78
78
  - **Resume and review a whole attempt** — `<ActivitySequence>` takes `defaultIndex`, `responses` and `submittedSlotIds` (read at mount; remount with a `key` to show a different attempt), so an interrupted paper reopens on the right question with the right answers and already-committed questions still committed. `onIndexChange` reports every position the pager lands on, including a clamp it had to apply. Pass `renderMode="review"` plus server-computed `outcomes` to render a finished attempt read-only. Pairs with `serializeAttemptState` / `restoreAttemptState` in `lk-core`.
79
79
  - **`renderMode`** — `practice` (default: the component scores locally and reveals correctness), `exam` (never scores, never reveals; submit emits the raw response for the server to grade), `review` (read-only, marks correctness only from an `outcome` you supply). This is the single switch that takes grading off the client.
@@ -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 './strings-PCYKWCwd.js';
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 './strings-BOPKhnS-.cjs';
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 chunkSXU26SFO_cjs = require('../chunk-SXU26SFO.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 chunkSXU26SFO_cjs.useAiExplanation; }
14
+ });
15
+ Object.defineProperty(exports, "useAiHints", {
16
+ enumerable: true,
17
+ get: function () { return chunkSXU26SFO_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,7 @@
1
+ 'use client';
2
+ export { useAiExplanation, useAiHints } from '../chunk-YIYMBCPV.js';
3
+ import '../chunk-YNDKAYW3.js';
4
+ import '../chunk-TEAVPUMX.js';
5
+ import '../chunk-PPFVTXGJ.js';
6
+ //# sourceMappingURL=useAiHelp.js.map
7
+ //# sourceMappingURL=useAiHelp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"useAiHelp.js"}