@intellectif/lk-react 21.0.0 → 22.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 (144) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +4 -4
  3. package/dist/{WrittenResponse-BVPmrmQL.d.cts → WrittenResponse-CgRrjM4S.d.ts} +14 -4
  4. package/dist/{WrittenResponse-IAJvvL-g.d.ts → WrittenResponse-D2K1VW_V.d.cts} +14 -4
  5. package/dist/ai/LkAiProvider.cjs +3 -3
  6. package/dist/ai/LkAiProvider.d.cts +12 -1
  7. package/dist/ai/LkAiProvider.d.ts +12 -1
  8. package/dist/ai/LkAiProvider.js +1 -1
  9. package/dist/ai/useAiHelp.cjs +8 -4
  10. package/dist/ai/useAiHelp.d.cts +58 -3
  11. package/dist/ai/useAiHelp.d.ts +58 -3
  12. package/dist/ai/useAiHelp.js +2 -2
  13. package/dist/{chunk-ZDDBWUUA.js → chunk-3GLYK37F.js} +118 -25
  14. package/dist/chunk-3GLYK37F.js.map +1 -0
  15. package/dist/{chunk-NILHU555.cjs → chunk-5YGVGMMV.cjs} +7 -7
  16. package/dist/{chunk-NILHU555.cjs.map → chunk-5YGVGMMV.cjs.map} +1 -1
  17. package/dist/{chunk-KRM6DX47.js → chunk-62K4Q565.js} +7 -7
  18. package/dist/{chunk-KRM6DX47.js.map → chunk-62K4Q565.js.map} +1 -1
  19. package/dist/{chunk-CB2A56Q7.cjs → chunk-6QY7GC55.cjs} +22 -22
  20. package/dist/{chunk-CB2A56Q7.cjs.map → chunk-6QY7GC55.cjs.map} +1 -1
  21. package/dist/{chunk-GND3OBVE.js → chunk-7EEKZWTQ.js} +3 -3
  22. package/dist/{chunk-GND3OBVE.js.map → chunk-7EEKZWTQ.js.map} +1 -1
  23. package/dist/{chunk-YWJ5XJDU.cjs → chunk-B4JRBV56.cjs} +6 -6
  24. package/dist/{chunk-YWJ5XJDU.cjs.map → chunk-B4JRBV56.cjs.map} +1 -1
  25. package/dist/{chunk-WXESKL75.cjs → chunk-BL5SIBQA.cjs} +22 -22
  26. package/dist/{chunk-WXESKL75.cjs.map → chunk-BL5SIBQA.cjs.map} +1 -1
  27. package/dist/{chunk-EYLBSS4Y.js → chunk-C4PSJ3ZC.js} +10 -2
  28. package/dist/chunk-C4PSJ3ZC.js.map +1 -0
  29. package/dist/{chunk-U6SEII5V.js → chunk-FB47DQPS.js} +3 -3
  30. package/dist/{chunk-U6SEII5V.js.map → chunk-FB47DQPS.js.map} +1 -1
  31. package/dist/{chunk-CQ37UD4T.cjs → chunk-FKNEHCNT.cjs} +5 -5
  32. package/dist/{chunk-CQ37UD4T.cjs.map → chunk-FKNEHCNT.cjs.map} +1 -1
  33. package/dist/{chunk-W6TWQY6S.js → chunk-FPM2GQZY.js} +7 -7
  34. package/dist/{chunk-W6TWQY6S.js.map → chunk-FPM2GQZY.js.map} +1 -1
  35. package/dist/{chunk-AY3WN4FB.js → chunk-HNZ3JMAN.js} +6 -6
  36. package/dist/{chunk-AY3WN4FB.js.map → chunk-HNZ3JMAN.js.map} +1 -1
  37. package/dist/{chunk-X4YWXZ4J.js → chunk-HYOUMTPW.js} +3 -3
  38. package/dist/{chunk-X4YWXZ4J.js.map → chunk-HYOUMTPW.js.map} +1 -1
  39. package/dist/{chunk-5Q2KFIZQ.js → chunk-HZIT24BK.js} +4 -4
  40. package/dist/{chunk-5Q2KFIZQ.js.map → chunk-HZIT24BK.js.map} +1 -1
  41. package/dist/{chunk-GPGCBDBN.cjs → chunk-JII7XSCT.cjs} +21 -21
  42. package/dist/{chunk-GPGCBDBN.cjs.map → chunk-JII7XSCT.cjs.map} +1 -1
  43. package/dist/{chunk-XB2AHEU5.js → chunk-KFPCZ7N3.js} +10 -10
  44. package/dist/{chunk-XB2AHEU5.js.map → chunk-KFPCZ7N3.js.map} +1 -1
  45. package/dist/{chunk-5MO7ACSB.js → chunk-LRWFU3KS.js} +10 -10
  46. package/dist/{chunk-5MO7ACSB.js.map → chunk-LRWFU3KS.js.map} +1 -1
  47. package/dist/{chunk-M3K6BF5M.js → chunk-LX4JI262.js} +4 -4
  48. package/dist/{chunk-M3K6BF5M.js.map → chunk-LX4JI262.js.map} +1 -1
  49. package/dist/{chunk-GHKMX5UT.cjs → chunk-NFGUJ52G.cjs} +10 -2
  50. package/dist/chunk-NFGUJ52G.cjs.map +1 -0
  51. package/dist/{chunk-LHFC2BCM.cjs → chunk-NP5YC4NV.cjs} +19 -19
  52. package/dist/{chunk-LHFC2BCM.cjs.map → chunk-NP5YC4NV.cjs.map} +1 -1
  53. package/dist/{chunk-MPHX3Y42.cjs → chunk-OJGTJOWI.cjs} +2 -2
  54. package/dist/chunk-OJGTJOWI.cjs.map +1 -0
  55. package/dist/{chunk-Y4IDF5UO.cjs → chunk-PZVRZ6OX.cjs} +138 -7
  56. package/dist/chunk-PZVRZ6OX.cjs.map +1 -0
  57. package/dist/{chunk-U2BX2RWY.js → chunk-R4ZNFDTK.js} +10 -10
  58. package/dist/{chunk-U2BX2RWY.js.map → chunk-R4ZNFDTK.js.map} +1 -1
  59. package/dist/{chunk-MCTNLUKO.js → chunk-RX3A6KIY.js} +2 -2
  60. package/dist/chunk-RX3A6KIY.js.map +1 -0
  61. package/dist/{chunk-XZCMUDFP.cjs → chunk-TQN5CJO6.cjs} +122 -29
  62. package/dist/chunk-TQN5CJO6.cjs.map +1 -0
  63. package/dist/{chunk-NDIKHMLJ.js → chunk-TRXUZSOY.js} +137 -7
  64. package/dist/chunk-TRXUZSOY.js.map +1 -0
  65. package/dist/{chunk-7JBXDQTD.cjs → chunk-UKUHVCZX.cjs} +5 -5
  66. package/dist/{chunk-7JBXDQTD.cjs.map → chunk-UKUHVCZX.cjs.map} +1 -1
  67. package/dist/{chunk-WGHMWBEJ.cjs → chunk-UWFJGFB6.cjs} +19 -19
  68. package/dist/{chunk-WGHMWBEJ.cjs.map → chunk-UWFJGFB6.cjs.map} +1 -1
  69. package/dist/{chunk-OKEN6NJL.cjs → chunk-UXTYFEIC.cjs} +22 -22
  70. package/dist/{chunk-OKEN6NJL.cjs.map → chunk-UXTYFEIC.cjs.map} +1 -1
  71. package/dist/{chunk-WNDNQ6NR.js → chunk-V2QZM2P2.js} +7 -7
  72. package/dist/{chunk-WNDNQ6NR.js.map → chunk-V2QZM2P2.js.map} +1 -1
  73. package/dist/{chunk-W2P5RILE.cjs → chunk-WFGDKBBP.cjs} +22 -22
  74. package/dist/{chunk-W2P5RILE.cjs.map → chunk-WFGDKBBP.cjs.map} +1 -1
  75. package/dist/{chunk-6SNNMVBV.cjs → chunk-Y5H4NOM6.cjs} +5 -5
  76. package/dist/{chunk-6SNNMVBV.cjs.map → chunk-Y5H4NOM6.cjs.map} +1 -1
  77. package/dist/{chunk-XM5Q2ZCH.js → chunk-YIXO6C4Z.js} +7 -7
  78. package/dist/{chunk-XM5Q2ZCH.js.map → chunk-YIXO6C4Z.js.map} +1 -1
  79. package/dist/{chunk-GW3YIPDE.cjs → chunk-ZUE32Z67.cjs} +18 -18
  80. package/dist/{chunk-GW3YIPDE.cjs.map → chunk-ZUE32Z67.cjs.map} +1 -1
  81. package/dist/components/ActivityPreview.cjs +15 -15
  82. package/dist/components/ActivityPreview.d.cts +2 -2
  83. package/dist/components/ActivityPreview.d.ts +2 -2
  84. package/dist/components/ActivityPreview.js +14 -14
  85. package/dist/components/ActivitySequence.cjs +16 -16
  86. package/dist/components/ActivitySequence.d.cts +2 -2
  87. package/dist/components/ActivitySequence.d.ts +2 -2
  88. package/dist/components/ActivitySequence.js +15 -15
  89. package/dist/components/Dictation.cjs +8 -8
  90. package/dist/components/Dictation.d.cts +1 -1
  91. package/dist/components/Dictation.d.ts +1 -1
  92. package/dist/components/Dictation.js +7 -7
  93. package/dist/components/FillInTheBlanks.cjs +8 -8
  94. package/dist/components/FillInTheBlanks.d.cts +1 -1
  95. package/dist/components/FillInTheBlanks.d.ts +1 -1
  96. package/dist/components/FillInTheBlanks.js +7 -7
  97. package/dist/components/GapSelect.cjs +8 -8
  98. package/dist/components/GapSelect.d.cts +1 -1
  99. package/dist/components/GapSelect.d.ts +1 -1
  100. package/dist/components/GapSelect.js +7 -7
  101. package/dist/components/InteractiveVideo.cjs +15 -15
  102. package/dist/components/InteractiveVideo.d.cts +2 -2
  103. package/dist/components/InteractiveVideo.d.ts +2 -2
  104. package/dist/components/InteractiveVideo.js +13 -13
  105. package/dist/components/MultipleChoice.cjs +8 -8
  106. package/dist/components/MultipleChoice.d.cts +1 -1
  107. package/dist/components/MultipleChoice.d.ts +1 -1
  108. package/dist/components/MultipleChoice.js +7 -7
  109. package/dist/components/PronunciationFeedback.cjs +4 -4
  110. package/dist/components/PronunciationFeedback.d.cts +1 -1
  111. package/dist/components/PronunciationFeedback.d.ts +1 -1
  112. package/dist/components/PronunciationFeedback.js +3 -3
  113. package/dist/components/ReadAloud.cjs +6 -6
  114. package/dist/components/ReadAloud.d.cts +2 -2
  115. package/dist/components/ReadAloud.d.ts +2 -2
  116. package/dist/components/ReadAloud.js +5 -5
  117. package/dist/components/StimulusPanel.cjs +4 -4
  118. package/dist/components/StimulusPanel.d.cts +1 -1
  119. package/dist/components/StimulusPanel.d.ts +1 -1
  120. package/dist/components/StimulusPanel.js +3 -3
  121. package/dist/components/WrittenResponse.cjs +7 -5
  122. package/dist/components/WrittenResponse.d.cts +3 -3
  123. package/dist/components/WrittenResponse.d.ts +3 -3
  124. package/dist/components/WrittenResponse.js +6 -4
  125. package/dist/i18n/LkIntlProvider.cjs +7 -7
  126. package/dist/i18n/LkIntlProvider.d.cts +2 -2
  127. package/dist/i18n/LkIntlProvider.d.ts +2 -2
  128. package/dist/i18n/LkIntlProvider.js +1 -1
  129. package/dist/index.cjs +43 -39
  130. package/dist/index.d.cts +3 -3
  131. package/dist/index.d.ts +3 -3
  132. package/dist/index.js +17 -17
  133. package/dist/theme/skin.css +23 -0
  134. package/dist/{types-Botf4Wqj.d.cts → types-CO0XLEN6.d.cts} +19 -0
  135. package/dist/{types-7lGlo0zE.d.ts → types-CrtIcRcK.d.ts} +19 -0
  136. package/package.json +3 -3
  137. package/dist/chunk-EYLBSS4Y.js.map +0 -1
  138. package/dist/chunk-GHKMX5UT.cjs.map +0 -1
  139. package/dist/chunk-MCTNLUKO.js.map +0 -1
  140. package/dist/chunk-MPHX3Y42.cjs.map +0 -1
  141. package/dist/chunk-NDIKHMLJ.js.map +0 -1
  142. package/dist/chunk-XZCMUDFP.cjs.map +0 -1
  143. package/dist/chunk-Y4IDF5UO.cjs.map +0 -1
  144. package/dist/chunk-ZDDBWUUA.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
- var chunkMPHX3Y42_cjs = require('./chunk-MPHX3Y42.cjs');
4
+ var chunkOJGTJOWI_cjs = require('./chunk-OJGTJOWI.cjs');
5
5
  var chunkJ2QIHZ3H_cjs = require('./chunk-J2QIHZ3H.cjs');
6
6
  var chunkEANGKAKG_cjs = require('./chunk-EANGKAKG.cjs');
7
7
  var lkCore = require('@intellectif/lk-core');
@@ -9,10 +9,20 @@ var react = require('react');
9
9
 
10
10
  var DEFAULT_MAX_HINTS = 3;
11
11
  var MAX_HINTS_CEILING = 10;
12
- function hintLimit(ai) {
13
- const asked = ai?.maxHints;
12
+ function limitOf(asked) {
14
13
  return typeof asked === "number" && Number.isInteger(asked) && asked >= 1 ? Math.min(asked, MAX_HINTS_CEILING) : DEFAULT_MAX_HINTS;
15
14
  }
15
+ function hintLimit(ai) {
16
+ return limitOf(ai?.maxHints);
17
+ }
18
+ function writingFeedbackLimit(ai) {
19
+ return limitOf(ai?.maxWritingFeedback);
20
+ }
21
+ function writingFeedbackOffered(input) {
22
+ const { data, renderMode, submitted, disabled } = input;
23
+ const delivery = input.delivery ?? lkCore.OPEN_DELIVERY_POLICY;
24
+ return delivery.feedback && delivery.solutions && delivery.ai.explanations && renderMode === "practice" && !submitted && !disabled && data.redacted !== true && lkCore.aiSupports(data.type, "writing-feedback") && lkCore.aiAllowedByContent(data, "writing-feedback");
25
+ }
16
26
  function hintOffered(input) {
17
27
  const { data, renderMode, submitted, disabled } = input;
18
28
  const delivery = input.delivery ?? lkCore.OPEN_DELIVERY_POLICY;
@@ -71,7 +81,7 @@ function useAiExplanation(input) {
71
81
  const around = react.useContext(chunkJ2QIHZ3H_cjs.SequenceSlotContext);
72
82
  const renderMode = modeInForce(input.renderMode, around?.renderMode);
73
83
  const delivery = lkCore.combineDeliveryPolicies(input.delivery, around?.delivery);
74
- const ai = chunkMPHX3Y42_cjs.useLearnerAi(input.ai);
84
+ const ai = chunkOJGTJOWI_cjs.useLearnerAi(input.ai);
75
85
  const resetKey = explanationKey(renderMode, submitted, response, outcome);
76
86
  const [state, setState] = react.useState({ key: resetKey, status: "idle" });
77
87
  const run = react.useRef(0);
@@ -155,7 +165,7 @@ function useAiHints(input) {
155
165
  const renderMode = modeInForce(input.renderMode, around?.renderMode);
156
166
  const delivery = lkCore.combineDeliveryPolicies(input.delivery, around?.delivery);
157
167
  const disabled = input.disabled === true;
158
- const ai = chunkMPHX3Y42_cjs.useLearnerAi(input.ai);
168
+ const ai = chunkOJGTJOWI_cjs.useLearnerAi(input.ai);
159
169
  const resetKey = data.id;
160
170
  const [state, setState] = react.useState({ key: resetKey, hints: [], status: "idle" });
161
171
  const run = react.useRef(0);
@@ -255,8 +265,129 @@ function useAiHints(input) {
255
265
  ask
256
266
  };
257
267
  }
268
+ var draftOf = (response) => response.type === "written-response" ? response.text : "";
269
+ function useAiWritingFeedback(input) {
270
+ const { data, submitted, response } = input;
271
+ const around = react.useContext(chunkJ2QIHZ3H_cjs.SequenceSlotContext);
272
+ const renderMode = modeInForce(input.renderMode, around?.renderMode);
273
+ const delivery = lkCore.combineDeliveryPolicies(input.delivery, around?.delivery);
274
+ const disabled = input.disabled === true;
275
+ const ai = chunkOJGTJOWI_cjs.useLearnerAi(input.ai);
276
+ const resetKey = data.id;
277
+ const [state, setState] = react.useState({
278
+ key: resetKey,
279
+ shown: [],
280
+ status: "idle"
281
+ });
282
+ const run = react.useRef(0);
283
+ const controller = react.useRef(null);
284
+ const current = state.key === resetKey ? state : { key: resetKey, shown: [], status: "idle" };
285
+ const offered = ai?.writingFeedback !== void 0 && writingFeedbackOffered({ data, renderMode, submitted, disabled, delivery });
286
+ const limit = writingFeedbackLimit(ai);
287
+ const latest = react.useRef({ input, ai, offered, limit, current });
288
+ latest.current = { input, ai, offered, limit, current };
289
+ react.useEffect(() => {
290
+ return () => {
291
+ run.current += 1;
292
+ controller.current?.abort();
293
+ controller.current = null;
294
+ };
295
+ }, [resetKey]);
296
+ react.useEffect(() => {
297
+ if (!offered) {
298
+ run.current += 1;
299
+ controller.current?.abort();
300
+ controller.current = null;
301
+ setState(
302
+ (previous) => previous.status === "loading" ? { ...previous, status: "idle" } : previous
303
+ );
304
+ }
305
+ }, [offered]);
306
+ const ask = react.useCallback(() => {
307
+ const now = latest.current;
308
+ const port = now.ai?.writingFeedback;
309
+ const held = now.current;
310
+ if (port === void 0 || !now.offered || held.status === "loading" || held.shown.length >= now.limit) {
311
+ return;
312
+ }
313
+ const key = held.key;
314
+ const about = draftOf(now.input.response);
315
+ const request = lkCore.aiWritingFeedbackRequest({
316
+ data: now.input.data,
317
+ response: now.input.response,
318
+ previousFeedback: held.shown.map((one) => one.feedback.text),
319
+ ...localeOf(now.ai, now.input.locale)
320
+ });
321
+ if (request === null) {
322
+ return;
323
+ }
324
+ run.current += 1;
325
+ const mine = run.current;
326
+ controller.current?.abort();
327
+ const abort = new AbortController();
328
+ controller.current = abort;
329
+ setState({ ...held, status: "loading" });
330
+ callPort(() => port(request, { signal: abort.signal })).then(
331
+ (raw) => {
332
+ if (run.current !== mine) {
333
+ return;
334
+ }
335
+ const checked = lkCore.checkAiWritingFeedback(raw, request);
336
+ if (!checked.ok) {
337
+ warnRefused("writing feedback", now.input.data.id, checked.refusal);
338
+ setState((was) => ({ ...was, status: "unavailable" }));
339
+ latest.current.input.onInteraction?.({
340
+ type: "ai-help-refused",
341
+ activityId: now.input.data.id,
342
+ timestamp: Date.now(),
343
+ payload: {
344
+ feature: "writing-feedback",
345
+ reason: checked.refusal,
346
+ draftNumber: request.draftNumber
347
+ }
348
+ });
349
+ return;
350
+ }
351
+ const { feedback } = checked;
352
+ setState((was) => ({
353
+ ...was,
354
+ shown: was.key === key ? [...was.shown, { feedback, about }] : was.shown,
355
+ status: "idle"
356
+ }));
357
+ latest.current.input.onInteraction?.({
358
+ type: "ai-writing-feedback-shown",
359
+ activityId: now.input.data.id,
360
+ timestamp: Date.now(),
361
+ payload: {
362
+ draftNumber: request.draftNumber,
363
+ corrections: feedback.corrections.length,
364
+ ...feedback.indicativeScore !== null ? { indicativeScore: feedback.indicativeScore } : {},
365
+ ...shownPayload(feedback)
366
+ }
367
+ });
368
+ },
369
+ () => {
370
+ if (run.current === mine) {
371
+ setState((was) => ({ ...was, status: "unavailable" }));
372
+ }
373
+ }
374
+ );
375
+ }, []);
376
+ const last = current.shown[current.shown.length - 1];
377
+ return {
378
+ offered,
379
+ status: current.status,
380
+ feedback: current.shown.map((one) => one.feedback),
381
+ latest: last?.feedback ?? null,
382
+ current: last !== void 0 && last.about === draftOf(response),
383
+ limit,
384
+ used: current.shown.length,
385
+ ask
386
+ };
387
+ }
258
388
 
259
389
  exports.useAiExplanation = useAiExplanation;
260
390
  exports.useAiHints = useAiHints;
261
- //# sourceMappingURL=chunk-Y4IDF5UO.cjs.map
262
- //# sourceMappingURL=chunk-Y4IDF5UO.cjs.map
391
+ exports.useAiWritingFeedback = useAiWritingFeedback;
392
+ //# sourceMappingURL=chunk-PZVRZ6OX.cjs.map
393
+ //# sourceMappingURL=chunk-PZVRZ6OX.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ai/rules.ts","../src/ai/useAiHelp.ts"],"names":["OPEN_DELIVERY_POLICY","aiSupports","aiAllowedByContent","isDevelopment","useContext","SequenceSlotContext","combineDeliveryPolicies","useLearnerAi","useState","useRef","useEffect","useCallback","aiExplanationRequest","checkAiExplanation","aiHintRequest","checkAiHint","aiWritingFeedbackRequest","checkAiWritingFeedback"],"mappings":"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","file":"chunk-PZVRZ6OX.cjs","sourcesContent":["/**\n * When AI help is offered, and what the SDK does with what a port answers.\n *\n * Internal: these are the rules behind {@link useAiHints} and\n * {@link useAiExplanation}, which are what a host calls. They live apart from\n * the hooks so the hooks' module exports nothing but the hooks themselves.\n */\nimport {\n type AiRefusal,\n aiAllowedByContent,\n aiSupports,\n type ItemOutcome,\n type LearnerResponse,\n OPEN_DELIVERY_POLICY,\n type ResolvedDeliveryPolicy,\n} from '@intellectif/lk-core';\nimport { isDevelopment } from '../components/_internal.js';\nimport type { RenderableActivity, RenderMode } from '../components/types.js';\nimport type { LearnerAi } from './LkAiProvider.js';\n\n/** The most hints a question gives when the host names no number, and the most it may name. */\nconst DEFAULT_MAX_HINTS = 3;\nconst MAX_HINTS_CEILING = 10;\n\n/** A host's limit as a whole number from 1 to 10; anything else is the default, 3. */\nfunction limitOf(asked: unknown): number {\n return typeof asked === 'number' && Number.isInteger(asked) && asked >= 1\n ? Math.min(asked, MAX_HINTS_CEILING)\n : DEFAULT_MAX_HINTS;\n}\n\n/** `maxHints` as a whole number from 1 to 10; anything else is the default. */\nexport function hintLimit(ai: LearnerAi | undefined): number {\n return limitOf(ai?.maxHints);\n}\n\n/** `maxWritingFeedback` as a whole number from 1 to 10; anything else is the default. */\nexport function writingFeedbackLimit(ai: LearnerAi | undefined): number {\n return limitOf(ai?.maxWritingFeedback);\n}\n\n/**\n * Whether the learner may ask for feedback on a draft: `practice`, before\n * submit, on a written response that is not disabled or redacted, whose author\n * left explanations on. The paper's policy must show feedback and solutions —\n * a correction is the right form beside a wrong one — and allow AI\n * explanations, which writing feedback is: a model's words about the\n * learner's own answer.\n */\nexport function writingFeedbackOffered(input: {\n data: RenderableActivity;\n renderMode: RenderMode;\n submitted: boolean;\n disabled: boolean;\n delivery?: ResolvedDeliveryPolicy;\n}): boolean {\n const { data, renderMode, submitted, disabled } = input;\n const delivery = input.delivery ?? OPEN_DELIVERY_POLICY;\n return (\n delivery.feedback &&\n delivery.solutions &&\n delivery.ai.explanations &&\n renderMode === 'practice' &&\n !submitted &&\n !disabled &&\n data.redacted !== true &&\n aiSupports(data.type, 'writing-feedback') &&\n aiAllowedByContent(data, 'writing-feedback')\n );\n}\n\n/**\n * Whether the learner may ask for a hint: `practice`, before submit, on a\n * question that is not disabled, of a type that takes hints, whose author left\n * hints on, and whole — a redacted item is an exam's, and gives none.\n */\nexport function hintOffered(input: {\n data: RenderableActivity;\n renderMode: RenderMode;\n submitted: boolean;\n disabled: boolean;\n /** The paper's policy: hints at all, and AI hints in particular. Open when absent. */\n delivery?: ResolvedDeliveryPolicy;\n}): boolean {\n const { data, renderMode, submitted, disabled } = input;\n const delivery = input.delivery ?? OPEN_DELIVERY_POLICY;\n return (\n delivery.hints &&\n delivery.ai.hints &&\n renderMode === 'practice' &&\n !submitted &&\n !disabled &&\n data.redacted !== true &&\n aiSupports(data.type, 'hint') &&\n aiAllowedByContent(data, 'hint')\n );\n}\n\n/**\n * Whether the learner may ask for an explanation: of a graded answer — in\n * `practice` once submitted, or in `review` when the grade of record is\n * scored — of a type the SDK explains, whose author left explanations on.\n * Never in `exam`.\n */\nexport function explanationOffered(input: {\n data: RenderableActivity;\n renderMode: RenderMode;\n submitted: boolean;\n outcome: ItemOutcome | undefined;\n /**\n * The paper's policy. An explanation needs AI explanations on, and more: it\n * explains a grade, so it needs the grade shown (`feedback`), and it all but\n * always says what the right answer was, so it needs `solutions` too. On a\n * paper that hides the answer, \"Explain my answer\" would hand it over.\n */\n delivery?: ResolvedDeliveryPolicy;\n}): boolean {\n const { data, renderMode, submitted, outcome } = input;\n const delivery = input.delivery ?? OPEN_DELIVERY_POLICY;\n if (!delivery.feedback || !delivery.solutions || !delivery.ai.explanations) {\n return false;\n }\n const graded =\n (renderMode === 'practice' && submitted) ||\n (renderMode === 'review' && outcome?.status === 'scored');\n return graded && aiSupports(data.type, 'explanation') && aiAllowedByContent(data, 'explanation');\n}\n\n/** Calls a port, turning a synchronous throw into a rejection. */\nexport function callPort<T>(call: () => Promise<T>): Promise<T> {\n try {\n return Promise.resolve(call());\n } catch (error) {\n return Promise.reject(error);\n }\n}\n\n/** Tells a developer why a port's answer was not shown. Silent in production. */\nexport function warnRefused(\n feature: 'explanation' | 'hint' | 'writing feedback',\n activityId: string,\n reason: AiRefusal,\n): void {\n if (isDevelopment()) {\n console.warn(\n `learning-kit: the AI ${feature} for \"${activityId}\" was not shown (${reason}). The learner saw \"not available\" instead.`,\n );\n }\n}\n\n/** The language to write in: the host's choice for the learner, else the interface language. */\nexport const localeOf = (\n ai: LearnerAi | undefined,\n locale: string | undefined,\n): { learnerLocale?: string } => {\n const chosen = ai?.learnerLocale ?? locale;\n return chosen === undefined ? {} : { learnerLocale: chosen };\n};\n\n/** What an explanation is about: when any of it changes, the explanation shown no longer is. */\nexport function explanationKey(\n renderMode: RenderMode,\n submitted: boolean,\n response: LearnerResponse,\n outcome: ItemOutcome | undefined,\n): string {\n let graded = '';\n if (outcome !== undefined) {\n graded = `${outcome.status}:${'score' in outcome ? outcome.score : ''}`;\n }\n return `${renderMode}|${submitted}|${graded}|${JSON.stringify(response)}`;\n}\n","'use client';\n\nimport {\n type AiTextResult,\n type AiWritingFeedback,\n aiExplanationRequest,\n aiHintRequest,\n aiWritingFeedbackRequest,\n checkAiExplanation,\n checkAiHint,\n checkAiWritingFeedback,\n combineDeliveryPolicies,\n type DeliveryPolicy,\n type InteractionEvent,\n type ItemOutcome,\n type LearnerResponse,\n} from '@intellectif/lk-core';\nimport { useCallback, useContext, useEffect, useRef, useState } from 'react';\nimport { SequenceSlotContext } from '../components/shared/sequence-slot.js';\nimport type { RenderableActivity, RenderMode } from '../components/types.js';\nimport { type LearnerAi, useLearnerAi } from './LkAiProvider.js';\nimport {\n callPort,\n explanationKey,\n explanationOffered,\n hintLimit,\n hintOffered,\n localeOf,\n warnRefused,\n writingFeedbackLimit,\n writingFeedbackOffered,\n} from './rules.js';\n\n/**\n * The question AI help is being asked about, as the SDK's own components know\n * it. A host drawing its own question passes what it has; everything optional\n * defaults to what the components pass.\n */\nexport interface AiHelpSituation {\n /** The question, as `data` is given to any activity component. */\n data: RenderableActivity;\n /** The learner's answer as it stands: submitted, restored, or being written. */\n response: LearnerResponse;\n /** Whether the learner has committed that answer. */\n submitted: boolean;\n /**\n * Pass on the `renderMode` your question was given. Left out, the paper\n * around the question answers for it — a question inside `<ActivitySequence>`\n * or `<InteractiveVideo>` knows its mode — and `practice` only where there is\n * no paper at all. An `exam` around the question wins over anything passed\n * here.\n */\n renderMode?: RenderMode;\n /**\n * The AI ports to use, overriding {@link LkAiProvider}'s for this question.\n * A question inside `<ActivitySequence>` or `<InteractiveVideo>` is handed\n * the ports in force as `ai` — pass that straight in.\n */\n ai?: LearnerAi;\n /** The interface language: the default language for what a model writes. */\n locale?: string;\n /** Where `ai-hint-shown` and `ai-explanation-shown` go. */\n onInteraction?: (event: InteractionEvent) => void;\n /**\n * The delivery policy your question was handed. The paper around the\n * question holds as well — a set that switched AI hints off keeps them off\n * here, whatever is passed — so leaving it out never turns anything on.\n */\n delivery?: DeliveryPolicy | null;\n}\n\nexport interface AiExplanationInput extends AiHelpSituation {\n /** The grade of record, which `review` explains. */\n outcome?: ItemOutcome;\n}\n\nexport interface AiExplanationHelp {\n /**\n * Whether to offer the learner an explanation at all: a port is present, the\n * answer is graded, the type is one the SDK explains, and the author left\n * explanations on. False in `exam`, always.\n */\n offered: boolean;\n status: 'idle' | 'loading' | 'shown' | 'unavailable';\n /** What to show, once it has arrived and been checked. Its `text` is text, never HTML. */\n explanation: AiTextResult | null;\n /**\n * Asks the port, when the learner presses something. Does nothing while a\n * call is on its way, and nothing at all where `offered` is false — so an\n * exam cannot reach a model through this hook, whatever ports are in scope.\n *\n * One identity for the life of the question, so it is safe in an effect's\n * dependencies.\n */\n ask: () => void;\n}\n\nexport interface AiHintsInput extends AiHelpSituation {\n /** A disabled question gives no hints, as it takes no answer. */\n disabled?: boolean;\n}\n\nexport interface AiHintsHelp {\n /**\n * Whether the learner may ask for hints here at all: a port is present, the\n * mode is `practice`, the answer is not in, the question is not disabled or\n * redacted, the type takes hints and the author left them on. It stays true\n * once `used` reaches `limit` — there are no more to give, but the question\n * is still one that gives them.\n */\n offered: boolean;\n status: 'idle' | 'loading' | 'unavailable';\n /** Every hint shown for this question, in the order they were given. */\n hints: readonly AiTextResult[];\n /** The host's `maxHints`, as a whole number from 1 to 10 (3 by default). */\n limit: number;\n /** How many hints have been shown. A refused hint is not counted. */\n used: number;\n /**\n * Asks for the next hint. Does nothing at the limit, while a call is on its\n * way, or where `offered` is false. One identity for the life of the\n * question.\n */\n ask: () => void;\n}\n\n/**\n * The mode a question is answered in: what the caller says, and what the paper\n * around it says.\n *\n * A host drawing its own question is given `renderMode` like every other prop\n * and is expected to pass it on, but nothing makes it — and a hook that took\n * `practice` on trust would offer hints on a paper of record, as soon as an\n * `LkAiProvider` sat anywhere above the pager. So an `exam` around the question\n * wins, and where the caller says nothing the paper answers for it. A question\n * standing on its own belongs to no paper, and is `practice` as before.\n */\nfunction modeInForce(asked: RenderMode | undefined, around: RenderMode | undefined): RenderMode {\n if (around === 'exam') {\n return 'exam';\n }\n return asked ?? around ?? 'practice';\n}\n\n/**\n * What the record of help a learner was shown carries beside the fact of it:\n * who wrote it, and what the call cost — both only where the port sent them.\n *\n * The text is deliberately absent. A host that wants to keep what a learner\n * read has it in its own port; an interaction is a record of what happened, and\n * is kept for every learner and every question.\n */\nfunction shownPayload(result: AiTextResult): Record<string, unknown> {\n return {\n ...(result.provenance !== undefined ? { provenance: result.provenance } : {}),\n ...(result.usage !== undefined ? { usage: result.usage } : {}),\n };\n}\n\ntype ExplanationState =\n | { key: string; status: 'idle' | 'loading' | 'unavailable' }\n | { key: string; status: 'shown'; result: AiTextResult };\n\n/**\n * \"Explain my answer\", without the SDK's own button and panel: the rules, the\n * call, the checks and the record, for a question you draw yourself.\n *\n * It is what `<MultipleChoice>` and the others use, so a host's own question\n * offers help on exactly the same terms: never in `exam`, never where the\n * item's author switched explanations off, and never an explanation whose\n * verdict contradicts the grade the SDK computed.\n *\n * ```tsx\n * const { offered, status, explanation, ask } = useAiExplanation({\n * data, response, submitted, renderMode, outcome, ai: question.ai,\n * });\n * if (!offered) return null;\n * return explanation === null ? (\n * <button type=\"button\" onClick={ask} aria-busy={status === 'loading'}>Explain my answer</button>\n * ) : (\n * <section>\n * <p>{explanation.text}</p>\n * <p>Written by AI. It can make mistakes.</p>\n * </section>\n * );\n * ```\n *\n * **Say who wrote it.** The SDK's own panel carries \"Written by AI. It can make\n * mistakes.\"; a page that drops that line passes a model's words off as the\n * course's.\n *\n * When the answer or its grade changes, the explanation is dropped and a call\n * still on its way is abandoned — a late explanation never lands beside an\n * answer it was not written for — as it is when the caller unmounts.\n */\nexport function useAiExplanation(input: AiExplanationInput): AiExplanationHelp {\n const { data, response, submitted, outcome } = input;\n const around = useContext(SequenceSlotContext);\n const renderMode = modeInForce(input.renderMode, around?.renderMode);\n const delivery = combineDeliveryPolicies(input.delivery, around?.delivery);\n const ai = useLearnerAi(input.ai);\n const resetKey = explanationKey(renderMode, submitted, response, outcome);\n const [state, setState] = useState<ExplanationState>({ key: resetKey, status: 'idle' });\n const run = useRef(0);\n const controller = useRef<AbortController | null>(null);\n const current: ExplanationState =\n state.key === resetKey ? state : { key: resetKey, status: 'idle' };\n const offered =\n ai?.explain !== undefined &&\n explanationOffered({ data, renderMode, submitted, outcome, delivery });\n\n // What `ask` reads, as of this render, so `ask` itself can keep one identity\n // for the life of the question. The state goes in too: an event handler runs\n // after the render that set it, so the ref is what the learner last saw.\n const latest = useRef({ input, renderMode, ai, resetKey, offered, current });\n latest.current = { input, renderMode, ai, resetKey, offered, current };\n\n // Another answer, or the caller going away: abandon the call in flight.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the key is the trigger — its cleanup runs when the answer changes\n useEffect(() => {\n return () => {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n };\n }, [resetKey]);\n\n const ask = useCallback((): void => {\n const now = latest.current;\n const port = now.ai?.explain;\n if (port === undefined || !now.offered || now.current.status === 'loading') {\n return;\n }\n const key = now.resetKey;\n const request = aiExplanationRequest({\n data: now.input.data,\n response: now.input.response,\n ...(now.renderMode === 'review' && now.input.outcome !== undefined\n ? { outcome: now.input.outcome }\n : {}),\n ...localeOf(now.ai, now.input.locale),\n });\n if (request === null) {\n setState({ key, status: 'unavailable' });\n return;\n }\n run.current += 1;\n const mine = run.current;\n controller.current?.abort();\n const abort = new AbortController();\n controller.current = abort;\n setState({ key, status: 'loading' });\n callPort(() => port(request, { signal: abort.signal })).then(\n (raw) => {\n if (run.current !== mine) {\n return;\n }\n const checked = checkAiExplanation(raw, request);\n if (!checked.ok) {\n warnRefused('explanation', now.input.data.id, checked.refusal);\n setState({ key, status: 'unavailable' });\n latest.current.input.onInteraction?.({\n type: 'ai-help-refused',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: { feature: 'explanation', reason: checked.refusal },\n });\n return;\n }\n setState({ key, status: 'shown', result: checked.result });\n latest.current.input.onInteraction?.({\n type: 'ai-explanation-shown',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: shownPayload(checked.result),\n });\n },\n () => {\n if (run.current === mine) {\n setState({ key, status: 'unavailable' });\n }\n },\n );\n }, []);\n\n return {\n offered,\n status: current.status,\n explanation: current.status === 'shown' ? current.result : null,\n ask,\n };\n}\n\ninterface HintState {\n key: string;\n hints: AiTextResult[];\n status: 'idle' | 'loading' | 'unavailable';\n}\n\n/**\n * \"Get a hint\", without the SDK's own button and list: the rules, the call, the\n * checks and the record, for a question you draw yourself.\n *\n * It is what `<MultipleChoice>` and the others use, so a host's own question\n * gives hints on exactly the same terms: only in `practice` before submit, up\n * to the host's limit, never on a redacted item or a type the SDK does not hint\n * for, and never a hint that gives the answer away — which is refused rather\n * than shown, and not counted.\n *\n * ```tsx\n * const { offered, status, hints, used, limit, ask } = useAiHints({\n * data, response, submitted, renderMode, ai: question.ai,\n * });\n * ```\n *\n * **Say who wrote them**, as the SDK's own list does: \"Written by AI. It can\n * make mistakes.\"\n *\n * Keep the hints on screen after submit — the learner can see what they were\n * given — while `offered` turns false. Another question starts with none.\n */\nexport function useAiHints(input: AiHintsInput): AiHintsHelp {\n const { data, submitted } = input;\n const around = useContext(SequenceSlotContext);\n const renderMode = modeInForce(input.renderMode, around?.renderMode);\n const delivery = combineDeliveryPolicies(input.delivery, around?.delivery);\n const disabled = input.disabled === true;\n const ai = useLearnerAi(input.ai);\n const resetKey = data.id;\n const [state, setState] = useState<HintState>({ key: resetKey, hints: [], status: 'idle' });\n const run = useRef(0);\n const controller = useRef<AbortController | null>(null);\n const current: HintState =\n state.key === resetKey ? state : { key: resetKey, hints: [], status: 'idle' };\n const offered =\n ai?.hint !== undefined && hintOffered({ data, renderMode, submitted, disabled, delivery });\n const limit = hintLimit(ai);\n\n const latest = useRef({ input, renderMode, ai, offered, limit, current });\n latest.current = { input, renderMode, ai, offered, limit, current };\n\n // Another question, or the caller going away: abandon the call in flight.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the key is the trigger — its cleanup runs when the question changes\n useEffect(() => {\n return () => {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n };\n }, [resetKey]);\n\n // Submitted, or disabled, while a hint was on its way: it would arrive for an\n // answer that can no longer use it.\n useEffect(() => {\n if (!offered) {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n setState((previous) =>\n previous.status === 'loading' ? { ...previous, status: 'idle' } : previous,\n );\n }\n }, [offered]);\n\n const ask = useCallback((): void => {\n const now = latest.current;\n const port = now.ai?.hint;\n const held = now.current;\n if (\n port === undefined ||\n !now.offered ||\n held.status === 'loading' ||\n held.hints.length >= now.limit\n ) {\n return;\n }\n const key = held.key;\n const request = aiHintRequest({\n data: now.input.data,\n response: now.input.response,\n previousHints: held.hints.map((hint) => hint.text),\n ...localeOf(now.ai, now.input.locale),\n });\n if (request === null) {\n setState({ ...held, status: 'unavailable' });\n return;\n }\n run.current += 1;\n const mine = run.current;\n controller.current?.abort();\n const abort = new AbortController();\n controller.current = abort;\n setState({ ...held, status: 'loading' });\n callPort(() => port(request, { signal: abort.signal })).then(\n (raw) => {\n if (run.current !== mine) {\n return;\n }\n const checked = checkAiHint(raw, request);\n if (!checked.ok) {\n warnRefused('hint', now.input.data.id, checked.refusal);\n setState((shown) => ({ ...shown, status: 'unavailable' }));\n latest.current.input.onInteraction?.({\n type: 'ai-help-refused',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: {\n feature: 'hint',\n reason: checked.refusal,\n hintNumber: request.hintNumber,\n },\n });\n return;\n }\n setState((shown) => ({\n ...shown,\n hints: shown.key === key ? [...shown.hints, checked.result] : shown.hints,\n status: 'idle',\n }));\n latest.current.input.onInteraction?.({\n type: 'ai-hint-shown',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: { hintNumber: request.hintNumber, ...shownPayload(checked.result) },\n });\n },\n () => {\n if (run.current === mine) {\n setState((shown) => ({ ...shown, status: 'unavailable' }));\n }\n },\n );\n }, []);\n\n return {\n offered,\n status: current.status,\n hints: current.hints,\n limit,\n used: current.hints.length,\n ask,\n };\n}\n\nexport interface AiWritingFeedbackInput extends AiHelpSituation {\n /** A disabled question takes no feedback, as it takes no answer. */\n disabled?: boolean;\n}\n\nexport interface AiWritingFeedbackHelp {\n /**\n * Whether the learner may ask for feedback here at all: a port is present,\n * the mode is `practice`, the answer is not in, the question is a written\n * response that is not disabled or redacted, its author left explanations on,\n * and the paper shows feedback and solutions and allows AI explanations. It\n * stays true once `used` reaches `limit`.\n */\n offered: boolean;\n status: 'idle' | 'loading' | 'unavailable';\n /** Every piece of feedback shown for this question, oldest first. */\n feedback: readonly AiWritingFeedback[];\n /** The latest, or `null` before any. Its `text` is text, never HTML. */\n latest: AiWritingFeedback | null;\n /**\n * Whether `latest` is about the draft as it stands. Once the learner revises,\n * its corrections point at words that may no longer be there: say so.\n */\n current: boolean;\n /** The host's `maxWritingFeedback`, as a whole number from 1 to 10 (3 by default). */\n limit: number;\n /** How many times feedback has been shown. A refused reply is not counted. */\n used: number;\n /**\n * Asks for feedback on the draft as it stands. Does nothing at the limit,\n * while a call is on its way, on an empty draft, or where `offered` is false.\n * One identity for the life of the question.\n */\n ask: () => void;\n}\n\ninterface WritingFeedbackState {\n key: string;\n shown: { feedback: AiWritingFeedback; about: string }[];\n status: 'idle' | 'loading' | 'unavailable';\n}\n\n/** The text of a written response; another type's response is no draft at all. */\nconst draftOf = (response: LearnerResponse): string =>\n response.type === 'written-response' ? response.text : '';\n\n/**\n * \"Get feedback on my draft\", without the SDK's own button and panel: the\n * rules, the call, the checks and the record, for a written response you draw\n * yourself.\n *\n * It is what `<WrittenResponse>` uses, so a host's own essay box gets feedback\n * on the same terms: only in `practice` before submit, up to the host's limit,\n * and never feedback that corrects words the learner did not write — which is\n * refused rather than shown, and not counted.\n *\n * ```tsx\n * const { offered, status, latest, current, used, limit, ask } = useAiWritingFeedback({\n * data, response, submitted, renderMode, ai: question.ai,\n * });\n * ```\n *\n * **Say who wrote it**, as the SDK's own panel does: \"Written by AI. It can make\n * mistakes.\" And show `indicativeScore` as what it is — an indication, not a\n * grade.\n */\nexport function useAiWritingFeedback(input: AiWritingFeedbackInput): AiWritingFeedbackHelp {\n const { data, submitted, response } = input;\n const around = useContext(SequenceSlotContext);\n const renderMode = modeInForce(input.renderMode, around?.renderMode);\n const delivery = combineDeliveryPolicies(input.delivery, around?.delivery);\n const disabled = input.disabled === true;\n const ai = useLearnerAi(input.ai);\n const resetKey = data.id;\n const [state, setState] = useState<WritingFeedbackState>({\n key: resetKey,\n shown: [],\n status: 'idle',\n });\n const run = useRef(0);\n const controller = useRef<AbortController | null>(null);\n const current: WritingFeedbackState =\n state.key === resetKey ? state : { key: resetKey, shown: [], status: 'idle' };\n const offered =\n ai?.writingFeedback !== undefined &&\n writingFeedbackOffered({ data, renderMode, submitted, disabled, delivery });\n const limit = writingFeedbackLimit(ai);\n\n const latest = useRef({ input, ai, offered, limit, current });\n latest.current = { input, ai, offered, limit, current };\n\n // Another question, or the caller going away: abandon the call in flight.\n // biome-ignore lint/correctness/useExhaustiveDependencies: the key is the trigger — its cleanup runs when the question changes\n useEffect(() => {\n return () => {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n };\n }, [resetKey]);\n\n // Submitted, or disabled, while feedback was on its way: it would arrive for\n // a draft that can no longer be revised.\n useEffect(() => {\n if (!offered) {\n run.current += 1;\n controller.current?.abort();\n controller.current = null;\n setState((previous) =>\n previous.status === 'loading' ? { ...previous, status: 'idle' } : previous,\n );\n }\n }, [offered]);\n\n const ask = useCallback((): void => {\n const now = latest.current;\n const port = now.ai?.writingFeedback;\n const held = now.current;\n if (\n port === undefined ||\n !now.offered ||\n held.status === 'loading' ||\n held.shown.length >= now.limit\n ) {\n return;\n }\n const key = held.key;\n const about = draftOf(now.input.response);\n const request = aiWritingFeedbackRequest({\n data: now.input.data,\n response: now.input.response,\n previousFeedback: held.shown.map((one) => one.feedback.text),\n ...localeOf(now.ai, now.input.locale),\n });\n if (request === null) {\n // An empty draft: nothing to give feedback on, and nothing went wrong.\n return;\n }\n run.current += 1;\n const mine = run.current;\n controller.current?.abort();\n const abort = new AbortController();\n controller.current = abort;\n setState({ ...held, status: 'loading' });\n callPort(() => port(request, { signal: abort.signal })).then(\n (raw) => {\n if (run.current !== mine) {\n return;\n }\n const checked = checkAiWritingFeedback(raw, request);\n if (!checked.ok) {\n warnRefused('writing feedback', now.input.data.id, checked.refusal);\n setState((was) => ({ ...was, status: 'unavailable' }));\n latest.current.input.onInteraction?.({\n type: 'ai-help-refused',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: {\n feature: 'writing-feedback',\n reason: checked.refusal,\n draftNumber: request.draftNumber,\n },\n });\n return;\n }\n const { feedback } = checked;\n setState((was) => ({\n ...was,\n shown: was.key === key ? [...was.shown, { feedback, about }] : was.shown,\n status: 'idle',\n }));\n latest.current.input.onInteraction?.({\n type: 'ai-writing-feedback-shown',\n activityId: now.input.data.id,\n timestamp: Date.now(),\n payload: {\n draftNumber: request.draftNumber,\n corrections: feedback.corrections.length,\n ...(feedback.indicativeScore !== null\n ? { indicativeScore: feedback.indicativeScore }\n : {}),\n ...shownPayload(feedback),\n },\n });\n },\n () => {\n if (run.current === mine) {\n setState((was) => ({ ...was, status: 'unavailable' }));\n }\n },\n );\n }, []);\n\n const last = current.shown[current.shown.length - 1];\n return {\n offered,\n status: current.status,\n feedback: current.shown.map((one) => one.feedback),\n latest: last?.feedback ?? null,\n current: last !== undefined && last.about === draftOf(response),\n limit,\n used: current.shown.length,\n ask,\n };\n}\n"]}
@@ -1,14 +1,14 @@
1
1
  'use client';
2
- import { StimulusPanel } from './chunk-5Q2KFIZQ.js';
3
- import { WrittenResponse } from './chunk-ZDDBWUUA.js';
4
- import { MultipleChoice } from './chunk-W6TWQY6S.js';
5
- import { FillInTheBlanks } from './chunk-KRM6DX47.js';
6
- import { GapSelect } from './chunk-XM5Q2ZCH.js';
7
- import { Dictation } from './chunk-WNDNQ6NR.js';
8
- import { ReadAloud, stopCaptureGroup } from './chunk-AY3WN4FB.js';
2
+ import { StimulusPanel } from './chunk-HZIT24BK.js';
3
+ import { WrittenResponse } from './chunk-3GLYK37F.js';
4
+ import { MultipleChoice } from './chunk-FPM2GQZY.js';
5
+ import { FillInTheBlanks } from './chunk-62K4Q565.js';
6
+ import { GapSelect } from './chunk-YIXO6C4Z.js';
7
+ import { Dictation } from './chunk-V2QZM2P2.js';
8
+ import { ReadAloud, stopCaptureGroup } from './chunk-HNZ3JMAN.js';
9
9
  import { takeOf, mintTake, SequenceSlotContext } from './chunk-U4KTOH7C.js';
10
10
  import { randomSessionId } from './chunk-PPFVTXGJ.js';
11
- import { useLkStrings } from './chunk-EYLBSS4Y.js';
11
+ import { useLkStrings } from './chunk-C4PSJ3ZC.js';
12
12
  import { isItemGroup, flattenSequence, resolveDeliveryPolicy, resolveItemScoringPolicy, resolvePlaybackPolicy, slotMediaKey, stimulusMediaKey } from '@intellectif/lk-core';
13
13
  import { useRef, useState, useMemo, useEffect } from 'react';
14
14
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -820,5 +820,5 @@ function ActivitySequence({
820
820
  }
821
821
 
822
822
  export { ActivitySequence };
823
- //# sourceMappingURL=chunk-U2BX2RWY.js.map
824
- //# sourceMappingURL=chunk-U2BX2RWY.js.map
823
+ //# sourceMappingURL=chunk-R4ZNFDTK.js.map
824
+ //# sourceMappingURL=chunk-R4ZNFDTK.js.map