@intellectif/lk-react 6.0.0 → 6.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +330 -0
  2. package/README.md +43 -12
  3. package/dist/{WrittenResponse-C3GIWErG.d.ts → WrittenResponse-BXHidIaK.d.cts} +2 -2
  4. package/dist/{WrittenResponse-BfpzfLlR.d.cts → WrittenResponse-DJYGuMKk.d.ts} +2 -2
  5. package/dist/{chunk-E4MNA2IQ.js → chunk-2TW6WLBC.js} +3 -2
  6. package/dist/chunk-2TW6WLBC.js.map +1 -0
  7. package/dist/chunk-4HZ7LQIH.cjs +1 -0
  8. package/dist/chunk-57E3E2H4.js +1 -0
  9. package/dist/chunk-6F4X2NKL.js +1 -0
  10. package/dist/chunk-6N43WDVG.js +1 -0
  11. package/dist/{chunk-CPF2JJG6.cjs → chunk-AMGK7DDM.cjs} +32 -2
  12. package/dist/chunk-AMGK7DDM.cjs.map +1 -0
  13. package/dist/{chunk-YXG3UJVG.cjs → chunk-BE7R2X3S.cjs} +3 -2
  14. package/dist/chunk-BE7R2X3S.cjs.map +1 -0
  15. package/dist/{chunk-B3FUUKZ2.js → chunk-DQNVAXG6.js} +32 -3
  16. package/dist/chunk-DQNVAXG6.js.map +1 -0
  17. package/dist/{chunk-PBVMEOWI.cjs → chunk-DUIWK272.cjs} +7 -6
  18. package/dist/chunk-DUIWK272.cjs.map +1 -0
  19. package/dist/chunk-GB5URWM4.js +1 -0
  20. package/dist/{chunk-OQGBAQL2.js → chunk-IUODGQXE.js} +5 -4
  21. package/dist/chunk-IUODGQXE.js.map +1 -0
  22. package/dist/{chunk-R3OZ6JHQ.cjs → chunk-MDYKYDRN.cjs} +3 -2
  23. package/dist/chunk-MDYKYDRN.cjs.map +1 -0
  24. package/dist/{chunk-BQHDQ33H.js → chunk-MDZMKC27.js} +3 -2
  25. package/dist/chunk-MDZMKC27.js.map +1 -0
  26. package/dist/chunk-NEWGUDA5.cjs +1 -0
  27. package/dist/chunk-NPY2F7F6.cjs +1 -0
  28. package/dist/chunk-PGEG7UZJ.cjs +1 -0
  29. package/dist/{chunk-IYLQSNLE.cjs → chunk-PNNUPUH4.cjs} +3 -2
  30. package/dist/chunk-PNNUPUH4.cjs.map +1 -0
  31. package/dist/chunk-UQ3BBEIT.js +1 -0
  32. package/dist/chunk-XOR4MPEN.cjs +1 -0
  33. package/dist/{chunk-5TUTJ4R3.js → chunk-ZLUMUJUQ.js} +3 -2
  34. package/dist/chunk-ZLUMUJUQ.js.map +1 -0
  35. package/dist/components/ActivitySequence.cjs +5 -4
  36. package/dist/components/ActivitySequence.d.cts +18 -10
  37. package/dist/components/ActivitySequence.d.ts +18 -10
  38. package/dist/components/ActivitySequence.js +4 -3
  39. package/dist/components/FillInTheBlanks.cjs +1 -0
  40. package/dist/components/FillInTheBlanks.d.cts +1 -1
  41. package/dist/components/FillInTheBlanks.d.ts +1 -1
  42. package/dist/components/FillInTheBlanks.js +1 -0
  43. package/dist/components/MultipleChoice.cjs +3 -2
  44. package/dist/components/MultipleChoice.d.cts +2 -2
  45. package/dist/components/MultipleChoice.d.ts +2 -2
  46. package/dist/components/MultipleChoice.js +2 -1
  47. package/dist/components/StimulusPanel.cjs +1 -0
  48. package/dist/components/StimulusPanel.d.cts +1 -1
  49. package/dist/components/StimulusPanel.d.ts +1 -1
  50. package/dist/components/StimulusPanel.js +1 -0
  51. package/dist/components/WrittenResponse.cjs +3 -2
  52. package/dist/components/WrittenResponse.d.cts +3 -3
  53. package/dist/components/WrittenResponse.d.ts +3 -3
  54. package/dist/components/WrittenResponse.js +2 -1
  55. package/dist/hooks/useActivityState.cjs +1 -0
  56. package/dist/hooks/useActivityState.js +1 -0
  57. package/dist/hooks/useXAPI.cjs +3 -2
  58. package/dist/hooks/useXAPI.d.cts +31 -0
  59. package/dist/hooks/useXAPI.d.ts +31 -0
  60. package/dist/hooks/useXAPI.js +2 -1
  61. package/dist/index.cjs +18 -13
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.cts +3 -3
  64. package/dist/index.d.ts +3 -3
  65. package/dist/index.js +6 -5
  66. package/dist/index.js.map +1 -1
  67. package/dist/theme/ThemeProvider.cjs +10 -5
  68. package/dist/theme/ThemeProvider.d.cts +21 -1
  69. package/dist/theme/ThemeProvider.d.ts +21 -1
  70. package/dist/theme/ThemeProvider.js +2 -1
  71. package/dist/theme/skin.css +7 -7
  72. package/dist/{types-DlHmPLbD.d.cts → types-9iS1Vs2I.d.cts} +29 -13
  73. package/dist/{types-DlHmPLbD.d.ts → types-9iS1Vs2I.d.ts} +29 -13
  74. package/package.json +28 -5
  75. package/dist/chunk-5TUTJ4R3.js.map +0 -1
  76. package/dist/chunk-B3FUUKZ2.js.map +0 -1
  77. package/dist/chunk-BQHDQ33H.js.map +0 -1
  78. package/dist/chunk-CPF2JJG6.cjs.map +0 -1
  79. package/dist/chunk-E4MNA2IQ.js.map +0 -1
  80. package/dist/chunk-IYLQSNLE.cjs.map +0 -1
  81. package/dist/chunk-OQGBAQL2.js.map +0 -1
  82. package/dist/chunk-PBVMEOWI.cjs.map +0 -1
  83. package/dist/chunk-R3OZ6JHQ.cjs.map +0 -1
  84. package/dist/chunk-YXG3UJVG.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/WrittenResponse/WrittenResponse.tsx","../src/components/WrittenResponse/index.tsx"],"names":["WrittenResponse","jsx"],"mappings":";;;;;;;AAwHA,SAAS,OAAO,QAAA,EAA+C;AAC7D,EAAA,OAAO,aAAa,MAAA,IAAa,QAAA,CAAS,IAAA,KAAS,kBAAA,GAAqB,SAAS,IAAA,GAAO,EAAA;AAC1F;AAaA,SAAS,iBAAiB,SAAA,EAAmC;AAC3D,EAAA,MAAM,GAAA,GAAM,UAAU,QAAA,KAAa,MAAA,IAAa,UAAU,QAAA,GAAW,CAAA,GAAI,UAAU,QAAA,GAAW,CAAA;AAC9F,EAAA,OAAO,GAAG,IAAA,CAAK,KAAA,CAAQ,UAAU,KAAA,GAAmB,GAAA,GAAO,GAAG,CAAC,CAAA,CAAA,CAAA;AACjE;AASA,SAAS,SAAA,CAAU,EAAE,KAAA,EAAM,EAA2B;AACpD,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,IAAA,KAAA,CAAM,2BAAW,GAAA,CAAC,GAAA,EAAA,EAAE,WAAU,sBAAA,EAAwB,QAAA,EAAA,KAAA,CAAM,UAAS,CAAA,GAAO,IAAA;AAAA,IAC5E,KAAA,CAAM,QAAA,IAAY,KAAA,CAAM,QAAA,CAAS,SAAS,CAAA,mBACzC,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,gBAAA,EACX,QAAA,EAAA,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,CAAC,SAAA,qBACnB,IAAA;AAAA,MAAC,IAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,iBAAA;AAAA,QAEV,SAAA,EAAS,MAAA,CAAO,SAAA,CAAU,aAAA,KAAkB,IAAI,CAAA;AAAA,QAEhD,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,sBAAA,EAAwB,QAAA,EAAA,SAAA,CAAU,IAAA,EAAK,CAAA;AAAA,UACtD,SAAA,CAAU,kBAAkB,IAAA,mBAC3B,GAAA,CAAC,UAAK,SAAA,EAAU,uBAAA,EAAwB,QAAA,EAAA,gBAAA,EAAc,CAAA,mBAEtD,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,uBAAA,EACb,QAAA,EAAA,SAAA,CAAU,SACR,OAAO,SAAA,CAAU,UAAU,QAAA,GAAW,gBAAA,CAAiB,SAAS,CAAA,GAAI,EAAA,CAAA,EACzE,CAAA;AAAA,UAED,SAAA,CAAU,0BACT,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,yBAAA,EAA2B,QAAA,EAAA,SAAA,CAAU,SAAQ,CAAA,GAC3D;AAAA;AAAA,OAAA;AAAA,MAdC,SAAA,CAAU;AAAA,KAgBlB,GACH,CAAA,GACE,IAAA;AAAA,IACH,MAAM,WAAA,IAAe,KAAA,CAAM,YAAY,MAAA,GAAS,CAAA,uBAC9C,IAAA,EAAA,EAAG,SAAA,EAAU,mBAAA,EACX,QAAA,EAAA,KAAA,CAAM,YAAY,GAAA,CAAI,CAAC,+BACtB,IAAA,CAAC,IAAA,EAAA,EAAG,WAAU,kBAAA,EACZ,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2BAAA,EAA6B,QAAA,EAAA,UAAA,CAAW,QAAA,EAAS,CAAA;AAAA,MAAO,GAAA;AAAA,sBACvE,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,4BAAA,EAA8B,qBAAW,SAAA,EAAU,CAAA;AAAA,MACjE,UAAA,CAAW,8BACV,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,8BAAA,EAAgC,QAAA,EAAA,UAAA,CAAW,aAAY,CAAA,GACrE;AAAA,KAAA,EAAA,EALgC,CAAA,EAAG,WAAW,QAAQ,CAAA,CAAA,EAAI,WAAW,SAAS,CAAA,CAMpF,CACD,CAAA,EACH,CAAA,GACE,IAAA;AAAA,IACH,KAAA,CAAM,wBAAwB,IAAA,mBAC7B,GAAA,CAAC,OAAE,SAAA,EAAU,mBAAA,EAAoB,yDAA2C,CAAA,GAC1E;AAAA,GAAA,EACN,CAAA;AAEJ;AAOA,SAAS,cAAA,CAAe,EAAE,OAAA,EAAQ,EAA6B;AAC7D,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,QAAA,IAAY,OAAA,CAAQ,WAAW,QAAA,EAAU;AAI9D,IAAA,MAAM,UACJ,OAAA,CAAQ,QAAA,GAAW,CAAA,GACf,IAAA,CAAK,MAAO,OAAA,CAAQ,KAAA,GAAQ,OAAA,CAAQ,QAAA,GAAY,GAAG,CAAA,GACnD,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,QAAQ,GAAG,CAAA;AACpC,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,eAAA;AAAA,QACV,eAAa,OAAA,CAAQ,MAAA;AAAA,QACrB,aAAA,EAAa,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA;AAAA,QAElC,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,GAAA,EAAA,EAAE,WAAU,aAAA,EAAc,QAAA,EAAA;AAAA,YAAA,QAAA;AAAA,YAClB,OAAA;AAAA,YAAQ,KAAA;AAAA,YAAI,OAAA,CAAQ,SAAS,SAAA,GAAY;AAAA,WAAA,EAClD,CAAA;AAAA,UACC,QAAQ,MAAA,KAAW,QAAA,mBAClB,GAAA,CAAC,SAAA,EAAA,EAAU,OAAO,OAAA,CAAQ,KAAA,EAAO,CAAA,GAC/B,OAAA,CAAQ,2BACV,GAAA,CAAC,GAAA,EAAA,EAAE,WAAU,sBAAA,EAAwB,QAAA,EAAA,OAAA,CAAQ,UAAS,CAAA,GACpD;AAAA;AAAA;AAAA,KACN;AAAA,EAEJ;AACA,EAAA,IAAI,OAAA,CAAQ,WAAW,UAAA,EAAY;AACjC,IAAA,uBACE,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,eAAA,EAAgB,aAAA,EAAY,UAAA,EACzC,QAAA,kBAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,aAAA,EAAc,QAAA,EAAA,yDAAA,EAAuD,CAAA,EACpF,CAAA;AAAA,EAEJ;AAGA,EAAA,uBACE,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,eAAA,EAAgB,eAAY,YAAA,EAAa,aAAA,EAAa,OAAA,CAAQ,MAAA,EAC3E,QAAA,kBAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,aAAA,EAAc,gDAAkC,CAAA,EAC/D,CAAA;AAEJ;AAEO,SAAS,eAAA,CAAgB;AAAA,EAC9B,IAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA;AAAA,EACA,YAAA;AAAA,EACA,gBAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA,GAAa,UAAA;AAAA,EACb,OAAA;AAAA,EACA,YAAA;AAAA,EACA,aAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,EAAyB;AAGvB,EAAA,MAAM,QAAA,GAAW,QAAQ,MAAM;AAC7B,IAAA,IAAI,CAAC,eAAc,EAAG;AACpB,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,MAAM,MAAA,GAAS,gBAAA,CAAiB,kBAAA,EAAoB,IAAI,CAAA;AACxD,IAAA,OAAO,OAAO,OAAA,GAAU,IAAA,GAAO,IAAI,mBAAA,CAAoB,kBAAA,EAAoB,OAAO,MAAM,CAAA;AAAA,EAC1F,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,QAAA,EAAU,YAAA,EAAc,OAAM,GAAI,gBAAA;AAAA,IACtD,gBAAA,KAAqB,OAAO,WAAA,GAAc;AAAA,GAC5C;AACA,EAAA,MAAM,eAAe,KAAA,KAAU,MAAA;AAC/B,EAAA,MAAM,CAAC,WAAW,YAAY,CAAA,GAAI,SAAiB,MAAM,MAAA,CAAO,YAAY,CAAC,CAAA;AAC7E,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAwB,IAAI,CAAA;AAO1D,EAAA,MAAM,UAAA,GAAa,QAAQ,MAAM;AAC/B,IAAA,IAAI,YAAA,KAAiB,MAAA,IAAa,OAAO,IAAA,CAAK,eAAe,QAAA,EAAU;AACrE,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,YAAA,CAAa,KAAK,UAAU,CAAA;AAAA,EACrC,CAAA,EAAG,CAAC,IAAA,CAAK,UAAA,EAAY,YAAY,CAAC,CAAA;AAIlC,EAAA,MAAM,cAAA,GAAiB,MAAA,CAAe,MAAA,CAAO,YAAY,CAAC,CAAA;AAC1D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,cAAA,CAAe,OAAA,GAAU,OAAO,YAAY,CAAA;AAAA,EAC9C,CAAA,EAAG,CAAC,YAAY,CAAC,CAAA;AASjB,EAAA,MAAM,mBAAA,GAAsB,OAAO,gBAAgB,CAAA;AACnD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,mBAAA,CAAoB,OAAA,GAAU,gBAAA;AAAA,EAChC,CAAA,EAAG,CAAC,gBAAgB,CAAC,CAAA;AAQrB,EAAA,MAAM,WAAA,GAAc,OAAO,IAAI,CAAA;AAC/B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,WAAA,CAAY,YAAY,IAAA,EAAM;AAChC,MAAA;AAAA,IACF;AACA,IAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,IAAA,YAAA,CAAa,eAAe,OAAO,CAAA;AACnC,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,KAAA,CAAM,mBAAA,CAAoB,OAAA,KAAY,IAAA,GAAO,WAAA,GAAc,MAAM,CAAA;AAAA,EACnE,CAAA,EAAG,CAAC,IAAA,EAAM,KAAK,CAAC,CAAA;AAEhB,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,MAAM,QAAA;AAAA,EACR;AAEA,EAAA,MAAM,SAAS,UAAA,KAAe,MAAA;AAC9B,EAAA,MAAM,WAAW,UAAA,KAAe,QAAA;AAEhC,EAAA,MAAM,IAAA,GAAO,YAAA,GAAe,MAAA,CAAO,KAAK,CAAA,GAAI,SAAA;AAC5C,EAAA,MAAM,YAAY,KAAA,KAAU,WAAA;AAC5B,EAAA,MAAM,QAAA,GAAW,QAAA,KAAa,IAAA,IAAQ,SAAA,IAAa,QAAA;AACnD,EAAA,MAAM,SAAA,GAAY,WAAW,IAAI,CAAA;AACjC,EAAA,MAAM,YAAA,GAAe,SAAA,IAAa,IAAA,CAAK,QAAA,IAAY,aAAa,IAAA,CAAK,QAAA;AAErE,EAAA,MAAM,YAAA,GAAe,CAAC,IAAA,KAAuB;AAC3C,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA;AAAA,IACF;AACA,IAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,MAAA,KAAA,EAAM;AAAA,IACR;AAGA,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,YAAA,CAAa,IAAI,CAAA;AAAA,IACnB;AACA,IAAA,MAAM,aAAA,GAAgB,WAAW,IAAI,CAAA;AACrC,IAAA,QAAA,GAAW,EAAE,IAAA,EAAM,kBAAA,EAAoB,MAAM,IAAA,EAAM,SAAA,EAAW,eAAe,CAAA;AAC7E,IAAA,aAAA,GAAgB;AAAA,MACd,IAAA,EAAM,cAAA;AAAA,MACN,YAAY,IAAA,CAAK,EAAA;AAAA,MACjB,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,MACpB,OAAA,EAAS,EAAE,SAAA,EAAW,aAAA;AAAc,KACrC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,KAAA,KAAiC;AACrD,IAAA,KAAA,CAAM,cAAA,EAAe;AACrB,IAAA,IAAI,QAAA,IAAY,IAAA,CAAK,IAAA,EAAK,CAAE,WAAW,CAAA,EAAG;AACxC,MAAA;AAAA,IACF;AACA,IAAA,IAAI,cAAA,GAAiB,SAAA;AACrB,IAAA,IAAI,iBAAA,GAAoB,YAAA;AAMxB,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,WAAA,GAAc,SAAS,IAAA,EAAM,EAAE,MAAM,kBAAA,EAAoB,IAAA,EAAM,WAAW,CAAA;AAChF,MAAA,MAAM,OAAA,GAAU,WAAA,CAAY,MAAA,KAAW,UAAA,GAAa,YAAY,OAAA,GAAU,MAAA;AAC1E,MAAA,IAAI,OAAO,OAAA,EAAS,SAAA,KAAc,QAAA,EAAU;AAC1C,QAAA,cAAA,GAAiB,OAAA,CAAQ,SAAA;AAAA,MAC3B;AACA,MAAA,IAAI,OAAO,OAAA,EAAS,gBAAA,KAAqB,SAAA,EAAW;AAClD,QAAA,iBAAA,GAAoB,OAAA,CAAQ,gBAAA;AAAA,MAC9B;AAAA,IACF;AACA,IAAA,QAAA,EAAS;AACT,IAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,IAAA,MAAM,aAAA,GAAgB,YAAY,uBAAA,CAAwB;AAAA,MACxD,KAAA,EAAO,eAAA;AAAA,MACP,MAAA,EAAQ;AAAA,QACN,EAAA,EAAI,WAAA,CAAY,IAAA,CAAK,EAAE,CAAA;AAAA,QACvB,IAAA,EAAM,EAAE,CAAC,IAAA,CAAK,UAAU,OAAO,GAAG,KAAK,KAAA,EAAM;AAAA,QAC7C,IAAA,EAAM,qDAAA;AAAA,QACN,eAAA,EAAiB;AAAA,OACnB;AAAA,MACA,WAAA,EAAa,SAAA;AAAA,MACb,QAAA,EAAU,IAAA;AAAA,MACV,gBAAA,EAAkB;AAAA,QAChB,uCAAA,EAAyC,cAAA;AAAA,QACzC,+CAAA,EAAiD;AAAA;AACnD,KACD,CAAA;AAGD,IAAA,QAAA,GAAW,EAAE,IAAA,EAAM,kBAAA,EAAoB,IAAA,EAAM,SAAA,EAAW,gBAAgB,CAAA;AACxE,IAAA,WAAA,GAAc;AAAA,MACZ,IAAA;AAAA,MACA,SAAA,EAAW,cAAA;AAAA,MACX,gBAAA,EAAkB,iBAAA;AAAA,MAClB,SAAA;AAAA,MACA;AAAA,KACD,CAAA;AACD,IAAA,UAAA,CAAW,+EAA+E,CAAA;AAC1F,IAAA,aAAA,GAAgB;AAAA,MACd,IAAA,EAAM,WAAA;AAAA,MACN,YAAY,IAAA,CAAK,EAAA;AAAA,MACjB,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,MACpB,OAAA,EAAS,EAAE,SAAA,EAAW,cAAA,EAAgB,kBAAkB,iBAAA;AAAkB,KAC3E,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,QAAA,GAAW,CAAA,EAAG,IAAA,CAAK,EAAE,CAAA,OAAA,CAAA;AAC3B,EAAA,MAAM,SAAA,GAAY,CAAA,EAAG,IAAA,CAAK,EAAE,CAAA,QAAA,CAAA;AAC5B,EAAA,MAAM,WAAA,GACJ,IAAA,CAAK,QAAA,GAAW,CAAA,GAAI,CAAA,EAAG,IAAA,CAAK,QAAQ,CAAA,MAAA,EAAI,IAAA,CAAK,QAAQ,CAAA,MAAA,CAAA,GAAW,CAAA,MAAA,EAAS,KAAK,QAAQ,CAAA,MAAA,CAAA;AAExF,EAAA,uBACE,IAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,OAAA;AAAA,MACV,cAAY,IAAA,CAAK,KAAA;AAAA,MACjB,IAAA,EAAM,MAAA;AAAA,MACN,kBAAA,EAAkB,UAAA;AAAA,MAClB,KAAA,EAAO,KAAA;AAAA,MACP,QAAA,EAAU,YAAA;AAAA,MAET,QAAA,EAAA;AAAA,QAAA,IAAA,CAAK,wBAAQ,GAAA,CAAC,aAAA,EAAA,EAAc,KAAA,EAAO,IAAA,CAAK,OAAO,CAAA,GAAK,IAAA;AAAA,QACpD,UAAA,KAAe,uBACd,GAAA,CAAC,GAAA,EAAA,EAAE,WAAU,cAAA,EAAe,EAAA,EAAI,QAAA,EAC7B,QAAA,EAAA,IAAA,CAAK,MAAA,EACR,CAAA;AAAA;AAAA,0BAGA,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAU,cAAA;AAAA,cACV,EAAA,EAAI,QAAA;AAAA,cAEJ,uBAAA,EAAyB,EAAE,MAAA,EAAQ,UAAA;AAAW;AAAA;AAChD,SAAA;AAAA,wBAEF,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,gBAAA;AAAA,YACV,iBAAA,EAAiB,QAAA;AAAA,YACjB,kBAAA,EAAkB,SAAA;AAAA,YAClB,KAAA,EAAO,IAAA;AAAA,YACP,UAAU,CAAC,KAAA,KAAU,YAAA,CAAa,KAAA,CAAM,OAAO,KAAK,CAAA;AAAA,YAIpD,QAAA,EAAU,CAAC,QAAA,IAAY,QAAA;AAAA,YACvB,QAAA,EAAU,QAAA;AAAA,YACV,IAAA,EAAM;AAAA;AAAA,SACR;AAAA,wBACA,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,eAAA;AAAA,YACV,EAAA,EAAI,SAAA;AAAA,YACJ,WAAA,EAAU,QAAA;AAAA,YACV,oBAAA,EAAoB,YAAA;AAAA,YAEnB,QAAA,EAAA;AAAA,cAAA,SAAA;AAAA,cAAU,GAAA;AAAA,cAAE,SAAA,KAAc,IAAI,MAAA,GAAS,OAAA;AAAA,cAAQ,IAAA;AAAA,cAAG,WAAA;AAAA,cAAY;AAAA;AAAA;AAAA,SACjE;AAAA,QACC,WAAW,IAAA,mBACV,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,QAAA;AAAA,YACL,SAAA,EAAU,cAAA;AAAA,YACV,QAAA,EAAU,QAAA,IAAY,IAAA,CAAK,IAAA,GAAO,MAAA,KAAW,CAAA;AAAA,YAC9C,QAAA,EAAA;AAAA;AAAA,SAED;AAAA,wBAEF,GAAA,CAAC,cAAA,EAAA,EAAe,EAAA,EAAI,CAAA,EAAG,KAAK,EAAE,CAAA,SAAA,CAAA,EAC3B,QAAA,EAAA,QAAA,GAAW,OAAA,mBAAU,GAAA,CAAC,cAAA,EAAA,EAAe,OAAA,EAAkB,CAAA,GAAK,OAAO,OAAA,EACtE;AAAA;AAAA;AAAA,GACF;AAEJ;AC9cO,SAASA,iBAAgB,KAAA,EAA6B;AAC3D,EAAA,uBACEC,GAAAA,CAAC,qBAAA,EAAA,EAAsB,aAAA,EAAe,KAAA,CAAM,IAAA,EAAM,KAAA,EAChD,QAAA,kBAAAA,GAAAA,CAAC,eAAA,EAAA,EAAqB,GAAG,KAAA,EAAO,CAAA,EAClC,CAAA;AAEJ","file":"chunk-MDZMKC27.js","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n type CriterionScore,\n countWords,\n evaluate,\n type GradeRecord,\n type InteractionEvent,\n type ItemOutcome,\n type LearnerResponse,\n type ThemeTokens,\n validateActivity,\n type WrittenResponseData,\n type XAPIStatement,\n xAPIBuilder,\n} from '@intellectif/lk-core';\nimport { type CSSProperties, useEffect, useMemo, useRef, useState } from 'react';\nimport { useActivityState } from '../../hooks/useActivityState.js';\nimport { ANONYMOUS_ACTOR, isDevelopment, objectIdFor } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport type { HtmlSanitizer, Renderable, RenderMode } from '../types.js';\n\n/**\n * Payload delivered when the learner submits a written response. Grading is\n * DEFERRED (asynchronous AI/human grading), so there is no score here — and\n * deliberately no fake one: an ungraded submission must never be shown or\n * stored as a 0. The consumer persists `text` and grades it out-of-band.\n */\nexport interface WrittenResponseSubmission {\n /** The submitted text, verbatim. */\n text: string;\n /** Word count of `text`, recomputed with the canonical `countWords()`. */\n wordCount: number;\n /** Whether `wordCount` falls within the activity's `[minWords, maxWords]`. */\n withinWordBounds: boolean;\n /** Time in milliseconds from first interaction to submission. */\n timeSpent: number;\n /** The SUBMITTED-verb xAPI statement built for this submission (no score fields). */\n xapiStatement: XAPIStatement;\n}\n\n/**\n * Props for {@link WrittenResponse}. Mirrors `ActivityProps` except for the\n * completion callback: a deferred-grading activity completes with a\n * {@link WrittenResponseSubmission} (`onSubmitted`), not a scored\n * `ActivityResult` (`onComplete`) — deviation from Req 22.8 recorded in the\n * roadmap (§3.1): fabricating a score of 0 for ungraded work is the exact\n * bug this type exists to fix. Everything else — controlled value, render\n * mode, server outcome, rich text — follows the shared `ActivityProps`\n * contract verbatim.\n */\nexport interface WrittenResponseProps {\n /** Activity content. Accepts a `redact()` projection in `exam` mode. */\n data: Renderable<WrittenResponseData>;\n /**\n * Called on submit with the ungraded submission (text, recomputed word\n * count, bounds flag, timing, xAPI statement). Fires in `practice` and\n * `exam` mode, after `onSubmit`; never in `review`.\n *\n * Optional since lk-react 2.1.0 so a `review`-mode render (which has no submit\n * control) and an exam runner that only wants the raw response through\n * `onSubmit` do not have to pass a no-op. Supply it in `practice`/`exam`\n * unless `onSubmit` already captures everything you persist.\n */\n onSubmitted?: (submission: WrittenResponseSubmission) => void;\n /**\n * Called on submit with the raw learner response and no grade, BEFORE\n * `onSubmitted`. This is the only submit callback an exam runner needs:\n * the server grades the text and returns an {@link ItemOutcome} later.\n */\n onSubmit?: (response: LearnerResponse) => void;\n /**\n * Controlled value: the learner's current response. When present the\n * textarea renders from it (never from internal state) and every keystroke\n * goes out through `onChange` — restore an in-progress attempt, autosave a\n * delta, or drive a review render with it.\n */\n value?: LearnerResponse;\n /** Initial response for an uncontrolled component (ignored when `value` is set). */\n defaultValue?: LearnerResponse;\n /**\n * Mount as already submitted — read at mount only. Without it a resumed\n * attempt reopens an essay the learner had already submitted as editable\n * and re-submittable.\n */\n defaultSubmitted?: boolean;\n /** Fires on every change to the learner's response. Required for a controlled component. */\n onChange?: (response: LearnerResponse) => void;\n /**\n * Presentation mode, default `practice`. `practice` and `exam` are nearly\n * identical here — a written response is never graded client-side, so\n * there is no local score to withhold; `exam` additionally never calls\n * `evaluate()` and renders happily from a `redact()` projection.\n * `review` is read-only and shows the server's `outcome`.\n */\n renderMode?: RenderMode;\n /**\n * Server-computed outcome, rendered by `review` mode: `deferred` shows a\n * \"not graded yet\" affordance, `scored` shows the returned grade and\n * feedback. Ignored in other modes — this component never manufactures an\n * outcome of its own.\n */\n outcome?: ItemOutcome;\n /** Renders `data.promptHtml` when provided. See `HtmlSanitizer`. */\n sanitizeHtml?: HtmlSanitizer;\n onInteraction?: (event: InteractionEvent) => void;\n /** Per-instance token overrides, applied as inline CSS vars on the root. */\n theme?: Partial<ThemeTokens>;\n locale?: string;\n disabled?: boolean;\n}\n\n/**\n * Reads the text out of a learner response. Another activity type's response\n * shape (the `value`/`defaultValue` props take the `LearnerResponse` union,\n * so a generic runner can hand any of them over) reads as empty rather than\n * throwing mid-exam.\n */\nfunction textOf(response: LearnerResponse | undefined): string {\n return response !== undefined && response.type === 'written-response' ? response.text : '';\n}\n\n/**\n * One criterion's score as a percentage of its OWN maximum.\n *\n * `CriterionScore.maxScore` defaults to 1 — the SDK's scaled convention — but\n * a grader may work out of 100, or out of 9 for a CEFR band, and\n * `gradeFromRubric` deliberately stores those judgements verbatim so a grade\n * stays auditable in the units the grader actually used. It normalises when it\n * computes the total; anything DISPLAYING a criterion has to normalise too, or\n * a perfectly ordinary 82/100 renders as \"8200%\". This is the same guard the\n * overall score already applies against `outcome.maxScore`.\n */\nfunction criterionPercent(criterion: CriterionScore): string {\n const max = criterion.maxScore !== undefined && criterion.maxScore > 0 ? criterion.maxScore : 1;\n return `${Math.round(((criterion.score as number) / max) * 100)}%`;\n}\n\n/**\n * Renders the server-computed outcome in `review` mode. The grade shown here\n * always came back from the asynchronous grader — nothing on this path scores,\n * infers, or defaults a grade. A `written-response` outcome carries no\n * per-item `details` (there are no options or blanks to mark), so the render\n * is the scaled score, the pass state, and the grader's feedback.\n */\nfunction GradeBody({ grade }: { grade: GradeRecord }) {\n return (\n <>\n {grade.feedback ? <p className=\"lk-wr-grade-feedback\">{grade.feedback}</p> : null}\n {grade.criteria && grade.criteria.length > 0 ? (\n <ul className=\"lk-wr-criteria\">\n {grade.criteria.map((criterion) => (\n <li\n className=\"lk-wr-criterion\"\n key={criterion.name}\n data-na={String(criterion.notApplicable === true)}\n >\n <span className=\"lk-wr-criterion-name\">{criterion.name}</span>\n {criterion.notApplicable === true ? (\n <span className=\"lk-wr-criterion-score\">Not applicable</span>\n ) : (\n <span className=\"lk-wr-criterion-score\">\n {criterion.band ??\n (typeof criterion.score === 'number' ? criterionPercent(criterion) : '')}\n </span>\n )}\n {criterion.comment ? (\n <span className=\"lk-wr-criterion-comment\">{criterion.comment}</span>\n ) : null}\n </li>\n ))}\n </ul>\n ) : null}\n {grade.corrections && grade.corrections.length > 0 ? (\n <ul className=\"lk-wr-corrections\">\n {grade.corrections.map((correction) => (\n <li className=\"lk-wr-correction\" key={`${correction.original}:${correction.corrected}`}>\n <del className=\"lk-wr-correction-original\">{correction.original}</del>{' '}\n <ins className=\"lk-wr-correction-corrected\">{correction.corrected}</ins>\n {correction.explanation ? (\n <span className=\"lk-wr-correction-explanation\">{correction.explanation}</span>\n ) : null}\n </li>\n ))}\n </ul>\n ) : null}\n {grade.requiresHumanReview === true ? (\n <p className=\"lk-wr-review-flag\">This grade is awaiting review by a teacher.</p>\n ) : null}\n </>\n );\n}\n\n/**\n * Renders the server-computed outcome in `review` mode. The grade shown here\n * always came back from the grader — nothing on this path scores, infers, or\n * defaults a grade.\n */\nfunction OutcomeSummary({ outcome }: { outcome: ItemOutcome }) {\n if (outcome.status === 'scored' || outcome.status === 'graded') {\n // `score` is scaled [0–1] against `maxScore`; normalising by `maxScore`\n // is a no-op for the canonical maxScore of 1 and keeps an unscaled\n // grader payload (8.5 / 10) from rendering as 850%.\n const percent =\n outcome.maxScore > 0\n ? Math.round((outcome.score / outcome.maxScore) * 100)\n : Math.round(outcome.score * 100);\n return (\n <div\n className=\"lk-wr-outcome\"\n data-status={outcome.status}\n data-passed={String(outcome.passed)}\n >\n <p className=\"lk-wr-grade\">\n Score {percent}%. {outcome.passed ? 'Passed.' : 'Not passed.'}\n </p>\n {outcome.status === 'graded' ? (\n <GradeBody grade={outcome.grade} />\n ) : outcome.feedback ? (\n <p className=\"lk-wr-grade-feedback\">{outcome.feedback}</p>\n ) : null}\n </div>\n );\n }\n if (outcome.status === 'deferred') {\n return (\n <div className=\"lk-wr-outcome\" data-status=\"deferred\">\n <p className=\"lk-wr-grade\">Not graded yet. This response is waiting for its grade.</p>\n </div>\n );\n }\n // `unscorable` reasons are developer-facing strings; surface the state, not\n // the reason, and leave it on the element for diagnostics.\n return (\n <div className=\"lk-wr-outcome\" data-status=\"unscorable\" data-reason={outcome.reason}>\n <p className=\"lk-wr-grade\">This response could not be graded.</p>\n </div>\n );\n}\n\nexport function WrittenResponse({\n data,\n onSubmitted,\n onSubmit,\n value,\n defaultValue,\n defaultSubmitted,\n onChange,\n renderMode = 'practice',\n outcome,\n sanitizeHtml,\n onInteraction,\n theme,\n locale,\n disabled,\n}: WrittenResponseProps) {\n // Dev-only boundary validation (Req 2.3), same convention as MC/FIB. The\n // content schema is loose, so a `redact()` projection validates too.\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n const result = validateActivity('written-response', data);\n return result.success ? null : new ActivitySchemaError('written-response', result.errors);\n }, [data]);\n\n const { state, start, complete, getTimeSpent, reset } = useActivityState(\n defaultSubmitted === true ? 'completed' : 'idle',\n );\n const isControlled = value !== undefined;\n const [innerText, setInnerText] = useState<string>(() => textOf(defaultValue));\n const [summary, setSummary] = useState<string | null>(null);\n\n /**\n * Rich text is rendered ONLY through a caller-supplied sanitiser, and only\n * when the author actually shipped the HTML sidecar. Without both, the\n * plain-text prompt is rendered as an escaped React child.\n */\n const promptHtml = useMemo(() => {\n if (sanitizeHtml === undefined || typeof data.promptHtml !== 'string') {\n return null;\n }\n return sanitizeHtml(data.promptHtml);\n }, [data.promptHtml, sanitizeHtml]);\n\n // Latest seed text, kept in a ref so `defaultValue` is NOT a reset trigger:\n // callers pass an object literal, whose identity changes every render.\n const defaultTextRef = useRef<string>(textOf(defaultValue));\n useEffect(() => {\n defaultTextRef.current = textOf(defaultValue);\n }, [defaultValue]);\n\n // Reset on data-prop change (Req 3.7): back to the seed (empty when there\n // is none — the v1 behaviour), never the previous activity's draft.\n // biome-ignore lint/correctness/useExhaustiveDependencies: data is the reset trigger (Req 3.7)\n // Mirror `defaultSubmitted` so the reset below returns to the SEEDED state.\n // Resetting unconditionally to idle unlocked an item the learner had already\n // committed — which made `defaultSubmitted` a no-op here, since this effect\n // runs right after the first paint.\n const defaultSubmittedRef = useRef(defaultSubmitted);\n useEffect(() => {\n defaultSubmittedRef.current = defaultSubmitted;\n }, [defaultSubmitted]);\n\n // Identity guard so the MOUNT run is a no-op. Without it this effect fires\n // after the first paint and undoes every seed it was just given; with it,\n // only a real `data` change resets. A parent that rebuilds structurally\n // identical entries in render (`activities={raw.map(redact)}`) must not\n // unlock or revert a restored answer — the pager already remounts a slot\n // whose activity actually changed, via its key.\n const lastDataRef = useRef(data);\n useEffect(() => {\n if (lastDataRef.current === data) {\n return;\n }\n lastDataRef.current = data;\n setInnerText(defaultTextRef.current);\n setSummary(null);\n reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [data, reset]);\n\n if (devError) {\n throw devError;\n }\n\n const isExam = renderMode === 'exam';\n const isReview = renderMode === 'review';\n\n const text = isControlled ? textOf(value) : innerText;\n const submitted = state === 'completed';\n const inactive = disabled === true || submitted || isReview;\n const wordCount = countWords(text);\n const withinBounds = wordCount >= data.minWords && wordCount <= data.maxWords;\n\n const handleChange = (next: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n // A controlled component renders from `props.value` only; the caller's\n // state is the single source of truth for the text.\n if (!isControlled) {\n setInnerText(next);\n }\n const nextWordCount = countWords(next);\n onChange?.({ type: 'written-response', text: next, wordCount: nextWordCount });\n onInteraction?.({\n type: 'text-changed',\n activityId: data.id,\n timestamp: Date.now(),\n payload: { wordCount: nextWordCount },\n });\n };\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive || text.trim().length === 0) {\n return;\n }\n let finalWordCount = wordCount;\n let finalWithinBounds = withinBounds;\n // `exam` mode must not call evaluate(): the component does not grade, and\n // a redacted projection carries no scoring input anyway. The two facts\n // evaluate() returns for a deferred type (recomputed word count, bounds)\n // are exactly what is computed above, so the payload is identical either\n // way; practice keeps going through core so the numbers stay canonical.\n if (!isExam) {\n const itemOutcome = evaluate(data, { type: 'written-response', text, wordCount });\n const partial = itemOutcome.status === 'deferred' ? itemOutcome.partial : undefined;\n if (typeof partial?.wordCount === 'number') {\n finalWordCount = partial.wordCount;\n }\n if (typeof partial?.withinWordBounds === 'boolean') {\n finalWithinBounds = partial.withinWordBounds;\n }\n }\n complete();\n const timeSpent = getTimeSpent();\n const xapiStatement = xAPIBuilder.buildSubmittedStatement({\n actor: ANONYMOUS_ACTOR,\n object: {\n id: objectIdFor(data.id),\n name: { [data.locale ?? 'en-US']: data.title },\n type: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n interactionType: 'long-fill-in',\n },\n timeSpentMs: timeSpent,\n response: text,\n resultExtensions: {\n 'urn:learning-kit:extension:word-count': finalWordCount,\n 'urn:learning-kit:extension:within-word-bounds': finalWithinBounds,\n },\n });\n // Raw response first (an exam runner persists this), then the fuller\n // ungraded submission — mirroring `onSubmit` before `onComplete`.\n onSubmit?.({ type: 'written-response', text, wordCount: finalWordCount });\n onSubmitted?.({\n text,\n wordCount: finalWordCount,\n withinWordBounds: finalWithinBounds,\n timeSpent,\n xapiStatement,\n });\n setSummary('Response submitted. It will be graded and your result will appear here later.');\n onInteraction?.({\n type: 'submitted',\n activityId: data.id,\n timestamp: Date.now(),\n payload: { wordCount: finalWordCount, withinWordBounds: finalWithinBounds },\n });\n };\n\n const promptId = `${data.id}-prompt`;\n const counterId = `${data.id}-counter`;\n const boundsLabel =\n data.minWords > 0 ? `${data.minWords}–${data.maxWords} words` : `up to ${data.maxWords} words`;\n\n return (\n <form\n className=\"lk-wr\"\n aria-label={data.title}\n lang={locale}\n data-render-mode={renderMode}\n style={theme as CSSProperties | undefined}\n onSubmit={handleSubmit}\n >\n {data.media ? <ActivityMedia media={data.media} /> : null}\n {promptHtml === null ? (\n <p className=\"lk-wr-prompt\" id={promptId}>\n {data.prompt}\n </p>\n ) : (\n // A div, not a p: sanitised author HTML may contain block content.\n <div\n className=\"lk-wr-prompt\"\n id={promptId}\n // biome-ignore lint/security/noDangerouslySetInnerHtml: rendered only through the caller-supplied HtmlSanitizer — the SDK never injects unsanitised markup\n dangerouslySetInnerHTML={{ __html: promptHtml }}\n />\n )}\n <textarea\n className=\"lk-wr-textarea\"\n aria-labelledby={promptId}\n aria-describedby={counterId}\n value={text}\n onChange={(event) => handleChange(event.target.value)}\n // `review` uses readOnly rather than disabled: the submitted essay\n // must stay focusable, selectable and scrollable for a screen reader\n // and a keyboard, while remaining uneditable.\n disabled={!isReview && inactive}\n readOnly={isReview}\n rows={8}\n />\n <div\n className=\"lk-wr-counter\"\n id={counterId}\n aria-live=\"polite\"\n data-within-bounds={withinBounds}\n >\n {wordCount} {wordCount === 1 ? 'word' : 'words'} ({boundsLabel})\n </div>\n {isReview ? null : (\n <button\n type=\"submit\"\n className=\"lk-wr-submit\"\n disabled={inactive || text.trim().length === 0}\n >\n Submit\n </button>\n )}\n <FeedbackRegion id={`${data.id}-feedback`}>\n {isReview ? outcome ? <OutcomeSummary outcome={outcome} /> : null : summary}\n </FeedbackRegion>\n </form>\n );\n}\n","'use client';\n\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport {\n WrittenResponse as WrittenResponseCore,\n type WrittenResponseProps,\n} from './WrittenResponse.js';\n\nexport type { HtmlSanitizer, Renderable, RenderMode } from '../types.js';\nexport type { WrittenResponseProps, WrittenResponseSubmission } from './WrittenResponse.js';\n\n/**\n * Public Written Response activity: a labelled textarea with a live word\n * counter for free-text writing graded asynchronously (AI or human). The core\n * is wrapped in `ActivityErrorBoundary` so a render failure (incl. the dev\n * schema-validation throw) degrades to an accessible fallback.\n */\nexport function WrittenResponse(props: WrittenResponseProps) {\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title}>\n <WrittenResponseCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
4
  var chunk4HZ7LQIH_cjs = require('./chunk-4HZ7LQIH.cjs');
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
4
  var chunkPGEG7UZJ_cjs = require('./chunk-PGEG7UZJ.cjs');
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
4
  var react = require('react');
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
4
  var react = require('react');
@@ -77,5 +78,5 @@ function useXAPI(config) {
77
78
  }
78
79
 
79
80
  exports.useXAPI = useXAPI;
80
- //# sourceMappingURL=chunk-IYLQSNLE.cjs.map
81
- //# sourceMappingURL=chunk-IYLQSNLE.cjs.map
81
+ //# sourceMappingURL=chunk-PNNUPUH4.cjs.map
82
+ //# sourceMappingURL=chunk-PNNUPUH4.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useXAPI.ts"],"names":["useRef","useCallback"],"mappings":"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","file":"chunk-PNNUPUH4.cjs","sourcesContent":["'use client';\n\nimport type { XAPIConfig, XAPIStatement } from '@intellectif/lk-core';\nimport { useCallback, useRef } from 'react';\n\n/** Backoff before retry 1 / 2 / 3 (Req 10.4). */\nconst BACKOFF_MS = [1000, 2000, 4000] as const;\n\n/**\n * Applies the configured learner identity to a statement built by an activity\n * component. Components emit an anonymous placeholder actor and a\n * `urn:learning-kit:activity:*` object id (they cannot know identity — Req\n * 3.1); this hook is the documented place where `XAPIConfig.actor` and\n * `XAPIConfig.activityId` take over. Conservative on purpose: the actor is\n * replaced only when the statement still carries the SDK's anonymous\n * placeholder, and the object id only when it is still the SDK URN — a\n * consumer that already rewrote either keeps its own values.\n */\nfunction applyIdentity(statement: XAPIStatement, cfg: XAPIConfig): XAPIStatement {\n const isAnonymous =\n statement.actor?.account?.name === 'anonymous' &&\n statement.actor.account.homePage === 'https://github.com/intellectif/learning-kit';\n const isSdkObjectId =\n statement.object?.objectType === 'Activity' &&\n typeof statement.object.id === 'string' &&\n statement.object.id.startsWith('urn:learning-kit:activity:');\n\n if (!isAnonymous && !isSdkObjectId) {\n return statement;\n }\n const mappedId =\n isSdkObjectId && cfg.activityId\n ? typeof cfg.activityId === 'function'\n ? cfg.activityId(statement.object.id)\n : cfg.activityId\n : undefined;\n return {\n ...statement,\n ...(isAnonymous && cfg.actor !== undefined ? { actor: cfg.actor } : {}),\n ...(mappedId !== undefined ? { object: { ...statement.object, id: mappedId } } : {}),\n };\n}\n\n/** UTF-8-safe base64 (`btoa` is Latin1-only and breaks on non-ASCII creds). */\nfunction toBase64Utf8(input: string): string {\n const bytes = new TextEncoder().encode(input);\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary);\n}\n\nfunction authHeader(auth: XAPIConfig['auth']): string {\n return auth.type === 'basic'\n ? `Basic ${toBase64Utf8(`${auth.username}:${auth.password}`)}`\n : `Bearer ${auth.token}`;\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport interface UseXAPIResult {\n /**\n * Sends a statement to the configured LRS. **Never rejects** — xAPI is\n * fire-and-forget telemetry that must not break the learner's activity\n * flow. Network errors and 5xx responses are retried (backoff 1s/2s/4s,\n * up to 3 retries); a 4xx fails immediately. Any final failure is delivered\n * to `config.onError`.\n */\n sendStatement: (statement: XAPIStatement) => Promise<void>;\n}\n\n/**\n * Delivers xAPI statements to a Learning Record Store, fire-and-forget.\n *\n * Telemetry must never break the learner's attempt, so\n * {@link UseXAPIResult.sendStatement} **never rejects**: network errors and 5xx\n * responses are retried (backoff 1s / 2s / 4s, up to 3 retries), a 4xx fails\n * immediately, and any final failure is reported to `config.onError` instead of\n * throwing. See `XAPIConfig` in `@intellectif/lk-core` for the options.\n *\n * The hook is also where learner identity is applied. Activity components\n * cannot know who the learner is, so they emit an anonymous placeholder actor\n * and a `urn:learning-kit:activity:*` object id; this hook substitutes\n * `config.actor` and `config.activityId` on the way out — and only while those\n * placeholders are still in place, so a statement you rewrote yourself is left\n * alone.\n *\n * `sendStatement` keeps a stable identity across renders even when `config` is\n * an inline object, so it is safe in a dependency array.\n *\n * ```tsx\n * const { sendStatement } = useXAPI({\n * endpoint: 'https://your-lrs.example/xapi/statements',\n * auth: { type: 'bearer', token: 'YOUR_TOKEN' },\n * activityId: 'https://your-app.example/quiz/capital-jp',\n * actor: { objectType: 'Agent', mbox: 'mailto:learner@example.com' },\n * onError: (e) => console.error('xAPI send failed', e),\n * });\n *\n * <MultipleChoice data={quiz} onComplete={(r) => void sendStatement(r.xapiStatement)} />\n * ```\n */\nexport function useXAPI(config: XAPIConfig): UseXAPIResult {\n // Keep the latest config without changing `sendStatement`'s identity when\n // callers pass an inline config object.\n const configRef = useRef(config);\n configRef.current = config;\n\n const sendStatement = useCallback(async (rawStatement: XAPIStatement): Promise<void> => {\n const cfg = configRef.current;\n const statement = applyIdentity(rawStatement, cfg);\n const maxAttempts = 1 + BACKOFF_MS.length;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n let statusCode: number | undefined;\n let message = 'Request failed';\n\n try {\n const response = await fetch(cfg.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Experience-API-Version': '1.0.3',\n Authorization: authHeader(cfg.auth),\n },\n body: JSON.stringify(statement),\n });\n\n if (response.ok) {\n return;\n }\n\n statusCode = response.status;\n message = `LRS responded with ${response.status}`;\n\n // Only network errors and 5xx are retryable (Req 10.4). A 4xx is a\n // client error — fail immediately rather than waste retries.\n if (response.status < 500) {\n cfg.onError?.({ statement, attempt, statusCode, message });\n return;\n }\n } catch (error) {\n message = error instanceof Error ? error.message : 'Network error';\n }\n\n if (attempt === maxAttempts) {\n cfg.onError?.({\n statement,\n attempt,\n ...(statusCode !== undefined ? { statusCode } : {}),\n message,\n });\n return;\n }\n\n await sleep(BACKOFF_MS[attempt - 1]);\n }\n }, []);\n\n return { sendStatement };\n}\n"]}
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { ActivityMedia } from './chunk-GB5URWM4.js';
2
3
  import { useId, useMemo } from 'react';
3
4
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
4
  var react = require('react');
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { ActivityErrorBoundary, isDevelopment, randomSessionId, FeedbackRegion, ANONYMOUS_ACTOR, objectIdFor } from './chunk-6F4X2NKL.js';
2
3
  import { ActivityMedia } from './chunk-GB5URWM4.js';
3
4
  import { useActivityState } from './chunk-6N43WDVG.js';
@@ -246,5 +247,5 @@ function MultipleChoice2(props) {
246
247
  }
247
248
 
248
249
  export { MultipleChoice2 as MultipleChoice };
249
- //# sourceMappingURL=chunk-5TUTJ4R3.js.map
250
- //# sourceMappingURL=chunk-5TUTJ4R3.js.map
250
+ //# sourceMappingURL=chunk-ZLUMUJUQ.js.map
251
+ //# sourceMappingURL=chunk-ZLUMUJUQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/MultipleChoice/MultipleChoice.tsx","../src/components/MultipleChoice/index.tsx"],"names":["MultipleChoice","jsx"],"mappings":";;;;;;;AA4BA,SAAS,YAAY,QAAA,EAAiD;AACpE,EAAA,OAAO,QAAA,EAAU,IAAA,KAAS,iBAAA,GAAoB,QAAA,CAAS,oBAAoB,EAAC;AAC9E;AAWA,SAAS,cAAA,CAAe,QAAuB,WAAA,EAA+B;AAC5E,EAAA,QAAQ,OAAO,OAAA;AAAS,IACtB,KAAK,SAAA;AAAA,IACL,KAAK,oBAAA;AACH,MAAA,OAAO,IAAA;AAAA,IACT,KAAK,WAAA;AAAA,IACL,KAAK,kBAAA;AACH,MAAA,OAAO,KAAA;AAAA,IACT;AACE,MAAA,OAAO,WAAA,GAAc,MAAA,CAAO,OAAA,GAAU,CAAC,MAAA,CAAO,OAAA;AAAA;AAEpD;AAGA,SAAS,mBAAmB,OAAA,EAAiD;AAC3E,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAA,CAAQ,WAAW,QAAA,EAAU;AAC/B,IAAA,MAAM,UAAU,OAAA,CAAQ,QAAA;AACxB,IAAA,OAAO,SAAS,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,KAAA,GAAQ,GAAG,CAAC,CAAA,GAAA,EAC7C,OAAA,CAAQ,MAAA,GAAS,YAAY,aAC/B,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,OAAO,KAAK,EAAE,CAAA,CAAA;AAAA,EACjC;AACA,EAAA,IAAI,OAAA,CAAQ,WAAW,UAAA,EAAY;AAEjC,IAAA,OAAO,iBAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAA;AACT;AAaO,SAAS,cAAA,CAAe;AAAA,EAC7B,IAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA;AAAA,EACA,YAAA;AAAA,EACA,gBAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA,GAAa,UAAA;AAAA,EACb,OAAA;AAAA,EACA,YAAA;AAAA,EACA,aAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAAwB;AAGtB,EAAA,MAAM,QAAA,GAAW,QAAQ,MAAM;AAC7B,IAAA,IAAI,CAAC,eAAc,EAAG;AACpB,MAAA,OAAO,IAAA;AAAA,IACT;AAKA,IAAA,IAAI,IAAA,CAAK,aAAa,IAAA,EAAM;AAC1B,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,MAAM,MAAA,GAAS,gBAAA,CAAiB,iBAAA,EAAmB,IAAI,CAAA;AACvD,IAAA,OAAO,OAAO,OAAA,GAAU,IAAA,GAAO,IAAI,mBAAA,CAAoB,iBAAA,EAAmB,OAAO,MAAM,CAAA;AAAA,EACzF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAIT,EAAA,MAAM,YAAA,GAAe,OAAsB,IAAI,CAAA;AAC/C,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,QAAA,EAAU,YAAA,EAAc,OAAM,GAAI,gBAAA;AAAA,IACtD,gBAAA,KAAqB,OAAO,WAAA,GAAc;AAAA,GAC5C;AAGA,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAI,QAAA;AAAA,IAAmB,MACnE,YAAY,YAAY;AAAA,GAC1B;AACA,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAwB,IAAI,CAAA;AAe1D,EAAA,MAAM,eAAA,GAAkB,OAAO,YAAY,CAAA;AAC3C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,eAAA,CAAgB,OAAA,GAAU,YAAA;AAAA,EAC5B,CAAA,EAAG,CAAC,YAAY,CAAC,CAAA;AAGjB,EAAA,MAAM,mBAAA,GAAsB,OAAO,gBAAgB,CAAA;AACnD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,mBAAA,CAAoB,OAAA,GAAU,gBAAA;AAAA,EAChC,CAAA,EAAG,CAAC,gBAAgB,CAAC,CAAA;AACrB,EAAA,MAAM,WAAA,GAAc,OAAO,IAAI,CAAA;AAC/B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,WAAA,CAAY,YAAY,IAAA,EAAM;AAChC,MAAA;AAAA,IACF;AACA,IAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,IAAA,oBAAA,CAAqB,WAAA,CAAY,eAAA,CAAgB,OAAO,CAAC,CAAA;AACzD,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,KAAA,CAAM,mBAAA,CAAoB,OAAA,KAAY,IAAA,GAAO,WAAA,GAAc,MAAM,CAAA;AAAA,EACnE,CAAA,EAAG,CAAC,IAAA,EAAM,KAAK,CAAC,CAAA;AAEhB,EAAA,MAAM,gBAAA,GAAmB,QAAgC,MAAM;AAC7D,IAAA,IAAI,CAAC,KAAK,OAAA,EAAS;AACjB,MAAA,OAAO,CAAC,GAAG,IAAA,CAAK,OAAO,CAAA;AAAA,IACzB;AAEA,IAAA,MAAM,UAAA,GAAa,WAAA,KAAgB,YAAA,CAAa,OAAA,KAAY,eAAA,EAAgB,CAAA;AAG5E,IAAA,OAAO,aAAA,CAAc,KAAK,OAAA,EAAS,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,IAAA,CAAK,EAAE,CAAA,CAAE,CAAA;AAAA,EAC/D,CAAA,EAAG,CAAC,IAAA,EAAM,WAAW,CAAC,CAAA;AAItB,EAAA,MAAM,aAAA,GAAgB,QAAQ,MAAM;AAClC,IAAA,IAAI,eAAe,QAAA,IAAY,OAAA,KAAY,MAAA,IAAa,OAAA,CAAQ,WAAW,QAAA,EAAU;AACnF,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,IAAI,GAAA,CAAI,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW,CAAC,MAAA,CAAO,MAAA,EAAQ,MAAM,CAAC,CAAC,CAAA;AAAA,EACzE,CAAA,EAAG,CAAC,UAAA,EAAY,OAAO,CAAC,CAAA;AAGxB,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,MAAM,QAAA;AAAA,EACR;AAMA,EAAA,IAAI,IAAA,CAAK,QAAA,KAAa,IAAA,IAAQ,UAAA,KAAe,UAAA,EAAY;AACvD,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,iBAAA,EAAoB,KAAK,EAAE,CAAA,oNAAA;AAAA,KAG7B;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,KAAK,IAAA,KAAS,QAAA;AAC/B,EAAA,MAAM,SAAS,UAAA,KAAe,MAAA;AAC9B,EAAA,MAAM,WAAW,UAAA,KAAe,QAAA;AAChC,EAAA,MAAM,YAAY,KAAA,KAAU,WAAA;AAC5B,EAAA,MAAM,QAAA,GAAW,QAAA,KAAa,IAAA,IAAQ,SAAA,IAAa,QAAA;AACnD,EAAA,MAAM,eAAe,KAAA,KAAU,MAAA;AAC/B,EAAA,MAAM,QAAA,GAAW,YAAA,GAAe,WAAA,CAAY,KAAK,CAAA,GAAI,iBAAA;AAMrD,EAAA,MAAM,MAAA,GAAS,QAAA,IAAa,UAAA,KAAe,UAAA,IAAc,SAAA;AAEzD,EAAA,MAAM,eAAA,GAAkB,CACtB,IAAA,EACA,OAAA,KACS;AACT,IAAA,aAAA,GAAgB,EAAE,IAAA,EAAM,UAAA,EAAY,IAAA,CAAK,EAAA,EAAI,WAAW,IAAA,CAAK,GAAA,EAAI,EAAG,OAAA,EAAS,CAAA;AAAA,EAC/E,CAAA;AAGA,EAAA,MAAM,UAAA,GAAa,CAAC,iBAAA,KAAsC;AACxD,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,oBAAA,CAAqB,iBAAiB,CAAA;AAAA,IACxC;AACA,IAAA,QAAA,GAAW,EAAE,IAAA,EAAM,iBAAA,EAAmB,iBAAA,EAAmB,CAAA;AAAA,EAC3D,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,QAAA,KAA2B;AAC/C,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA;AAAA,IACF;AACA,IAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,MAAA,KAAA,EAAM;AAAA,IACR;AACA,IAAA,UAAA,CAAW,CAAC,QAAQ,CAAC,CAAA;AACrB,IAAA,eAAA,CAAgB,iBAAA,EAAmB,EAAE,QAAA,EAAU,CAAA;AAAA,EACjD,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,QAAA,EAAkB,OAAA,KAA2B;AAChE,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA;AAAA,IACF;AACA,IAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,MAAA,KAAA,EAAM;AAAA,IACR;AACA,IAAA,UAAA,CAAW,OAAA,GAAU,CAAC,GAAG,QAAA,EAAU,QAAQ,CAAA,GAAI,QAAA,CAAS,MAAA,CAAO,CAAC,EAAA,KAAO,EAAA,KAAO,QAAQ,CAAC,CAAA;AACvF,IAAA,eAAA,CAAgB,OAAA,GAAU,iBAAA,GAAoB,mBAAA,EAAqB,EAAE,UAAU,CAAA;AAAA,EACjF,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,KAAA,KAAiC;AACrD,IAAA,KAAA,CAAM,cAAA,EAAe;AACrB,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA;AAAA,IACF;AACA,IAAA,MAAM,QAAA,GAA0C;AAAA,MAC9C,IAAA,EAAM,iBAAA;AAAA,MACN,iBAAA,EAAmB;AAAA,KACrB;AAGA,IAAA,QAAA,GAAW,QAAQ,CAAA;AAEnB,IAAA,IAAI,MAAA,EAAQ;AAGV,MAAA,QAAA,EAAS;AACT,MAAA,UAAA,CAAW,mBAAmB,CAAA;AAC9B,MAAA,eAAA,CAAgB,WAAA,EAAa,EAAE,iBAAA,EAAmB,QAAA,EAAU,CAAA;AAC5D,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,aAAA,GAAgB,KAAA,CAAM,iBAAA,EAAmB,IAAA,EAA4B,QAAQ,CAAA;AACnF,IAAA,QAAA,EAAS;AACT,IAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,IAAA,MAAM,aAAA,GAAgB,YAAY,sBAAA,CAAuB;AAAA,MACvD,KAAA,EAAO,eAAA;AAAA,MACP,MAAA,EAAQ;AAAA,QACN,EAAA,EAAI,WAAA,CAAY,IAAA,CAAK,EAAE,CAAA;AAAA,QACvB,IAAA,EAAM,EAAE,CAAC,IAAA,CAAK,UAAU,OAAO,GAAG,KAAK,KAAA,EAAM;AAAA;AAAA;AAAA;AAAA,QAI7C,GAAG,kBAAkB,IAAI,CAAA;AAAA,QACzB,OAAA,EAAS,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,MAAY;AAAA,UACrC,IAAI,MAAA,CAAO,EAAA;AAAA,UACX,WAAA,EAAa,EAAE,CAAC,IAAA,CAAK,UAAU,OAAO,GAAG,OAAO,IAAA;AAAK,SACvD,CAAE;AAAA,OACJ;AAAA,MACA,aAAA;AAAA,MACA,WAAA,EAAa,SAAA;AAAA,MACb,QAAA,EAAU,QAAA,CAAS,IAAA,CAAK,GAAG;AAAA,KAC5B,CAAA;AACD,IAAA,UAAA,GAAa;AAAA,MACX,OAAO,aAAA,CAAc,KAAA;AAAA,MACrB,UAAU,aAAA,CAAc,QAAA;AAAA,MACxB,QAAQ,aAAA,CAAc,MAAA;AAAA,MACtB,SAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,MAAM,UAAU,aAAA,CAAc,QAAA;AAC9B,IAAA,UAAA;AAAA,MACE,2BAA2B,IAAA,CAAK,KAAA,CAAM,aAAA,CAAc,KAAA,GAAQ,GAAG,CAAC,CAAA,GAAA,EAC9D,aAAA,CAAc,MAAA,GAAS,YAAY,aACrC,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,OAAO,KAAK,EAAE,CAAA;AAAA,KACjC;AACA,IAAA,eAAA,CAAgB,WAAA,EAAa;AAAA,MAC3B,iBAAA,EAAmB,QAAA;AAAA,MACnB,OAAO,aAAA,CAAc;AAAA,KACtB,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,UAAA,GAAa,CAAA,EAAG,IAAA,CAAK,EAAE,CAAA,SAAA,CAAA;AAG7B,EAAA,MAAM,YAAA,GACJ,iBAAiB,MAAA,IAAa,IAAA,CAAK,iBAAiB,MAAA,GAChD,YAAA,CAAa,IAAA,CAAK,YAAY,CAAA,GAC9B,IAAA;AAEN,EAAA,MAAM,UAAA,GAAa,gBAAA,CAAiB,GAAA,CAAI,CAAC,MAAA,KAAW;AAClD,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,QAAA,CAAS,MAAA,CAAO,EAAE,CAAA;AAE3C,IAAA,IAAI,WAAA;AACJ,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,MAAM,MAAA,GAAS,aAAA,EAAe,GAAA,CAAI,MAAA,CAAO,EAAE,CAAA;AAC3C,MAAA,WAAA,GAAc,WAAW,MAAA,GAAY,MAAA,GAAY,OAAO,cAAA,CAAe,MAAA,EAAQ,OAAO,CAAC,CAAA;AAAA,IACzF,WAAW,MAAA,EAAQ;AACjB,MAAA,WAAA,GAAc,MAAA,CAAO,OAAO,SAAS,CAAA;AAAA,IACvC;AACA,IAAA,uBACE,IAAA,CAAC,OAAA,EAAA,EAAsB,SAAA,EAAU,cAAA,EAAe,gBAAc,WAAA,EAC5D,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,OAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAM,WAAW,OAAA,GAAU,UAAA;AAAA,UAC3B,IAAA,EAAM,QAAA,GAAW,CAAA,EAAG,IAAA,CAAK,EAAE,CAAA,QAAA,CAAA,GAAa,MAAA;AAAA,UACxC,OAAO,MAAA,CAAO,EAAA;AAAA,UACd,OAAA;AAAA,UACA,QAAA,EAAU,QAAA;AAAA,UACV,iBAAe,QAAA,IAAY,MAAA;AAAA,UAC3B,QAAA,EAAU,CAAC,CAAA,KACT,QAAA,GAAW,YAAA,CAAa,MAAA,CAAO,EAAE,CAAA,GAAI,WAAA,CAAY,MAAA,CAAO,EAAA,EAAI,CAAA,CAAE,OAAO,OAAO;AAAA;AAAA,OAEhF;AAAA,sBACA,GAAA,CAAC,MAAA,EAAA,EAAM,QAAA,EAAA,MAAA,CAAO,IAAA,EAAK,CAAA;AAAA,MAClB,MAAA,IAAU,MAAA,CAAO,QAAA,mBAChB,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uBAAA,EAAwB,IAAA,EAAK,MAAA,EAC1C,QAAA,EAAA,MAAA,CAAO,QAAA,EACV,CAAA,GACE;AAAA,KAAA,EAAA,EAjBM,OAAO,EAkBnB,CAAA;AAAA,EAEJ,CAAC,CAAA;AAED,EAAA,4BACG,KAAA,EAAA,EAAI,SAAA,EAAU,SAAQ,IAAA,EAAM,MAAA,EAAQ,OAAO,KAAA,EACzC,QAAA,EAAA;AAAA,IAAA,IAAA,CAAK,wBAAQ,GAAA,CAAC,aAAA,EAAA,EAAc,KAAA,EAAO,IAAA,CAAK,OAAO,CAAA,GAAK,IAAA;AAAA,wBACpD,MAAA,EAAA,EAAK,QAAA,EAAU,cACd,QAAA,kBAAA,IAAA,CAAC,UAAA,EAAA,EAAS,UAAU,QAAA,EACjB,QAAA,EAAA;AAAA,MAAA,YAAA,KAAiB,uBAChB,GAAA,CAAC,QAAA,EAAA,EAAO,IAAI,UAAA,EAAa,QAAA,EAAA,IAAA,CAAK,UAAS,CAAA,mBAEvC,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,EAAA,EAAI,UAAA;AAAA,UAEJ,uBAAA,EAAyB,EAAE,MAAA,EAAQ,YAAA;AAAa;AAAA,OAClD;AAAA,MASD,QAAA,mBACC,GAAA,CAAC,KAAA,EAAA,EAAI,IAAA,EAAK,YAAA,EAAa,iBAAA,EAAiB,UAAA,EACrC,QAAA,EAAA,UAAA,EACH,CAAA,mBAEA,GAAA,CAAC,KAAA,EAAA,EAAK,QAAA,EAAA,UAAA,EAAW,CAAA;AAAA,MAGlB,QAAA,GAAW,uBACV,GAAA,CAAC,QAAA,EAAA,EAAO,MAAK,QAAA,EAAS,QAAA,EAAU,UAAU,QAAA,EAAA,QAAA,EAE1C;AAAA,KAAA,EAEJ,CAAA,EACF,CAAA;AAAA,oBACA,GAAA,CAAC,cAAA,EAAA,EAAe,EAAA,EAAI,CAAA,EAAG,IAAA,CAAK,EAAE,CAAA,SAAA,CAAA,EAC3B,QAAA,EAAA,QAAA,GAAW,kBAAA,CAAmB,OAAO,CAAA,GAAI,OAAA,EAC5C;AAAA,GAAA,EACF,CAAA;AAEJ;AC9XO,SAASA,gBAAe,KAAA,EAA4B;AACzD,EAAA,uBACEC,GAAAA,CAAC,qBAAA,EAAA,EAAsB,aAAA,EAAe,KAAA,CAAM,IAAA,EAAM,KAAA,EAChD,QAAA,kBAAAA,GAAAA,CAAC,cAAA,EAAA,EAAoB,GAAG,KAAA,EAAO,CAAA,EACjC,CAAA;AAEJ","file":"chunk-ZLUMUJUQ.js","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n type ItemOutcome,\n type LearnerResponse,\n type MultipleChoiceData,\n type MultipleChoiceLearnerResponse,\n type MultipleChoiceOption,\n type ScoringDetail,\n score,\n seededShuffle,\n validateActivity,\n xAPIBuilder,\n xapiDefinitionFor,\n} from '@intellectif/lk-core';\nimport { type CSSProperties, useEffect, useMemo, useRef, useState } from 'react';\nimport { useActivityState } from '../../hooks/useActivityState.js';\nimport { ANONYMOUS_ACTOR, isDevelopment, objectIdFor, randomSessionId } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport type { ActivityProps } from '../types.js';\n\n/**\n * Selected option ids carried by a learner response. Any other response shape\n * (a caller wiring the wrong item's state through `value`) reads as \"nothing\n * selected\" rather than throwing mid-exam.\n */\nfunction selectionOf(response: LearnerResponse | undefined): string[] {\n return response?.type === 'multiple-choice' ? response.selectedOptionIds : [];\n}\n\n/**\n * Whether an option is part of the correct answer, derived from the SERVER's\n * outcome — review mode never holds an answer key of its own.\n * {@link ScoringDetail.outcome} states this unambiguously. For 0.2-era details\n * that carry only the deprecated `correct` flag (which means \"the learner\n * ACTED correctly on this option\", not \"this option is the answer\") the fact\n * is still recoverable, because we know whether the learner selected it:\n * `isCorrect = wasSelected ? correct : !correct`.\n */\nfunction isAnswerOption(detail: ScoringDetail, wasSelected: boolean): boolean {\n switch (detail.outcome) {\n case 'correct':\n case 'incorrect-omission':\n return true;\n case 'incorrect':\n case 'correct-omission':\n return false;\n default:\n return wasSelected ? detail.correct : !detail.correct;\n }\n}\n\n/** Announcement for `review` mode, built only from the server-supplied outcome. */\nfunction reviewAnnouncement(outcome: ItemOutcome | undefined): string | null {\n if (outcome === undefined) {\n return null;\n }\n if (outcome.status === 'scored') {\n const overall = outcome.feedback;\n return `Score ${Math.round(outcome.score * 100)}%. ${\n outcome.passed ? 'Passed.' : 'Not passed.'\n }${overall ? ` ${overall}` : ''}`;\n }\n if (outcome.status === 'deferred') {\n // Never \"0%\": ungraded is not the same as wrong.\n return 'Not graded yet.';\n }\n return null;\n}\n\nexport interface MultipleChoiceProps extends ActivityProps<MultipleChoiceData> {\n /**\n * Seed for the deterministic option shuffle. Supply one (e.g. the attempt\n * id) to make the shuffled order reproducible server-side, stable across\n * page reloads, and identical between SSR and hydration. When absent, a\n * random per-mount session seed is used (order stable within the mount\n * only — the behaviour before `shuffleSeed` existed).\n */\n shuffleSeed?: string;\n}\n\nexport function MultipleChoice({\n data,\n onComplete,\n onSubmit,\n value,\n defaultValue,\n defaultSubmitted,\n onChange,\n renderMode = 'practice',\n outcome,\n sanitizeHtml,\n onInteraction,\n theme,\n locale,\n disabled,\n shuffleSeed,\n}: MultipleChoiceProps) {\n // Dev-only boundary validation (Req 2.3). Throwing during render lets\n // ActivityErrorBoundary catch it. Memoised so it only re-runs on data change.\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n // A `redact()` projection cannot satisfy the full content schema — the\n // answer key is gone by design — and must not be checked against the\n // strict redacted schema either, since a `reveal: 'after-submit'`\n // projection legitimately carries answer-key fields for review renders.\n if (data.redacted === true) {\n return null;\n }\n const result = validateActivity('multiple-choice', data);\n return result.success ? null : new ActivitySchemaError('multiple-choice', result.errors);\n }, [data]);\n\n // Lazily created only when shuffling without a caller seed: avoids calling\n // crypto.randomUUID (absent on non-secure http origins) unless needed.\n const sessionIdRef = useRef<string | null>(null);\n const { state, start, complete, getTimeSpent, reset } = useActivityState(\n defaultSubmitted === true ? 'completed' : 'idle',\n );\n // Uncontrolled state. `defaultValue` seeds the mount only (React convention);\n // to re-seed later, remount with a `key` or drive the component with `value`.\n const [internalSelection, setInternalSelection] = useState<string[]>(() =>\n selectionOf(defaultValue),\n );\n const [summary, setSummary] = useState<string | null>(null);\n\n // Reset on data-prop CHANGE (Req 3.7). The identity guard makes the mount\n // run a no-op, which it always was before `defaultValue` existed — without\n // it this effect would wipe the seed immediately after the first render.\n // (It also makes the reset StrictMode-safe: a remount with unchanged data\n // no longer discards the learner's selection.)\n // Resets back to `defaultValue`, not to empty. Clearing looked safer — it\n // cannot carry a stale answer into a different question — but `data`\n // identity is a poor proxy for \"different question\": a parent that builds\n // entries in render (`activities={raw.map(redact)}`, the documented exam\n // pattern) hands over a new object every render, and clearing wiped every\n // RESTORED answer on the first unrelated re-render, silently. `defaultValue`\n // is by definition the caller's seed for the CURRENT data, which is what\n // FillInTheBlanks already reset to; the two now agree.\n const defaultValueRef = useRef(defaultValue);\n useEffect(() => {\n defaultValueRef.current = defaultValue;\n }, [defaultValue]);\n // A data change must return the item to its SEED, submitted state included —\n // resetting to idle unlocked work the learner had already committed.\n const defaultSubmittedRef = useRef(defaultSubmitted);\n useEffect(() => {\n defaultSubmittedRef.current = defaultSubmitted;\n }, [defaultSubmitted]);\n const lastDataRef = useRef(data);\n useEffect(() => {\n if (lastDataRef.current === data) {\n return;\n }\n lastDataRef.current = data;\n setInternalSelection(selectionOf(defaultValueRef.current));\n setSummary(null);\n reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [data, reset]);\n\n const displayedOptions = useMemo<MultipleChoiceOption[]>(() => {\n if (!data.shuffle) {\n return [...data.options];\n }\n // biome-ignore lint/suspicious/noAssignInExpressions: sanctioned lazy ref initialization\n const seedSource = shuffleSeed ?? (sessionIdRef.current ??= randomSessionId());\n // The seed string is unchanged from when the algorithm lived in this file,\n // so an order a consumer recorded against a seed still reproduces.\n return seededShuffle(data.options, `${seedSource}:${data.id}`);\n }, [data, shuffleSeed]);\n\n // Per-option correctness for `review`, indexed by option id. Null unless the\n // caller supplied a SCORED outcome — the only source of truth this mode has.\n const reviewDetails = useMemo(() => {\n if (renderMode !== 'review' || outcome === undefined || outcome.status !== 'scored') {\n return null;\n }\n return new Map(outcome.details.map((detail) => [detail.itemId, detail]));\n }, [renderMode, outcome]);\n\n // All hooks are called before these throws, so hook order stays stable.\n if (devError) {\n throw devError;\n }\n // Practice grades locally, which a redacted projection cannot support:\n // `score()` would throw from the submit handler — where no error boundary\n // can catch it — AFTER the learner has answered. Fail loudly at render\n // instead, in production too. Wiring an exam item into the self-grading\n // mode is precisely the accident `renderMode` exists to prevent.\n if (data.redacted === true && renderMode === 'practice') {\n throw new Error(\n `Multiple Choice \"${data.id}\" received redacted activity data in renderMode \"practice\", ` +\n 'which grades locally and has no answer key to grade against. ' +\n 'Render redacted data with renderMode=\"exam\" (server grades) or \"review\" (pass `outcome`).',\n );\n }\n\n const isSingle = data.mode === 'single';\n const isExam = renderMode === 'exam';\n const isReview = renderMode === 'review';\n const submitted = state === 'completed';\n const inactive = disabled === true || submitted || isReview;\n const isControlled = value !== undefined;\n const selected = isControlled ? selectionOf(value) : internalSelection;\n /**\n * Whether correctness and authored feedback may be shown. Practice earns it\n * by grading locally after submit; review shows what the server already\n * graded. Exam never reveals anything — not before, not after submit.\n */\n const reveal = isReview || (renderMode === 'practice' && submitted);\n\n const fireInteraction = (\n type: 'option-selected' | 'option-deselected' | 'submitted',\n payload: Record<string, unknown>,\n ): void => {\n onInteraction?.({ type, activityId: data.id, timestamp: Date.now(), payload });\n };\n\n /** Single funnel for every response change: owns state only when uncontrolled. */\n const emitChange = (selectedOptionIds: string[]): void => {\n if (!isControlled) {\n setInternalSelection(selectedOptionIds);\n }\n onChange?.({ type: 'multiple-choice', selectedOptionIds });\n };\n\n const selectSingle = (optionId: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n emitChange([optionId]);\n fireInteraction('option-selected', { optionId });\n };\n\n const toggleMulti = (optionId: string, checked: boolean): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n emitChange(checked ? [...selected, optionId] : selected.filter((id) => id !== optionId));\n fireInteraction(checked ? 'option-selected' : 'option-deselected', { optionId });\n };\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive) {\n return;\n }\n const response: MultipleChoiceLearnerResponse = {\n type: 'multiple-choice',\n selectedOptionIds: selected,\n };\n // Always first, and before anything that can throw: an exam runner must be\n // able to persist the raw response no matter what happens after.\n onSubmit?.(response);\n\n if (isExam) {\n // No score(), no answer key read, no onComplete, no xAPI — the server\n // grades. The announcement deliberately carries no correctness signal.\n complete();\n setSummary('Answer submitted.');\n fireInteraction('submitted', { selectedOptionIds: selected });\n return;\n }\n\n const scoringResult = score('multiple-choice', data as MultipleChoiceData, response);\n complete();\n const timeSpent = getTimeSpent();\n const xapiStatement = xAPIBuilder.buildAnsweredStatement({\n actor: ANONYMOUS_ACTOR,\n object: {\n id: objectIdFor(data.id),\n name: { [data.locale ?? 'en-US']: data.title },\n // Activity-type IRI, interaction type and correct-responses pattern\n // come from the REGISTERED descriptor, so a consumer-registered type\n // gets correct interop without touching this component.\n ...xapiDefinitionFor(data),\n choices: data.options.map((option) => ({\n id: option.id,\n description: { [data.locale ?? 'en-US']: option.text },\n })),\n },\n scoringResult,\n timeSpentMs: timeSpent,\n response: selected.join(','),\n });\n onComplete?.({\n score: scoringResult.score,\n maxScore: scoringResult.maxScore,\n passed: scoringResult.passed,\n timeSpent,\n xapiStatement,\n });\n // Core selects the authored overall feedback on `passed` (B3 fix).\n const overall = scoringResult.feedback;\n setSummary(\n `Answer submitted. Score ${Math.round(scoringResult.score * 100)}%. ${\n scoringResult.passed ? 'Passed.' : 'Not passed.'\n }${overall ? ` ${overall}` : ''}`,\n );\n fireInteraction('submitted', {\n selectedOptionIds: selected,\n score: scoringResult.score,\n });\n };\n\n const questionId = `${data.id}-question`;\n // Rich text renders ONLY through a caller-supplied sanitiser; without one we\n // fall back to the plain-text field, escaped by React (types.ts HtmlSanitizer).\n const questionHtml =\n sanitizeHtml !== undefined && data.questionHtml !== undefined\n ? sanitizeHtml(data.questionHtml)\n : null;\n\n const optionList = displayedOptions.map((option) => {\n const checked = selected.includes(option.id);\n // `data-correct` means \"this option is part of the correct answer\".\n let correctness: string | undefined;\n if (isReview) {\n const detail = reviewDetails?.get(option.id);\n correctness = detail === undefined ? undefined : String(isAnswerOption(detail, checked));\n } else if (reveal) {\n correctness = String(option.isCorrect);\n }\n return (\n <label key={option.id} className=\"lk-mc-option\" data-correct={correctness}>\n <input\n type={isSingle ? 'radio' : 'checkbox'}\n name={isSingle ? `${data.id}-options` : undefined}\n value={option.id}\n checked={checked}\n disabled={inactive}\n aria-disabled={inactive || undefined}\n onChange={(e) =>\n isSingle ? selectSingle(option.id) : toggleMulti(option.id, e.target.checked)\n }\n />\n <span>{option.text}</span>\n {reveal && option.feedback ? (\n <span className=\"lk-mc-option-feedback\" role=\"note\">\n {option.feedback}\n </span>\n ) : null}\n </label>\n );\n });\n\n return (\n <div className=\"lk-mc\" lang={locale} style={theme as CSSProperties | undefined}>\n {data.media ? <ActivityMedia media={data.media} /> : null}\n <form onSubmit={handleSubmit}>\n <fieldset disabled={inactive}>\n {questionHtml === null ? (\n <legend id={questionId}>{data.question}</legend>\n ) : (\n <legend\n id={questionId}\n // biome-ignore lint/security/noDangerouslySetInnerHtml: HTML is the output of the caller-supplied sanitizeHtml (types.ts HtmlSanitizer contract)\n dangerouslySetInnerHTML={{ __html: questionHtml }}\n />\n )}\n {/*\n single: an explicit radiogroup is meaningful (fieldset's implicit\n role is `group`, not `radiogroup`). multi: the fieldset + legend\n already provide a named group — an explicit role=\"group\" here would\n be a redundant, duplicate same-named group in the a11y tree, so we\n use a plain layout div. (Refines the design ARIA sketch.)\n */}\n {isSingle ? (\n <div role=\"radiogroup\" aria-labelledby={questionId}>\n {optionList}\n </div>\n ) : (\n <div>{optionList}</div>\n )}\n {/* review is read-only: there is nothing left to submit. */}\n {isReview ? null : (\n <button type=\"submit\" disabled={inactive}>\n Submit\n </button>\n )}\n </fieldset>\n </form>\n <FeedbackRegion id={`${data.id}-feedback`}>\n {isReview ? reviewAnnouncement(outcome) : summary}\n </FeedbackRegion>\n </div>\n );\n}\n","'use client';\n\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport {\n MultipleChoice as MultipleChoiceCore,\n type MultipleChoiceProps,\n} from './MultipleChoice.js';\n\nexport type { MultipleChoiceProps } from './MultipleChoice.js';\n\n/**\n * Public Multiple Choice activity: the core component wrapped in\n * `ActivityErrorBoundary` so a render failure (incl. the dev schema-validation\n * throw) degrades to an accessible fallback instead of crashing the host.\n */\nexport function MultipleChoice(props: MultipleChoiceProps) {\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title}>\n <MultipleChoiceCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
@@ -1,9 +1,10 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
- var chunkPBVMEOWI_cjs = require('../chunk-PBVMEOWI.cjs');
4
- require('../chunk-YXG3UJVG.cjs');
4
+ var chunkDUIWK272_cjs = require('../chunk-DUIWK272.cjs');
5
+ require('../chunk-BE7R2X3S.cjs');
5
6
  require('../chunk-NPY2F7F6.cjs');
6
- require('../chunk-R3OZ6JHQ.cjs');
7
+ require('../chunk-MDYKYDRN.cjs');
7
8
  require('../chunk-PGEG7UZJ.cjs');
8
9
  require('../chunk-NEWGUDA5.cjs');
9
10
  require('../chunk-4HZ7LQIH.cjs');
@@ -13,7 +14,7 @@ require('../chunk-XOR4MPEN.cjs');
13
14
 
14
15
  Object.defineProperty(exports, "ActivitySequence", {
15
16
  enumerable: true,
16
- get: function () { return chunkPBVMEOWI_cjs.ActivitySequence; }
17
+ get: function () { return chunkDUIWK272_cjs.ActivitySequence; }
17
18
  });
18
19
  //# sourceMappingURL=ActivitySequence.cjs.map
19
20
  //# sourceMappingURL=ActivitySequence.cjs.map
@@ -1,7 +1,7 @@
1
1
  import { SequenceEntry, ActivityResult, LearnerResponse, InteractionEvent, ItemOutcome, ThemeTokens } from '@intellectif/lk-core';
2
2
  import { ComponentType } from 'react';
3
- import { A as ActivityProps, b as RenderableActivity, R as RenderMode, H as HtmlSanitizer } from '../types-DlHmPLbD.cjs';
4
- import { a as WrittenResponseSubmission } from '../WrittenResponse-BfpzfLlR.cjs';
3
+ import { A as ActivityProps, b as RenderableActivity, R as RenderMode, H as HtmlSanitizer } from '../types-9iS1Vs2I.cjs';
4
+ import { a as WrittenResponseSubmission } from '../WrittenResponse-BXHidIaK.cjs';
5
5
 
6
6
  /**
7
7
  * What one finished slot in a sequence produced. Two kinds, because two kinds
@@ -129,14 +129,22 @@ interface ActivitySequenceProps {
129
129
  * server's `flattenSequence(entries, { seed })` derives the same order this
130
130
  * pager shows.
131
131
  *
132
- * **Required in `exam` and `review` mode** — omitting it throws. An order
133
- * nobody can reproduce cannot be reconciled with a recorded attempt, and
134
- * failing at render is the only way that mistake surfaces before a learner
135
- * sits the paper. In `practice` mode it stays optional: a random per-mount
136
- * seed is used, which is stable within the mount and deliberately not
137
- * reproducible. That fallback is also not SSR-safe (server and client would
138
- * invent different orders and hydration would mismatch), so supply a seed
139
- * for any server-rendered sequence regardless of mode.
132
+ * **Required whenever this sequence shuffles** — `shuffle: 'entries'`, or any
133
+ * group with `shuffle: 'within-group'` in `exam` or `review` mode; omitting
134
+ * it then throws. An order nobody can reproduce cannot be reconciled with a
135
+ * recorded attempt, and failing at render is the only way that mistake
136
+ * surfaces before a learner sits the paper. A sequence that does not shuffle
137
+ * needs no seed in any mode.
138
+ *
139
+ * Note the guard does NOT cover an activity's own `data.shuffle`:
140
+ * `<MultipleChoice>` falls back to a per-mount seed there in every mode, so
141
+ * pass this whenever any item shuffles its options.
142
+ *
143
+ * In `practice` mode it stays optional: a random per-mount seed is used,
144
+ * stable within the mount and deliberately not reproducible. That fallback is
145
+ * also not SSR-safe (server and client would invent different orders and
146
+ * hydration would mismatch), so supply a seed for any server-rendered
147
+ * sequence regardless of mode.
140
148
  */
141
149
  shuffleSeed?: string;
142
150
  /**
@@ -1,7 +1,7 @@
1
1
  import { SequenceEntry, ActivityResult, LearnerResponse, InteractionEvent, ItemOutcome, ThemeTokens } from '@intellectif/lk-core';
2
2
  import { ComponentType } from 'react';
3
- import { A as ActivityProps, b as RenderableActivity, R as RenderMode, H as HtmlSanitizer } from '../types-DlHmPLbD.js';
4
- import { a as WrittenResponseSubmission } from '../WrittenResponse-C3GIWErG.js';
3
+ import { A as ActivityProps, b as RenderableActivity, R as RenderMode, H as HtmlSanitizer } from '../types-9iS1Vs2I.js';
4
+ import { a as WrittenResponseSubmission } from '../WrittenResponse-DJYGuMKk.js';
5
5
 
6
6
  /**
7
7
  * What one finished slot in a sequence produced. Two kinds, because two kinds
@@ -129,14 +129,22 @@ interface ActivitySequenceProps {
129
129
  * server's `flattenSequence(entries, { seed })` derives the same order this
130
130
  * pager shows.
131
131
  *
132
- * **Required in `exam` and `review` mode** — omitting it throws. An order
133
- * nobody can reproduce cannot be reconciled with a recorded attempt, and
134
- * failing at render is the only way that mistake surfaces before a learner
135
- * sits the paper. In `practice` mode it stays optional: a random per-mount
136
- * seed is used, which is stable within the mount and deliberately not
137
- * reproducible. That fallback is also not SSR-safe (server and client would
138
- * invent different orders and hydration would mismatch), so supply a seed
139
- * for any server-rendered sequence regardless of mode.
132
+ * **Required whenever this sequence shuffles** — `shuffle: 'entries'`, or any
133
+ * group with `shuffle: 'within-group'` in `exam` or `review` mode; omitting
134
+ * it then throws. An order nobody can reproduce cannot be reconciled with a
135
+ * recorded attempt, and failing at render is the only way that mistake
136
+ * surfaces before a learner sits the paper. A sequence that does not shuffle
137
+ * needs no seed in any mode.
138
+ *
139
+ * Note the guard does NOT cover an activity's own `data.shuffle`:
140
+ * `<MultipleChoice>` falls back to a per-mount seed there in every mode, so
141
+ * pass this whenever any item shuffles its options.
142
+ *
143
+ * In `practice` mode it stays optional: a random per-mount seed is used,
144
+ * stable within the mount and deliberately not reproducible. That fallback is
145
+ * also not SSR-safe (server and client would invent different orders and
146
+ * hydration would mismatch), so supply a seed for any server-rendered
147
+ * sequence regardless of mode.
140
148
  */
141
149
  shuffleSeed?: string;
142
150
  /**
@@ -1,7 +1,8 @@
1
- export { ActivitySequence } from '../chunk-OQGBAQL2.js';
2
- import '../chunk-5TUTJ4R3.js';
1
+ 'use client';
2
+ export { ActivitySequence } from '../chunk-IUODGQXE.js';
3
+ import '../chunk-ZLUMUJUQ.js';
3
4
  import '../chunk-57E3E2H4.js';
4
- import '../chunk-BQHDQ33H.js';
5
+ import '../chunk-MDZMKC27.js';
5
6
  import '../chunk-6F4X2NKL.js';
6
7
  import '../chunk-UQ3BBEIT.js';
7
8
  import '../chunk-GB5URWM4.js';
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
4
  var chunkNPY2F7F6_cjs = require('../chunk-NPY2F7F6.cjs');
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { FillInTheBlanksData } from '@intellectif/lk-core';
3
- import { A as ActivityProps } from '../types-DlHmPLbD.cjs';
3
+ import { A as ActivityProps } from '../types-9iS1Vs2I.cjs';
4
4
 
5
5
  interface FillInTheBlanksProps extends ActivityProps<FillInTheBlanksData> {
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { FillInTheBlanksData } from '@intellectif/lk-core';
3
- import { A as ActivityProps } from '../types-DlHmPLbD.js';
3
+ import { A as ActivityProps } from '../types-9iS1Vs2I.js';
4
4
 
5
5
  interface FillInTheBlanksProps extends ActivityProps<FillInTheBlanksData> {
6
6
  /**
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  export { FillInTheBlanks } from '../chunk-57E3E2H4.js';
2
3
  import '../chunk-6F4X2NKL.js';
3
4
  import '../chunk-GB5URWM4.js';
@@ -1,6 +1,7 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
- var chunkYXG3UJVG_cjs = require('../chunk-YXG3UJVG.cjs');
4
+ var chunkBE7R2X3S_cjs = require('../chunk-BE7R2X3S.cjs');
4
5
  require('../chunk-PGEG7UZJ.cjs');
5
6
  require('../chunk-4HZ7LQIH.cjs');
6
7
  require('../chunk-XOR4MPEN.cjs');
@@ -9,7 +10,7 @@ require('../chunk-XOR4MPEN.cjs');
9
10
 
10
11
  Object.defineProperty(exports, "MultipleChoice", {
11
12
  enumerable: true,
12
- get: function () { return chunkYXG3UJVG_cjs.MultipleChoice; }
13
+ get: function () { return chunkBE7R2X3S_cjs.MultipleChoice; }
13
14
  });
14
15
  //# sourceMappingURL=MultipleChoice.cjs.map
15
16
  //# sourceMappingURL=MultipleChoice.cjs.map
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MultipleChoiceData } from '@intellectif/lk-core';
3
- import { A as ActivityProps } from '../types-DlHmPLbD.cjs';
3
+ import { A as ActivityProps } from '../types-9iS1Vs2I.cjs';
4
4
 
5
5
  interface MultipleChoiceProps extends ActivityProps<MultipleChoiceData> {
6
6
  /**
@@ -8,7 +8,7 @@ interface MultipleChoiceProps extends ActivityProps<MultipleChoiceData> {
8
8
  * id) to make the shuffled order reproducible server-side, stable across
9
9
  * page reloads, and identical between SSR and hydration. When absent, a
10
10
  * random per-mount session seed is used (order stable within the mount
11
- * only — the v1 behaviour).
11
+ * only — the behaviour before `shuffleSeed` existed).
12
12
  */
13
13
  shuffleSeed?: string;
14
14
  }
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { MultipleChoiceData } from '@intellectif/lk-core';
3
- import { A as ActivityProps } from '../types-DlHmPLbD.js';
3
+ import { A as ActivityProps } from '../types-9iS1Vs2I.js';
4
4
 
5
5
  interface MultipleChoiceProps extends ActivityProps<MultipleChoiceData> {
6
6
  /**
@@ -8,7 +8,7 @@ interface MultipleChoiceProps extends ActivityProps<MultipleChoiceData> {
8
8
  * id) to make the shuffled order reproducible server-side, stable across
9
9
  * page reloads, and identical between SSR and hydration. When absent, a
10
10
  * random per-mount session seed is used (order stable within the mount
11
- * only — the v1 behaviour).
11
+ * only — the behaviour before `shuffleSeed` existed).
12
12
  */
13
13
  shuffleSeed?: string;
14
14
  }
@@ -1,4 +1,5 @@
1
- export { MultipleChoice } from '../chunk-5TUTJ4R3.js';
1
+ 'use client';
2
+ export { MultipleChoice } from '../chunk-ZLUMUJUQ.js';
2
3
  import '../chunk-6F4X2NKL.js';
3
4
  import '../chunk-GB5URWM4.js';
4
5
  import '../chunk-6N43WDVG.js';
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
4
  var chunkNEWGUDA5_cjs = require('../chunk-NEWGUDA5.cjs');
@@ -1,5 +1,5 @@
1
1
  import { Stimulus } from '@intellectif/lk-core';
2
- import { H as HtmlSanitizer } from '../types-DlHmPLbD.cjs';
2
+ import { H as HtmlSanitizer } from '../types-9iS1Vs2I.cjs';
3
3
 
4
4
  interface StimulusPanelProps {
5
5
  stimulus: Stimulus;
@@ -1,5 +1,5 @@
1
1
  import { Stimulus } from '@intellectif/lk-core';
2
- import { H as HtmlSanitizer } from '../types-DlHmPLbD.js';
2
+ import { H as HtmlSanitizer } from '../types-9iS1Vs2I.js';
3
3
 
4
4
  interface StimulusPanelProps {
5
5
  stimulus: Stimulus;
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  export { StimulusPanel } from '../chunk-UQ3BBEIT.js';
2
3
  import '../chunk-GB5URWM4.js';
3
4
  //# sourceMappingURL=StimulusPanel.js.map
@@ -1,6 +1,7 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
- var chunkR3OZ6JHQ_cjs = require('../chunk-R3OZ6JHQ.cjs');
4
+ var chunkMDYKYDRN_cjs = require('../chunk-MDYKYDRN.cjs');
4
5
  require('../chunk-PGEG7UZJ.cjs');
5
6
  require('../chunk-4HZ7LQIH.cjs');
6
7
  require('../chunk-XOR4MPEN.cjs');
@@ -9,7 +10,7 @@ require('../chunk-XOR4MPEN.cjs');
9
10
 
10
11
  Object.defineProperty(exports, "WrittenResponse", {
11
12
  enumerable: true,
12
- get: function () { return chunkR3OZ6JHQ_cjs.WrittenResponse; }
13
+ get: function () { return chunkMDYKYDRN_cjs.WrittenResponse; }
13
14
  });
14
15
  //# sourceMappingURL=WrittenResponse.cjs.map
15
16
  //# sourceMappingURL=WrittenResponse.cjs.map
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { W as WrittenResponseProps } from '../WrittenResponse-BfpzfLlR.cjs';
3
- export { a as WrittenResponseSubmission } from '../WrittenResponse-BfpzfLlR.cjs';
4
- export { H as HtmlSanitizer, R as RenderMode, a as Renderable } from '../types-DlHmPLbD.cjs';
2
+ import { W as WrittenResponseProps } from '../WrittenResponse-BXHidIaK.cjs';
3
+ export { a as WrittenResponseSubmission } from '../WrittenResponse-BXHidIaK.cjs';
4
+ export { H as HtmlSanitizer, R as RenderMode, a as Renderable } from '../types-9iS1Vs2I.cjs';
5
5
  import '@intellectif/lk-core';
6
6
 
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { W as WrittenResponseProps } from '../WrittenResponse-C3GIWErG.js';
3
- export { a as WrittenResponseSubmission } from '../WrittenResponse-C3GIWErG.js';
4
- export { H as HtmlSanitizer, R as RenderMode, a as Renderable } from '../types-DlHmPLbD.js';
2
+ import { W as WrittenResponseProps } from '../WrittenResponse-DJYGuMKk.js';
3
+ export { a as WrittenResponseSubmission } from '../WrittenResponse-DJYGuMKk.js';
4
+ export { H as HtmlSanitizer, R as RenderMode, a as Renderable } from '../types-9iS1Vs2I.js';
5
5
  import '@intellectif/lk-core';
6
6
 
7
7
  /**
@@ -1,4 +1,5 @@
1
- export { WrittenResponse } from '../chunk-BQHDQ33H.js';
1
+ 'use client';
2
+ export { WrittenResponse } from '../chunk-MDZMKC27.js';
2
3
  import '../chunk-6F4X2NKL.js';
3
4
  import '../chunk-GB5URWM4.js';
4
5
  import '../chunk-6N43WDVG.js';
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
4
  var chunkXOR4MPEN_cjs = require('../chunk-XOR4MPEN.cjs');
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  export { useActivityState } from '../chunk-6N43WDVG.js';
2
3
  //# sourceMappingURL=useActivityState.js.map
3
4
  //# sourceMappingURL=useActivityState.js.map
@@ -1,12 +1,13 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
- var chunkIYLQSNLE_cjs = require('../chunk-IYLQSNLE.cjs');
4
+ var chunkPNNUPUH4_cjs = require('../chunk-PNNUPUH4.cjs');
4
5
 
5
6
 
6
7
 
7
8
  Object.defineProperty(exports, "useXAPI", {
8
9
  enumerable: true,
9
- get: function () { return chunkIYLQSNLE_cjs.useXAPI; }
10
+ get: function () { return chunkPNNUPUH4_cjs.useXAPI; }
10
11
  });
11
12
  //# sourceMappingURL=useXAPI.cjs.map
12
13
  //# sourceMappingURL=useXAPI.cjs.map
@@ -10,6 +10,37 @@ interface UseXAPIResult {
10
10
  */
11
11
  sendStatement: (statement: XAPIStatement) => Promise<void>;
12
12
  }
13
+ /**
14
+ * Delivers xAPI statements to a Learning Record Store, fire-and-forget.
15
+ *
16
+ * Telemetry must never break the learner's attempt, so
17
+ * {@link UseXAPIResult.sendStatement} **never rejects**: network errors and 5xx
18
+ * responses are retried (backoff 1s / 2s / 4s, up to 3 retries), a 4xx fails
19
+ * immediately, and any final failure is reported to `config.onError` instead of
20
+ * throwing. See `XAPIConfig` in `@intellectif/lk-core` for the options.
21
+ *
22
+ * The hook is also where learner identity is applied. Activity components
23
+ * cannot know who the learner is, so they emit an anonymous placeholder actor
24
+ * and a `urn:learning-kit:activity:*` object id; this hook substitutes
25
+ * `config.actor` and `config.activityId` on the way out — and only while those
26
+ * placeholders are still in place, so a statement you rewrote yourself is left
27
+ * alone.
28
+ *
29
+ * `sendStatement` keeps a stable identity across renders even when `config` is
30
+ * an inline object, so it is safe in a dependency array.
31
+ *
32
+ * ```tsx
33
+ * const { sendStatement } = useXAPI({
34
+ * endpoint: 'https://your-lrs.example/xapi/statements',
35
+ * auth: { type: 'bearer', token: 'YOUR_TOKEN' },
36
+ * activityId: 'https://your-app.example/quiz/capital-jp',
37
+ * actor: { objectType: 'Agent', mbox: 'mailto:learner@example.com' },
38
+ * onError: (e) => console.error('xAPI send failed', e),
39
+ * });
40
+ *
41
+ * <MultipleChoice data={quiz} onComplete={(r) => void sendStatement(r.xapiStatement)} />
42
+ * ```
43
+ */
13
44
  declare function useXAPI(config: XAPIConfig): UseXAPIResult;
14
45
 
15
46
  export { type UseXAPIResult, useXAPI };