@intellectif/lk-react 23.1.0 → 23.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +9 -5
- package/dist/ActivityErrorBoundary-B379hvtL.cjs +39 -0
- package/dist/ActivityErrorBoundary-B379hvtL.cjs.map +1 -0
- package/dist/ActivityErrorBoundary-Dw6SbWv3.js +34 -0
- package/dist/ActivityErrorBoundary-Dw6SbWv3.js.map +1 -0
- package/dist/ActivityMedia-CQQTRNl7.cjs +641 -0
- package/dist/ActivityMedia-CQQTRNl7.cjs.map +1 -0
- package/dist/ActivityMedia-vDiP-Psa.js +636 -0
- package/dist/ActivityMedia-vDiP-Psa.js.map +1 -0
- package/dist/ActivityPreview-1hhTkYuj.js +240 -0
- package/dist/ActivityPreview-1hhTkYuj.js.map +1 -0
- package/dist/ActivityPreview-BKr34exz.cjs +245 -0
- package/dist/ActivityPreview-BKr34exz.cjs.map +1 -0
- package/dist/ActivitySequence-CkX87pfB.js +839 -0
- package/dist/ActivitySequence-CkX87pfB.js.map +1 -0
- package/dist/ActivitySequence-CmoSyDnp.cjs +844 -0
- package/dist/ActivitySequence-CmoSyDnp.cjs.map +1 -0
- package/dist/Dictation-BlxRp-3k.js +926 -0
- package/dist/Dictation-BlxRp-3k.js.map +1 -0
- package/dist/Dictation-jECzL88E.cjs +931 -0
- package/dist/Dictation-jECzL88E.cjs.map +1 -0
- package/dist/FeedbackRegion-BNNMtTY-.cjs +108 -0
- package/dist/FeedbackRegion-BNNMtTY-.cjs.map +1 -0
- package/dist/FeedbackRegion-CyPSp8bA.js +85 -0
- package/dist/FeedbackRegion-CyPSp8bA.js.map +1 -0
- package/dist/FillInTheBlanks-B6xzqYXx.js +474 -0
- package/dist/FillInTheBlanks-B6xzqYXx.js.map +1 -0
- package/dist/FillInTheBlanks-BpKhdHg2.cjs +479 -0
- package/dist/FillInTheBlanks-BpKhdHg2.cjs.map +1 -0
- package/dist/GapSelect-DUSiaEQH.js +441 -0
- package/dist/GapSelect-DUSiaEQH.js.map +1 -0
- package/dist/GapSelect-payTq_Zn.cjs +446 -0
- package/dist/GapSelect-payTq_Zn.cjs.map +1 -0
- package/dist/InteractiveVideo-BguGv-HA.cjs +2955 -0
- package/dist/InteractiveVideo-BguGv-HA.cjs.map +1 -0
- package/dist/InteractiveVideo-Cg2jbU3j.js +2944 -0
- package/dist/InteractiveVideo-Cg2jbU3j.js.map +1 -0
- package/dist/LkAiProvider-DAPpA3PQ.d.cts +84 -0
- package/dist/LkAiProvider-DAPpA3PQ.d.cts.map +1 -0
- package/dist/LkAiProvider-QdEcIhoo.d.ts +84 -0
- package/dist/LkAiProvider-QdEcIhoo.d.ts.map +1 -0
- package/dist/LkIntlProvider-D_FdYXjQ.js +375 -0
- package/dist/LkIntlProvider-D_FdYXjQ.js.map +1 -0
- package/dist/LkIntlProvider-oi57lYB0.cjs +410 -0
- package/dist/LkIntlProvider-oi57lYB0.cjs.map +1 -0
- package/dist/MultipleChoice-COn5MtKY.cjs +433 -0
- package/dist/MultipleChoice-COn5MtKY.cjs.map +1 -0
- package/dist/MultipleChoice-Dra0_KYD.js +428 -0
- package/dist/MultipleChoice-Dra0_KYD.js.map +1 -0
- package/dist/PronunciationFeedback-DlMPGjkR.cjs +831 -0
- package/dist/PronunciationFeedback-DlMPGjkR.cjs.map +1 -0
- package/dist/PronunciationFeedback-Dxd6XPkO.js +772 -0
- package/dist/PronunciationFeedback-Dxd6XPkO.js.map +1 -0
- package/dist/ReadAloud-C8RhaUqV.js +1092 -0
- package/dist/{chunk-DC4CQCK5.js.map → ReadAloud-C8RhaUqV.js.map} +1 -1
- package/dist/ReadAloud-akDehitT.cjs +1103 -0
- package/dist/ReadAloud-akDehitT.cjs.map +1 -0
- package/dist/StimulusPanel-BgzByfkb.js +74 -0
- package/dist/StimulusPanel-BgzByfkb.js.map +1 -0
- package/dist/StimulusPanel-b66gaN3i.cjs +79 -0
- package/dist/StimulusPanel-b66gaN3i.cjs.map +1 -0
- package/dist/ThemeProvider-B6DyGqzh.d.cts +62 -0
- package/dist/ThemeProvider-B6DyGqzh.d.cts.map +1 -0
- package/dist/ThemeProvider-BCrxBaY-.d.ts +62 -0
- package/dist/ThemeProvider-BCrxBaY-.d.ts.map +1 -0
- package/dist/ThemeProvider-BRN1tj4n.cjs +187 -0
- package/dist/ThemeProvider-BRN1tj4n.cjs.map +1 -0
- package/dist/ThemeProvider-C7K9iMLX.js +158 -0
- package/dist/ThemeProvider-C7K9iMLX.js.map +1 -0
- package/dist/WrittenResponse-Bj3eFmGl.cjs +491 -0
- package/dist/WrittenResponse-Bj3eFmGl.cjs.map +1 -0
- package/dist/WrittenResponse-Cl8G6nO_.js +486 -0
- package/dist/WrittenResponse-Cl8G6nO_.js.map +1 -0
- package/dist/ai/LkAiProvider.cjs +34 -14
- package/dist/ai/LkAiProvider.cjs.map +1 -1
- package/dist/ai/LkAiProvider.d.cts +2 -83
- package/dist/ai/LkAiProvider.d.ts +2 -83
- package/dist/ai/LkAiProvider.js +33 -2
- package/dist/ai/LkAiProvider.js.map +1 -1
- package/dist/ai/useAiHelp.cjs +7 -25
- package/dist/ai/useAiHelp.d.cts +175 -177
- package/dist/ai/useAiHelp.d.cts.map +1 -0
- package/dist/ai/useAiHelp.d.ts +175 -177
- package/dist/ai/useAiHelp.d.ts.map +1 -0
- package/dist/ai/useAiHelp.js +3 -4
- package/dist/components/ActivityPreview.cjs +3 -29
- package/dist/components/ActivityPreview.d.cts +2 -81
- package/dist/components/ActivityPreview.d.ts +2 -81
- package/dist/components/ActivityPreview.js +2 -20
- package/dist/components/ActivitySequence.cjs +3 -30
- package/dist/components/ActivitySequence.d.cts +2 -432
- package/dist/components/ActivitySequence.d.ts +2 -432
- package/dist/components/ActivitySequence.js +2 -21
- package/dist/components/Dictation.cjs +4 -21
- package/dist/components/Dictation.d.cts +2 -19
- package/dist/components/Dictation.d.ts +2 -19
- package/dist/components/Dictation.js +3 -12
- package/dist/components/FillInTheBlanks.cjs +4 -20
- package/dist/components/FillInTheBlanks.d.cts +2 -30
- package/dist/components/FillInTheBlanks.d.ts +2 -30
- package/dist/components/FillInTheBlanks.js +3 -11
- package/dist/components/GapSelect.cjs +4 -20
- package/dist/components/GapSelect.d.cts +2 -27
- package/dist/components/GapSelect.d.ts +2 -27
- package/dist/components/GapSelect.js +3 -11
- package/dist/components/InteractiveVideo.cjs +5 -32
- package/dist/components/InteractiveVideo.d.cts +3 -291
- package/dist/components/InteractiveVideo.d.ts +3 -291
- package/dist/components/InteractiveVideo.js +3 -19
- package/dist/components/MultipleChoice.cjs +4 -20
- package/dist/components/MultipleChoice.d.cts +2 -26
- package/dist/components/MultipleChoice.d.ts +2 -26
- package/dist/components/MultipleChoice.js +3 -11
- package/dist/components/PronunciationFeedback.cjs +4 -17
- package/dist/components/PronunciationFeedback.d.cts +2 -90
- package/dist/components/PronunciationFeedback.d.ts +2 -90
- package/dist/components/PronunciationFeedback.js +3 -8
- package/dist/components/ReadAloud.cjs +4 -22
- package/dist/components/ReadAloud.d.cts +7 -11
- package/dist/components/ReadAloud.d.cts.map +1 -0
- package/dist/components/ReadAloud.d.ts +7 -11
- package/dist/components/ReadAloud.d.ts.map +1 -0
- package/dist/components/ReadAloud.js +3 -13
- package/dist/components/StimulusPanel.cjs +3 -14
- package/dist/components/StimulusPanel.d.cts +2 -51
- package/dist/components/StimulusPanel.d.ts +2 -51
- package/dist/components/StimulusPanel.js +2 -5
- package/dist/components/WrittenResponse.cjs +4 -19
- package/dist/components/WrittenResponse.d.cts +3 -18
- package/dist/components/WrittenResponse.d.ts +3 -18
- package/dist/components/WrittenResponse.js +3 -10
- package/dist/direction-Dq0_HtA5.js +183 -0
- package/dist/direction-Dq0_HtA5.js.map +1 -0
- package/dist/direction-K1pj0HCA.cjs +200 -0
- package/dist/direction-K1pj0HCA.cjs.map +1 -0
- package/dist/hooks/useActivityState.cjs +28 -10
- package/dist/hooks/useActivityState.cjs.map +1 -1
- package/dist/hooks/useActivityState.d.cts +25 -24
- package/dist/hooks/useActivityState.d.cts.map +1 -0
- package/dist/hooks/useActivityState.d.ts +25 -24
- package/dist/hooks/useActivityState.d.ts.map +1 -0
- package/dist/hooks/useActivityState.js +28 -2
- package/dist/hooks/useActivityState.js.map +1 -1
- package/dist/hooks/useSpeechRecorder.cjs +5 -16
- package/dist/hooks/useSpeechRecorder.d.cts +2 -127
- package/dist/hooks/useSpeechRecorder.d.ts +2 -127
- package/dist/hooks/useSpeechRecorder.js +3 -3
- package/dist/hooks/useXAPI.cjs +126 -10
- package/dist/hooks/useXAPI.cjs.map +1 -1
- package/dist/hooks/useXAPI.d.cts +14 -14
- package/dist/hooks/useXAPI.d.cts.map +1 -0
- package/dist/hooks/useXAPI.d.ts +14 -14
- package/dist/hooks/useXAPI.d.ts.map +1 -0
- package/dist/hooks/useXAPI.js +126 -2
- package/dist/hooks/useXAPI.js.map +1 -1
- package/dist/i18n/LkIntlProvider.cjs +9 -32
- package/dist/i18n/LkIntlProvider.d.cts +34 -38
- package/dist/i18n/LkIntlProvider.d.cts.map +1 -0
- package/dist/i18n/LkIntlProvider.d.ts +34 -38
- package/dist/i18n/LkIntlProvider.d.ts.map +1 -0
- package/dist/i18n/LkIntlProvider.js +3 -3
- package/dist/index-6P4rJHrJ.d.cts +432 -0
- package/dist/index-6P4rJHrJ.d.cts.map +1 -0
- package/dist/index-90CX504k.d.ts +90 -0
- package/dist/index-90CX504k.d.ts.map +1 -0
- package/dist/index-B_SgnjsC.d.cts +291 -0
- package/dist/index-B_SgnjsC.d.cts.map +1 -0
- package/dist/index-BavVW5z5.d.cts +25 -0
- package/dist/index-BavVW5z5.d.cts.map +1 -0
- package/dist/index-BhHv6Fiu.d.cts +28 -0
- package/dist/index-BhHv6Fiu.d.cts.map +1 -0
- package/dist/index-Bsu8VciA.d.ts +432 -0
- package/dist/index-Bsu8VciA.d.ts.map +1 -0
- package/dist/index-C1KGpD1k.d.ts +49 -0
- package/dist/index-C1KGpD1k.d.ts.map +1 -0
- package/dist/index-CKTpYTCC.d.cts +49 -0
- package/dist/index-CKTpYTCC.d.cts.map +1 -0
- package/dist/index-CO_SRFJj.d.ts +16 -0
- package/dist/index-CO_SRFJj.d.ts.map +1 -0
- package/dist/index-CSsuUsP7.d.cts +124 -0
- package/dist/index-CSsuUsP7.d.cts.map +1 -0
- package/dist/index-CunUyeuN.d.cts +78 -0
- package/dist/index-CunUyeuN.d.cts.map +1 -0
- package/dist/index-CzfRzks6.d.cts +90 -0
- package/dist/index-CzfRzks6.d.cts.map +1 -0
- package/dist/index-D14xtpBS.d.cts +24 -0
- package/dist/index-D14xtpBS.d.cts.map +1 -0
- package/dist/index-DHRPAPZf.d.ts +291 -0
- package/dist/index-DHRPAPZf.d.ts.map +1 -0
- package/dist/index-DLV6cK26.d.cts +16 -0
- package/dist/index-DLV6cK26.d.cts.map +1 -0
- package/dist/index-DbCCQ9cP.d.ts +124 -0
- package/dist/index-DbCCQ9cP.d.ts.map +1 -0
- package/dist/index-DeV5B2m5.d.ts +24 -0
- package/dist/index-DeV5B2m5.d.ts.map +1 -0
- package/dist/index-TZCJVSge.d.ts +28 -0
- package/dist/index-TZCJVSge.d.ts.map +1 -0
- package/dist/index-ZVvLHsd3.d.ts +25 -0
- package/dist/index-ZVvLHsd3.d.ts.map +1 -0
- package/dist/index-eQwBgpSw.d.ts +78 -0
- package/dist/index-eQwBgpSw.d.ts.map +1 -0
- package/dist/index.cjs +118 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -24
- package/dist/index.d.ts +20 -24
- package/dist/index.js +84 -29
- package/dist/index.js.map +1 -1
- package/dist/theme/ThemeProvider.cjs +8 -28
- package/dist/theme/ThemeProvider.d.cts +2 -59
- package/dist/theme/ThemeProvider.d.ts +2 -59
- package/dist/theme/ThemeProvider.js +3 -3
- package/dist/tries-CbbtZkdG.cjs +363 -0
- package/dist/tries-CbbtZkdG.cjs.map +1 -0
- package/dist/tries-DTHSrLCm.js +310 -0
- package/dist/tries-DTHSrLCm.js.map +1 -0
- package/dist/types-BOQd93x3.d.cts +994 -0
- package/dist/types-BOQd93x3.d.cts.map +1 -0
- package/dist/types-HIrgF_mS.d.ts +994 -0
- package/dist/types-HIrgF_mS.d.ts.map +1 -0
- package/dist/useAiHelp-B-ccDL5Q.js +906 -0
- package/dist/useAiHelp-B-ccDL5Q.js.map +1 -0
- package/dist/useAiHelp-h9yOLFqt.cjs +995 -0
- package/dist/useAiHelp-h9yOLFqt.cjs.map +1 -0
- package/dist/useSpeechRecorder--O_f-xvt.cjs +720 -0
- package/dist/useSpeechRecorder--O_f-xvt.cjs.map +1 -0
- package/dist/useSpeechRecorder-DtAYvl02.d.cts +154 -0
- package/dist/useSpeechRecorder-DtAYvl02.d.cts.map +1 -0
- package/dist/useSpeechRecorder-DtAYvl02.d.ts +154 -0
- package/dist/useSpeechRecorder-DtAYvl02.d.ts.map +1 -0
- package/dist/useSpeechRecorder-zIi9Dlw8.js +709 -0
- package/dist/useSpeechRecorder-zIi9Dlw8.js.map +1 -0
- package/package.json +10 -11
- package/dist/WrittenResponse-B7A5gHMj.d.ts +0 -115
- package/dist/WrittenResponse-BpL9p2Hn.d.cts +0 -115
- package/dist/ai/useAiHelp.cjs.map +0 -1
- package/dist/ai/useAiHelp.js.map +0 -1
- package/dist/chunk-2PNSVBLQ.cjs +0 -54
- package/dist/chunk-2PNSVBLQ.cjs.map +0 -1
- package/dist/chunk-2TW6WLBC.js +0 -80
- package/dist/chunk-2TW6WLBC.js.map +0 -1
- package/dist/chunk-4ZZWZSWI.js +0 -61
- package/dist/chunk-4ZZWZSWI.js.map +0 -1
- package/dist/chunk-6N43WDVG.js +0 -29
- package/dist/chunk-6N43WDVG.js.map +0 -1
- package/dist/chunk-7ALBSPYR.cjs +0 -436
- package/dist/chunk-7ALBSPYR.cjs.map +0 -1
- package/dist/chunk-7NFY3M3W.js +0 -234
- package/dist/chunk-7NFY3M3W.js.map +0 -1
- package/dist/chunk-7SB2MJA4.js +0 -29
- package/dist/chunk-7SB2MJA4.js.map +0 -1
- package/dist/chunk-ABJJ3LII.js +0 -434
- package/dist/chunk-ABJJ3LII.js.map +0 -1
- package/dist/chunk-BIGGXHAW.cjs +0 -342
- package/dist/chunk-BIGGXHAW.cjs.map +0 -1
- package/dist/chunk-C7QZE5BI.js +0 -430
- package/dist/chunk-C7QZE5BI.js.map +0 -1
- package/dist/chunk-CWTUWNV3.cjs +0 -2937
- package/dist/chunk-CWTUWNV3.cjs.map +0 -1
- package/dist/chunk-DC4CQCK5.js +0 -915
- package/dist/chunk-DS6HN3YR.cjs +0 -496
- package/dist/chunk-DS6HN3YR.cjs.map +0 -1
- package/dist/chunk-FDQR4GAZ.js +0 -240
- package/dist/chunk-FDQR4GAZ.js.map +0 -1
- package/dist/chunk-FKGMSOJF.js +0 -394
- package/dist/chunk-FKGMSOJF.js.map +0 -1
- package/dist/chunk-G6PXJ2SA.js +0 -823
- package/dist/chunk-G6PXJ2SA.js.map +0 -1
- package/dist/chunk-HNLRVRQR.js +0 -335
- package/dist/chunk-HNLRVRQR.js.map +0 -1
- package/dist/chunk-J3C72RPK.cjs +0 -31
- package/dist/chunk-J3C72RPK.cjs.map +0 -1
- package/dist/chunk-K63VPFOV.cjs +0 -134
- package/dist/chunk-K63VPFOV.cjs.map +0 -1
- package/dist/chunk-KFLIDTDU.cjs +0 -825
- package/dist/chunk-KFLIDTDU.cjs.map +0 -1
- package/dist/chunk-KHKH5LCT.cjs +0 -250
- package/dist/chunk-KHKH5LCT.cjs.map +0 -1
- package/dist/chunk-LBEKRN6Y.cjs +0 -664
- package/dist/chunk-LBEKRN6Y.cjs.map +0 -1
- package/dist/chunk-LPUWXIPR.cjs +0 -578
- package/dist/chunk-LPUWXIPR.cjs.map +0 -1
- package/dist/chunk-MCO52PV5.js +0 -520
- package/dist/chunk-MCO52PV5.js.map +0 -1
- package/dist/chunk-MKXEM2C5.cjs +0 -396
- package/dist/chunk-MKXEM2C5.cjs.map +0 -1
- package/dist/chunk-MXNC5UNO.js +0 -563
- package/dist/chunk-MXNC5UNO.js.map +0 -1
- package/dist/chunk-NAK55BYU.cjs +0 -859
- package/dist/chunk-NAK55BYU.cjs.map +0 -1
- package/dist/chunk-NDBMKGQ5.cjs +0 -599
- package/dist/chunk-NDBMKGQ5.cjs.map +0 -1
- package/dist/chunk-OD65OZIW.cjs +0 -159
- package/dist/chunk-OD65OZIW.cjs.map +0 -1
- package/dist/chunk-ORT3KLGI.js +0 -50
- package/dist/chunk-ORT3KLGI.js.map +0 -1
- package/dist/chunk-PNNUPUH4.cjs +0 -82
- package/dist/chunk-PNNUPUH4.cjs.map +0 -1
- package/dist/chunk-PWHECW6N.js +0 -2934
- package/dist/chunk-PWHECW6N.js.map +0 -1
- package/dist/chunk-RBS5A2DF.js +0 -857
- package/dist/chunk-RBS5A2DF.js.map +0 -1
- package/dist/chunk-RPWUDXG5.cjs +0 -63
- package/dist/chunk-RPWUDXG5.cjs.map +0 -1
- package/dist/chunk-TCDJY3H6.js +0 -494
- package/dist/chunk-TCDJY3H6.js.map +0 -1
- package/dist/chunk-TP5UMRAU.cjs +0 -918
- package/dist/chunk-TP5UMRAU.cjs.map +0 -1
- package/dist/chunk-TS7J6DTY.js +0 -597
- package/dist/chunk-TS7J6DTY.js.map +0 -1
- package/dist/chunk-TUVENMXB.cjs +0 -20
- package/dist/chunk-TUVENMXB.cjs.map +0 -1
- package/dist/chunk-UDCQTOVU.cjs +0 -523
- package/dist/chunk-UDCQTOVU.cjs.map +0 -1
- package/dist/chunk-UHKZXOXD.js +0 -128
- package/dist/chunk-UHKZXOXD.js.map +0 -1
- package/dist/chunk-V6BA44DS.cjs +0 -236
- package/dist/chunk-V6BA44DS.cjs.map +0 -1
- package/dist/chunk-XO5R73Y4.js +0 -653
- package/dist/chunk-XO5R73Y4.js.map +0 -1
- package/dist/chunk-XOR4MPEN.cjs +0 -31
- package/dist/chunk-XOR4MPEN.cjs.map +0 -1
- package/dist/chunk-YORQQXGO.js +0 -155
- package/dist/chunk-YORQQXGO.js.map +0 -1
- package/dist/chunk-YY4PFR3Z.cjs +0 -432
- package/dist/chunk-YY4PFR3Z.cjs.map +0 -1
- package/dist/chunk-ZPI6Q42R.js +0 -17
- package/dist/chunk-ZPI6Q42R.js.map +0 -1
- package/dist/components/ActivityPreview.cjs.map +0 -1
- package/dist/components/ActivityPreview.js.map +0 -1
- package/dist/components/ActivitySequence.cjs.map +0 -1
- package/dist/components/ActivitySequence.js.map +0 -1
- package/dist/components/Dictation.cjs.map +0 -1
- package/dist/components/Dictation.js.map +0 -1
- package/dist/components/FillInTheBlanks.cjs.map +0 -1
- package/dist/components/FillInTheBlanks.js.map +0 -1
- package/dist/components/GapSelect.cjs.map +0 -1
- package/dist/components/GapSelect.js.map +0 -1
- package/dist/components/InteractiveVideo.cjs.map +0 -1
- package/dist/components/InteractiveVideo.js.map +0 -1
- package/dist/components/MultipleChoice.cjs.map +0 -1
- package/dist/components/MultipleChoice.js.map +0 -1
- package/dist/components/PronunciationFeedback.cjs.map +0 -1
- package/dist/components/PronunciationFeedback.js.map +0 -1
- package/dist/components/ReadAloud.cjs.map +0 -1
- package/dist/components/ReadAloud.js.map +0 -1
- package/dist/components/StimulusPanel.cjs.map +0 -1
- package/dist/components/StimulusPanel.js.map +0 -1
- package/dist/components/WrittenResponse.cjs.map +0 -1
- package/dist/components/WrittenResponse.js.map +0 -1
- package/dist/hooks/useSpeechRecorder.cjs.map +0 -1
- package/dist/hooks/useSpeechRecorder.js.map +0 -1
- package/dist/i18n/LkIntlProvider.cjs.map +0 -1
- package/dist/i18n/LkIntlProvider.js.map +0 -1
- package/dist/machine-CuwKqWXC.d.cts +0 -26
- package/dist/machine-CuwKqWXC.d.ts +0 -26
- package/dist/theme/ThemeProvider.cjs.map +0 -1
- package/dist/theme/ThemeProvider.js.map +0 -1
- package/dist/types-C4IsUQsK.d.ts +0 -989
- package/dist/types-CJgZWtl4.d.cts +0 -989
|
@@ -0,0 +1,931 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
const require_ai_LkAiProvider = require("./ai/LkAiProvider.cjs");
|
|
3
|
+
const require_useAiHelp = require("./useAiHelp-h9yOLFqt.cjs");
|
|
4
|
+
const require_LkIntlProvider = require("./LkIntlProvider-oi57lYB0.cjs");
|
|
5
|
+
const require_ActivityErrorBoundary = require("./ActivityErrorBoundary-B379hvtL.cjs");
|
|
6
|
+
const require_hooks_useActivityState = require("./hooks/useActivityState.cjs");
|
|
7
|
+
const require_direction = require("./direction-K1pj0HCA.cjs");
|
|
8
|
+
const require_ActivityMedia = require("./ActivityMedia-CQQTRNl7.cjs");
|
|
9
|
+
const require_tries = require("./tries-CbbtZkdG.cjs");
|
|
10
|
+
const require_FeedbackRegion = require("./FeedbackRegion-BNNMtTY-.cjs");
|
|
11
|
+
let react = require("react");
|
|
12
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
13
|
+
let _intellectif_lk_core = require("@intellectif/lk-core");
|
|
14
|
+
//#region src/components/Dictation/Dictation.tsx
|
|
15
|
+
/** How long the learner has to stop typing before one `text-changed` is emitted. */
|
|
16
|
+
const TEXT_CHANGED_DEBOUNCE_MS = 500;
|
|
17
|
+
/**
|
|
18
|
+
* Present for assistive technology, invisible on screen. Inline rather than
|
|
19
|
+
* left to the optional skin: the per-word sentences are the marks' only
|
|
20
|
+
* channel for a screen reader, and without this a page that does not load the
|
|
21
|
+
* skin would show every one of them beside its word.
|
|
22
|
+
*/
|
|
23
|
+
const VISUALLY_HIDDEN = {
|
|
24
|
+
position: "absolute",
|
|
25
|
+
width: 1,
|
|
26
|
+
height: 1,
|
|
27
|
+
margin: -1,
|
|
28
|
+
padding: 0,
|
|
29
|
+
overflow: "hidden",
|
|
30
|
+
clip: "rect(0 0 0 0)",
|
|
31
|
+
clipPath: "inset(50%)",
|
|
32
|
+
whiteSpace: "nowrap",
|
|
33
|
+
border: 0
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Names playback groups. Per mount and never derived from `useId`: two
|
|
37
|
+
* separately hydrated roots derive the same id for the same tree position, and
|
|
38
|
+
* their recordings would pause each other. The name never reaches the DOM.
|
|
39
|
+
*/
|
|
40
|
+
let recordingsGroupCount = 0;
|
|
41
|
+
/**
|
|
42
|
+
* A token with nothing spelled in it, as the scorer reads it once composed:
|
|
43
|
+
* only punctuation, control, format and default-ignorable characters, the acute
|
|
44
|
+
* accent, grave accent (and its fullwidth form) and modifier apostrophe the
|
|
45
|
+
* scorer folds into an apostrophe, and the Arabic tatweel and N'Ko lajanyalan
|
|
46
|
+
* it removes.
|
|
47
|
+
*/
|
|
48
|
+
const UNSPELLED_RE = new RegExp(`^[\\p{P}\\p{Cc}\\p{Cf}\\p{Default_Ignorable_Code_Point}${String.fromCodePoint(180, 96, 65344, 700, 1600, 2042)}]*$`, "u");
|
|
49
|
+
/**
|
|
50
|
+
* Spacing as the scorer reads it: whitespace and NEXT LINE — but not the
|
|
51
|
+
* byte-order mark, which JavaScript counts as whitespace and the scorer
|
|
52
|
+
* removes as a format character, joining the letters on either side.
|
|
53
|
+
*/
|
|
54
|
+
const HINT_SPACE_RE = new RegExp(`(?:(?!${String.fromCodePoint(65279)})\\s|${String.fromCodePoint(133)})+`, "u");
|
|
55
|
+
/** A title with something in it a screen reader can say. */
|
|
56
|
+
const VISIBLE_TEXT_RE = /[^\p{White_Space}\p{Cc}\p{Cf}\p{Default_Ignorable_Code_Point}]/u;
|
|
57
|
+
const BULLET = "•";
|
|
58
|
+
const LEFT_TO_RIGHT_MARK = String.fromCodePoint(8206);
|
|
59
|
+
const RIGHT_TO_LEFT_MARK = String.fromCodePoint(8207);
|
|
60
|
+
/**
|
|
61
|
+
* Code points a font draws onto the character before them rather than beside
|
|
62
|
+
* it — combining marks (variation selectors among them), the joiners, emoji
|
|
63
|
+
* skin tones, tag characters, the vowel and final jamo of an old Hangul
|
|
64
|
+
* syllable, and the Thai and Lao SARA AM, whose ring the font sets on the
|
|
65
|
+
* consonant before — so their own span can have no width to decorate.
|
|
66
|
+
*/
|
|
67
|
+
const EXTENDING_RE = new RegExp(`^[\\p{M}${String.fromCodePoint(8204, 8205, 3635, 3763)}${String.fromCodePoint(127995)}-${String.fromCodePoint(127999)}${String.fromCodePoint(917536)}-${String.fromCodePoint(917631)}${String.fromCodePoint(4448)}-${String.fromCodePoint(4607)}${String.fromCodePoint(55216)}-${String.fromCodePoint(55295)}]$`, "u");
|
|
68
|
+
/**
|
|
69
|
+
* Viramas that stack the letter after them into one glyph without asking —
|
|
70
|
+
* Devanagari, Bengali, Gujarati, Oriya, Telugu, Kannada and Malayalam, the
|
|
71
|
+
* Burmese stacker, the Khmer coeng, and the Tai Tham, Balinese, Javanese,
|
|
72
|
+
* Chakma and Brahmi signs. Not the Tamil, Gurmukhi or Sinhala virama, which is
|
|
73
|
+
* drawn visibly and leaves most letters after it on their own.
|
|
74
|
+
*/
|
|
75
|
+
const STACKING_VIRAMAS = new Set([
|
|
76
|
+
2381,
|
|
77
|
+
2509,
|
|
78
|
+
2765,
|
|
79
|
+
2893,
|
|
80
|
+
3149,
|
|
81
|
+
3277,
|
|
82
|
+
3405,
|
|
83
|
+
4153,
|
|
84
|
+
6098,
|
|
85
|
+
6752,
|
|
86
|
+
6980,
|
|
87
|
+
43456,
|
|
88
|
+
69702,
|
|
89
|
+
69939
|
|
90
|
+
].map((point) => String.fromCodePoint(point)));
|
|
91
|
+
/**
|
|
92
|
+
* The letters a visible virama still draws into the letter before it: the
|
|
93
|
+
* Gurmukhi RA, YA and VA, which fonts set under the consonant, and the Tamil
|
|
94
|
+
* conjuncts KSSA (க்ஷ) and SHRII (ஸ்ரீ), which fonts draw as one glyph —
|
|
95
|
+
* each keyed by its letter, virama and the letter before the virama.
|
|
96
|
+
*/
|
|
97
|
+
const SUBJOINED_AFTER = new Map([
|
|
98
|
+
...[
|
|
99
|
+
2608,
|
|
100
|
+
2607,
|
|
101
|
+
2613
|
|
102
|
+
].map((point) => [String.fromCodePoint(point), (before) => before.at(-1) === String.fromCodePoint(2637)]),
|
|
103
|
+
[String.fromCodePoint(2999), (before) => before.at(-1) === String.fromCodePoint(3021) && before.at(-2) === String.fromCodePoint(2965)],
|
|
104
|
+
[String.fromCodePoint(2992), (before) => before.at(-1) === String.fromCodePoint(3021) && before.at(-2) === String.fromCodePoint(3e3)]
|
|
105
|
+
]);
|
|
106
|
+
/** Viramas after which a zero-width joiner asks for a conjunct: every stacking one, and the Sinhala. */
|
|
107
|
+
const JOINABLE_VIRAMAS = /* @__PURE__ */ new Set([...STACKING_VIRAMAS, String.fromCodePoint(3530)]);
|
|
108
|
+
const ZERO_WIDTH_JOINER = String.fromCodePoint(8205);
|
|
109
|
+
/**
|
|
110
|
+
* Characters the font draws into the letter after them: MALAYALAM LETTER DOT
|
|
111
|
+
* REPH, set on the consonant after it, and the MONGOLIAN VOWEL SEPARATOR, which
|
|
112
|
+
* has no width of its own and changes the shape of the vowel after it.
|
|
113
|
+
*/
|
|
114
|
+
const PREPENDED = /* @__PURE__ */ new Set([String.fromCodePoint(3406), String.fromCodePoint(6158)]);
|
|
115
|
+
const ARABIC_LAM = String.fromCodePoint(1604);
|
|
116
|
+
/** The alefs a lam before them fuses with into one glyph, in every Arabic font. */
|
|
117
|
+
const LIGATING_ALEFS = new Set([
|
|
118
|
+
1570,
|
|
119
|
+
1571,
|
|
120
|
+
1573,
|
|
121
|
+
1575,
|
|
122
|
+
1649,
|
|
123
|
+
1650,
|
|
124
|
+
1651,
|
|
125
|
+
1653
|
|
126
|
+
].map((point) => String.fromCodePoint(point)));
|
|
127
|
+
const REGIONAL_INDICATOR_RE = /^[\u{1f1e6}-\u{1f1ff}]$/u;
|
|
128
|
+
const LETTER_RE = /^\p{L}$/u;
|
|
129
|
+
/** A letter of any script but Latin, Greek or Cyrillic, whose common ligatures are only decorative. */
|
|
130
|
+
const LETTER_WITH_REQUIRED_LIGATURES_RE = /(?![\p{Script=Latin}\p{Script=Greek}\p{Script=Cyrillic}])\p{L}/u;
|
|
131
|
+
const DIGIT_RE = /^\p{Nd}$/u;
|
|
132
|
+
/**
|
|
133
|
+
* Signs laid out with the European number beside them (bidirectional class
|
|
134
|
+
* ET): the currency signs, the degree, plus-minus and per-mille signs.
|
|
135
|
+
*/
|
|
136
|
+
const NUMBER_SIGN_RE = new RegExp(`^[\\p{Sc}${String.fromCodePoint(176, 177, 8240, 8241, 8494, 8723)}]$`, "u");
|
|
137
|
+
/** Letters of the Arabic, Syriac and Thaana blocks and their presentation forms: before them, a European number is not one. */
|
|
138
|
+
const ARABIC_LETTER_BLOCKS = [
|
|
139
|
+
[1536, 1983],
|
|
140
|
+
[2144, 2303],
|
|
141
|
+
[64336, 65023],
|
|
142
|
+
[65136, 65279]
|
|
143
|
+
];
|
|
144
|
+
/**
|
|
145
|
+
* The text out of a `LearnerResponse`. A response of another activity type
|
|
146
|
+
* reads as nothing typed rather than throwing — a controlled host that has not
|
|
147
|
+
* yet swapped its state on an activity change would otherwise crash the attempt.
|
|
148
|
+
*/
|
|
149
|
+
function textOf(response) {
|
|
150
|
+
return response !== void 0 && response.type === "dictation" ? response.text : "";
|
|
151
|
+
}
|
|
152
|
+
/** The hint count out of a `LearnerResponse`; absent or another type reads as none. */
|
|
153
|
+
function hintsOf(response) {
|
|
154
|
+
if (response === void 0 || response.type !== "dictation") return 0;
|
|
155
|
+
const revealed = response.hintsRevealed;
|
|
156
|
+
return typeof revealed === "number" && Number.isInteger(revealed) && revealed > 0 ? revealed : 0;
|
|
157
|
+
}
|
|
158
|
+
/** The response this component emits; `hintsRevealed` is written only when there is one. */
|
|
159
|
+
function responseOf(text, hintsRevealed) {
|
|
160
|
+
return {
|
|
161
|
+
type: "dictation",
|
|
162
|
+
text,
|
|
163
|
+
...hintsRevealed > 0 ? { hintsRevealed } : {}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* The slower recording as an `ActivityMedia`: it follows the recording's
|
|
168
|
+
* playback policy minus `maxPlays` (`controls`, `seek`, `rate`, the native
|
|
169
|
+
* hints), so a locked scrubber or a fixed speed binds both files — and it never
|
|
170
|
+
* carries a budget of its own, so `<ActivityMedia>` never demands a binding
|
|
171
|
+
* for it. `undefined` when there is no slow recording.
|
|
172
|
+
*/
|
|
173
|
+
function slowMediaFor(data, label) {
|
|
174
|
+
const slow = data.slowMedia;
|
|
175
|
+
if (slow === void 0) return;
|
|
176
|
+
const { maxPlays: _budget, ...inherited } = data.media?.playback ?? {};
|
|
177
|
+
const hasPolicy = Object.values(inherited).some((value) => value !== void 0);
|
|
178
|
+
return {
|
|
179
|
+
type: "audio",
|
|
180
|
+
url: slow.url,
|
|
181
|
+
alt: slow.alt ?? label,
|
|
182
|
+
...hasPolicy ? { playback: inherited } : {}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* The transcript's words as a learner would read them: raw, in order, split at
|
|
187
|
+
* whitespace. A token with nothing in it that is spelled — a dash, an ellipsis
|
|
188
|
+
* — is joined to the word before it (or to the first word, when it leads), so
|
|
189
|
+
* every reveal shows a word. A script written without spaces is one token, so
|
|
190
|
+
* one reveal shows the whole sentence.
|
|
191
|
+
*/
|
|
192
|
+
function hintWordsOf(transcript) {
|
|
193
|
+
const words = [];
|
|
194
|
+
let leading = "";
|
|
195
|
+
for (const token of transcript.split(HINT_SPACE_RE)) {
|
|
196
|
+
if (token === "") continue;
|
|
197
|
+
const last = words.length - 1;
|
|
198
|
+
if (!UNSPELLED_RE.test(token.normalize("NFC"))) {
|
|
199
|
+
words.push(leading === "" ? token : `${leading} ${token}`);
|
|
200
|
+
leading = "";
|
|
201
|
+
} else if (last >= 0) words[last] = `${words[last]} ${token}`;
|
|
202
|
+
else leading = leading === "" ? token : `${leading} ${token}`;
|
|
203
|
+
}
|
|
204
|
+
return words;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Word marks rebuilt from stored scoring details — a server-recorded mark
|
|
208
|
+
* shown after an exam, with no transcript on the client. Extras are not in the
|
|
209
|
+
* details (the scorer writes one per transcript word), so none appear.
|
|
210
|
+
*/
|
|
211
|
+
function wordsFromDetails(details) {
|
|
212
|
+
if (details.length === 0 || !details.every((detail) => /^w\d+$/.test(detail.itemId))) return null;
|
|
213
|
+
return details.map((detail) => {
|
|
214
|
+
const attempt = Array.isArray(detail.learnerResponse) ? detail.learnerResponse.join(" ") : detail.learnerResponse;
|
|
215
|
+
const reference = Array.isArray(detail.correctResponse) ? detail.correctResponse.join(" ") : detail.correctResponse;
|
|
216
|
+
const correct = require_useAiHelp.detailIsRight(detail);
|
|
217
|
+
const missing = detail.outcome === "incorrect-omission" || !correct && attempt === "";
|
|
218
|
+
return {
|
|
219
|
+
itemId: detail.itemId,
|
|
220
|
+
reference,
|
|
221
|
+
attempt,
|
|
222
|
+
similarity: detail.score ?? (correct ? 1 : 0),
|
|
223
|
+
status: correct ? "correct" : missing ? "missing" : "incorrect"
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Whether `shown`, the character an operation displays, is drawn into the
|
|
229
|
+
* cluster whose characters so far are `before`: a mark, a joiner or a skin tone
|
|
230
|
+
* onto its base, a letter stacked under a virama, an alef fused with the lam
|
|
231
|
+
* before it, the second half of a flag.
|
|
232
|
+
*/
|
|
233
|
+
function extendsCluster(before, shown) {
|
|
234
|
+
const previous = before.at(-1);
|
|
235
|
+
if (EXTENDING_RE.test(shown)) return true;
|
|
236
|
+
if (LETTER_RE.test(shown)) {
|
|
237
|
+
if (STACKING_VIRAMAS.has(previous) || PREPENDED.has(previous) || SUBJOINED_AFTER.get(shown)?.(before) === true) return true;
|
|
238
|
+
if (previous === ZERO_WIDTH_JOINER && JOINABLE_VIRAMAS.has(before.at(-2))) return true;
|
|
239
|
+
}
|
|
240
|
+
if (previous === ARABIC_LAM && LIGATING_ALEFS.has(shown)) return true;
|
|
241
|
+
return REGIONAL_INDICATOR_RE.test(shown) && before.length % 2 === 1 && before.every((character) => REGIONAL_INDICATOR_RE.test(character));
|
|
242
|
+
}
|
|
243
|
+
/** The character an operation stands for: what should have been typed, or what was typed in excess. */
|
|
244
|
+
function characterOf(op) {
|
|
245
|
+
return op.reference === "" ? op.attempt : op.reference;
|
|
246
|
+
}
|
|
247
|
+
function isArabicLetter(character) {
|
|
248
|
+
const point = character?.codePointAt(0);
|
|
249
|
+
return point !== void 0 && LETTER_RE.test(character) && ARABIC_LETTER_BLOCKS.some(([first, last]) => point >= first && point <= last);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* The directions missing characters' bullets are laid out in, by operation
|
|
253
|
+
* index, worked out in two passes over the operations rather than a search
|
|
254
|
+
* per bullet: a word of 2,000 emoji missing its letters took seconds to draw.
|
|
255
|
+
*
|
|
256
|
+
* A bullet takes the character's own direction when it has one — a digit runs
|
|
257
|
+
* left to right, unless it is a digit of a right-to-left script. A currency,
|
|
258
|
+
* degree or per-mille sign beside a digit is laid out with that number, left to
|
|
259
|
+
* right, unless an Arabic letter comes before it, after which the number is not
|
|
260
|
+
* a European one. Otherwise, for a space or a punctuation mark, it takes the
|
|
261
|
+
* direction of the characters on both sides when they agree, and the content's
|
|
262
|
+
* when they do not. A bare bullet is a neutral, which the bidirectional
|
|
263
|
+
* algorithm would otherwise attach to the wrong side of a number or a word of
|
|
264
|
+
* the other direction.
|
|
265
|
+
*/
|
|
266
|
+
function bulletDirections(ops, contentDir) {
|
|
267
|
+
const own = ops.map((op) => require_direction.characterDirectionOf(characterOf(op)));
|
|
268
|
+
const before = [];
|
|
269
|
+
const arabicBefore = [];
|
|
270
|
+
let lastDirection;
|
|
271
|
+
let lastLetterArabic = false;
|
|
272
|
+
ops.forEach((op, index) => {
|
|
273
|
+
before[index] = lastDirection;
|
|
274
|
+
arabicBefore[index] = lastLetterArabic;
|
|
275
|
+
lastDirection = own[index] ?? lastDirection;
|
|
276
|
+
if (LETTER_RE.test(characterOf(op))) lastLetterArabic = isArabicLetter(characterOf(op));
|
|
277
|
+
});
|
|
278
|
+
const after = [];
|
|
279
|
+
let nextDirection;
|
|
280
|
+
for (let index = ops.length - 1; index >= 0; index -= 1) {
|
|
281
|
+
after[index] = nextDirection;
|
|
282
|
+
nextDirection = own[index] ?? nextDirection;
|
|
283
|
+
}
|
|
284
|
+
const directions = /* @__PURE__ */ new Map();
|
|
285
|
+
ops.forEach((op, index) => {
|
|
286
|
+
if (op.op !== "missing") return;
|
|
287
|
+
const neighbourIsDigit = [ops[index - 1], ops[index + 1]].some((neighbour) => neighbour !== void 0 && DIGIT_RE.test(characterOf(neighbour)));
|
|
288
|
+
directions.set(index, own[index] ?? (NUMBER_SIGN_RE.test(characterOf(op)) && neighbourIsDigit && !arabicBefore[index] ? "ltr" : before[index] !== void 0 && before[index] === after[index] ? before[index] : contentDir ?? before[index] ?? after[index]));
|
|
289
|
+
});
|
|
290
|
+
return directions;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* How a cluster is marked. One led by a missing character's bullet is missing,
|
|
294
|
+
* whatever marks sit on the bullet: the character they belong to is not there.
|
|
295
|
+
* Otherwise a cluster is marked as its wrong operations are when they agree —
|
|
296
|
+
* a correct letter with an extra mark is `extra` — and as a substitution when
|
|
297
|
+
* they do not.
|
|
298
|
+
*/
|
|
299
|
+
function clusterOpOf(ops) {
|
|
300
|
+
if (ops[0].op === "missing") return "missing";
|
|
301
|
+
const wrong = new Set(ops.map((op) => op.op).filter((kind) => kind !== "equal"));
|
|
302
|
+
if (wrong.size === 0) return "equal";
|
|
303
|
+
return wrong.size === 1 ? [...wrong][0] : "substitute";
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Character operations grouped into what is drawn as one cluster, so a wrong
|
|
307
|
+
* combining mark, joiner or stacked letter marks the glyph it is drawn into —
|
|
308
|
+
* alone, its span has no width, and its underline or strike would not be
|
|
309
|
+
* painted at all. A missing character's bullet carries only the marks drawn
|
|
310
|
+
* onto it, never a letter that is drawn apart. A missing character's bullet is
|
|
311
|
+
* wrapped in the direction mark of the character it stands for. Consecutive
|
|
312
|
+
* correct clusters are one run: an engine that shapes each element on its own
|
|
313
|
+
* would otherwise draw a correctly typed conjunct or a joined Mongolian word
|
|
314
|
+
* broken apart.
|
|
315
|
+
*/
|
|
316
|
+
function clustersOf(ops, contentDir) {
|
|
317
|
+
const bullets = bulletDirections(ops, contentDir);
|
|
318
|
+
const groups = [];
|
|
319
|
+
ops.forEach((op, index) => {
|
|
320
|
+
const shown = op.op === "missing" ? BULLET : op.attempt;
|
|
321
|
+
const current = groups.at(-1);
|
|
322
|
+
if (current !== void 0 && op.op !== "missing" && (current.ops[0].op === "missing" ? EXTENDING_RE.test(shown) : extendsCluster(current.shown, shown))) {
|
|
323
|
+
current.ops.push(op);
|
|
324
|
+
current.shown.push(shown);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const direction = bullets.get(index);
|
|
328
|
+
groups.push({
|
|
329
|
+
ops: [op],
|
|
330
|
+
shown: [shown],
|
|
331
|
+
...direction !== void 0 ? { bullet: direction } : {}
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
const clusters = [];
|
|
335
|
+
for (const group of groups) {
|
|
336
|
+
const mark = group.bullet === void 0 ? "" : group.bullet === "rtl" ? RIGHT_TO_LEFT_MARK : LEFT_TO_RIGHT_MARK;
|
|
337
|
+
const cluster = {
|
|
338
|
+
op: clusterOpOf(group.ops),
|
|
339
|
+
text: `${mark}${group.shown.join("")}${mark}`
|
|
340
|
+
};
|
|
341
|
+
const previous = clusters.at(-1);
|
|
342
|
+
if (previous !== void 0 && previous.op === "equal" && cluster.op === "equal") previous.text += cluster.text;
|
|
343
|
+
else clusters.push(cluster);
|
|
344
|
+
}
|
|
345
|
+
return clusters;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Character-level edit operations as decoration. Hidden from assistive
|
|
349
|
+
* technology as a whole: a screen reader must never verbalise surrogate or
|
|
350
|
+
* combining fragments one by one, and the word list already carries the
|
|
351
|
+
* meaning. Sighted colour-blind and forced-colors users still get one
|
|
352
|
+
* decoration per state, which the skin draws and the legend explains.
|
|
353
|
+
*/
|
|
354
|
+
function CharOps({ ops, contentDir }) {
|
|
355
|
+
return clustersOf(ops, contentDir).map((cluster, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
356
|
+
className: "lk-dc-op",
|
|
357
|
+
"data-op": cluster.op,
|
|
358
|
+
...LETTER_WITH_REQUIRED_LIGATURES_RE.test(cluster.text) ? {} : { "data-ligatures": "decorative" },
|
|
359
|
+
"aria-hidden": "true",
|
|
360
|
+
children: cluster.text
|
|
361
|
+
}, index));
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* The announcement, with authored feedback in its own isolated span: its
|
|
365
|
+
* direction is its own, so an English "Well heard." in an Arabic interface
|
|
366
|
+
* keeps its full stop at its end. Its language is not guessed — feedback may be
|
|
367
|
+
* written in the interface's language or the dictation's.
|
|
368
|
+
*/
|
|
369
|
+
function AnnouncementText({ announcement }) {
|
|
370
|
+
if (announcement === null) return null;
|
|
371
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [announcement.text, announcement.feedback ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
372
|
+
dir: "auto",
|
|
373
|
+
children: announcement.feedback
|
|
374
|
+
})] }) : null] });
|
|
375
|
+
}
|
|
376
|
+
function Dictation$1({ data, onComplete, onSubmit, onChange, onInteraction, value, defaultValue, defaultSubmitted, renderMode = "practice", outcome, mediaBudget, mediaStrings, strings, theme, locale, disabled, ai: aiProp, delivery, scoring }) {
|
|
377
|
+
const isExam = renderMode === "exam";
|
|
378
|
+
const isReview = renderMode === "review";
|
|
379
|
+
const s = require_LkIntlProvider.useLkStrings(strings);
|
|
380
|
+
const ai = require_ai_LkAiProvider.useLearnerAi(aiProp);
|
|
381
|
+
const policy = require_FeedbackRegion.useDeliveryPolicy(delivery);
|
|
382
|
+
const scoringPolicy = require_tries.useItemScoringPolicy(scoring);
|
|
383
|
+
const hasKey = typeof data.transcript === "string";
|
|
384
|
+
const devError = (0, react.useMemo)(() => {
|
|
385
|
+
if (!require_useAiHelp.isDevelopment()) return null;
|
|
386
|
+
if (data.redacted === true) {
|
|
387
|
+
if (hasKey) {
|
|
388
|
+
const shape = data;
|
|
389
|
+
return shape.type === "dictation" && typeof shape.id === "string" && typeof shape.title === "string" ? null : /* @__PURE__ */ new Error("Dictation received a revealed projection (redacted: true with a transcript) that is not a dictation: `type`, `id` and `title` are required.");
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
(0, _intellectif_lk_core.assertRedacted)(data);
|
|
393
|
+
return null;
|
|
394
|
+
} catch (error) {
|
|
395
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const result = (0, _intellectif_lk_core.validateActivity)("dictation", data);
|
|
399
|
+
return result.success ? null : new _intellectif_lk_core.ActivitySchemaError("dictation", result.errors);
|
|
400
|
+
}, [data, hasKey]);
|
|
401
|
+
const { state, start, complete, getTimeSpent, reset } = require_hooks_useActivityState.useActivityState(defaultSubmitted === true ? "completed" : "idle");
|
|
402
|
+
const tries = require_tries.useTries({
|
|
403
|
+
policy: scoringPolicy,
|
|
404
|
+
graded: renderMode === "practice" && policy.feedback,
|
|
405
|
+
submitted: state === "completed",
|
|
406
|
+
disabled: disabled === true
|
|
407
|
+
});
|
|
408
|
+
const resetTries = tries.reset;
|
|
409
|
+
const isControlled = value !== void 0;
|
|
410
|
+
const [internalText, setInternalText] = (0, react.useState)(() => textOf(defaultValue));
|
|
411
|
+
const [internalRevealed, setInternalRevealed] = (0, react.useState)(() => hintsOf(defaultValue));
|
|
412
|
+
const [result, setResult] = (0, react.useState)(null);
|
|
413
|
+
const [summary, setSummary] = (0, react.useState)(null);
|
|
414
|
+
const [solutionShown, setSolutionShown] = (0, react.useState)(false);
|
|
415
|
+
const formRef = (0, react.useRef)(null);
|
|
416
|
+
const feedbackRef = (0, react.useRef)(null);
|
|
417
|
+
const focusAfterRef = (0, react.useRef)(null);
|
|
418
|
+
const text = isControlled ? textOf(value) : internalText;
|
|
419
|
+
const revealed = isControlled ? hintsOf(value) : internalRevealed;
|
|
420
|
+
const defaultValueRef = (0, react.useRef)(defaultValue);
|
|
421
|
+
(0, react.useEffect)(() => {
|
|
422
|
+
defaultValueRef.current = defaultValue;
|
|
423
|
+
}, [defaultValue]);
|
|
424
|
+
const defaultSubmittedRef = (0, react.useRef)(defaultSubmitted);
|
|
425
|
+
(0, react.useEffect)(() => {
|
|
426
|
+
defaultSubmittedRef.current = defaultSubmitted;
|
|
427
|
+
}, [defaultSubmitted]);
|
|
428
|
+
const lastDataRef = (0, react.useRef)(data);
|
|
429
|
+
(0, react.useEffect)(() => {
|
|
430
|
+
if (lastDataRef.current === data) return;
|
|
431
|
+
lastDataRef.current = data;
|
|
432
|
+
setInternalText(textOf(defaultValueRef.current));
|
|
433
|
+
setInternalRevealed(hintsOf(defaultValueRef.current));
|
|
434
|
+
setResult(null);
|
|
435
|
+
setSummary(null);
|
|
436
|
+
setSolutionShown(false);
|
|
437
|
+
resetTries();
|
|
438
|
+
reset(defaultSubmittedRef.current === true ? "completed" : "idle");
|
|
439
|
+
}, [
|
|
440
|
+
data,
|
|
441
|
+
reset,
|
|
442
|
+
resetTries
|
|
443
|
+
]);
|
|
444
|
+
const textChangedRef = (0, react.useRef)(null);
|
|
445
|
+
(0, react.useEffect)(() => () => {
|
|
446
|
+
if (textChangedRef.current !== null) clearTimeout(textChangedRef.current.timer);
|
|
447
|
+
}, []);
|
|
448
|
+
const revealButtonRef = (0, react.useRef)(null);
|
|
449
|
+
const ids = (0, react.useId)();
|
|
450
|
+
const hintId = `${ids}-hint`;
|
|
451
|
+
const titleId = `${ids}-title`;
|
|
452
|
+
const solutionId = `${ids}-solution`;
|
|
453
|
+
const [recordingsGroup] = (0, react.useState)(() => {
|
|
454
|
+
recordingsGroupCount += 1;
|
|
455
|
+
return `lk-dc-recordings-${recordingsGroupCount}`;
|
|
456
|
+
});
|
|
457
|
+
const slowMedia = (0, react.useMemo)(() => slowMediaFor(data, s.dictationSlowRecording), [data, s.dictationSlowRecording]);
|
|
458
|
+
(0, react.useEffect)(() => {
|
|
459
|
+
const target = focusAfterRef.current;
|
|
460
|
+
if (target === null) return;
|
|
461
|
+
focusAfterRef.current = null;
|
|
462
|
+
if (target === "answer") formRef.current?.querySelector("textarea")?.focus();
|
|
463
|
+
else feedbackRef.current?.focus();
|
|
464
|
+
});
|
|
465
|
+
if (devError) throw devError;
|
|
466
|
+
if (data.redacted === true && renderMode === "practice") throw new Error(`Dictation "${data.id}" received redacted activity data in renderMode "practice", which grades locally and has no answer key to grade against. Render redacted data with renderMode="exam" (server grades) or "review" (pass \`outcome\`).`);
|
|
467
|
+
const submitted = state === "completed";
|
|
468
|
+
const inactive = disabled === true || submitted || isReview;
|
|
469
|
+
const marking = (isReview ? outcome?.status === "scored" : !isExam && submitted) && policy.feedback;
|
|
470
|
+
const solutionsShown = policy.solutions && !tries.open;
|
|
471
|
+
const transcript = isExam || !hasKey ? void 0 : data.transcript;
|
|
472
|
+
const hintWords = (0, react.useMemo)(() => transcript === void 0 ? [] : hintWordsOf(transcript), [transcript]);
|
|
473
|
+
const hintsOffered = policy.hints && data.hints?.mode === "progressive-words" && !isExam && !isReview && transcript !== void 0;
|
|
474
|
+
const hintsEnabled = hintsOffered && !submitted;
|
|
475
|
+
const hintTotal = hintWords.length;
|
|
476
|
+
const hintsShown = Math.min(revealed, hintTotal);
|
|
477
|
+
const reportedHints = hintsOffered ? hintsShown : 0;
|
|
478
|
+
const fireInteraction = (type, payload) => {
|
|
479
|
+
onInteraction?.({
|
|
480
|
+
type,
|
|
481
|
+
activityId: data.id,
|
|
482
|
+
timestamp: Date.now(),
|
|
483
|
+
payload
|
|
484
|
+
});
|
|
485
|
+
};
|
|
486
|
+
const flushTextChanged = () => {
|
|
487
|
+
const pending = textChangedRef.current;
|
|
488
|
+
if (pending === null) return;
|
|
489
|
+
clearTimeout(pending.timer);
|
|
490
|
+
textChangedRef.current = null;
|
|
491
|
+
fireInteraction("text-changed", { length: pending.length });
|
|
492
|
+
};
|
|
493
|
+
const handleTextChange = (next) => {
|
|
494
|
+
if (inactive) return;
|
|
495
|
+
if (state === "idle") start();
|
|
496
|
+
if (!isControlled) setInternalText(next);
|
|
497
|
+
onChange?.(responseOf(next, reportedHints));
|
|
498
|
+
if (onInteraction === void 0) return;
|
|
499
|
+
if (textChangedRef.current !== null) clearTimeout(textChangedRef.current.timer);
|
|
500
|
+
textChangedRef.current = {
|
|
501
|
+
length: next.length,
|
|
502
|
+
timer: setTimeout(() => {
|
|
503
|
+
const pending = textChangedRef.current;
|
|
504
|
+
textChangedRef.current = null;
|
|
505
|
+
if (pending !== null) fireInteraction("text-changed", { length: pending.length });
|
|
506
|
+
}, TEXT_CHANGED_DEBOUNCE_MS)
|
|
507
|
+
};
|
|
508
|
+
};
|
|
509
|
+
const setRevealed = (next) => {
|
|
510
|
+
if (!isControlled) setInternalRevealed(next);
|
|
511
|
+
onChange?.(responseOf(text, next));
|
|
512
|
+
};
|
|
513
|
+
const revealNextHint = () => {
|
|
514
|
+
if (inactive || hintsShown >= hintTotal) return;
|
|
515
|
+
if (state === "idle") start();
|
|
516
|
+
const next = hintsShown + 1;
|
|
517
|
+
setRevealed(next);
|
|
518
|
+
fireInteraction("hint-requested", {
|
|
519
|
+
revealed: next,
|
|
520
|
+
total: hintTotal
|
|
521
|
+
});
|
|
522
|
+
};
|
|
523
|
+
const resetHints = () => {
|
|
524
|
+
if (inactive || hintsShown === 0 || scoringPolicy.hintPenalty > 0) return;
|
|
525
|
+
revealButtonRef.current?.focus();
|
|
526
|
+
setRevealed(0);
|
|
527
|
+
};
|
|
528
|
+
const handleSubmit = (event) => {
|
|
529
|
+
event.preventDefault();
|
|
530
|
+
if (inactive) return;
|
|
531
|
+
const response = responseOf(text, reportedHints);
|
|
532
|
+
onSubmit?.(response);
|
|
533
|
+
flushTextChanged();
|
|
534
|
+
if (isExam) {
|
|
535
|
+
complete();
|
|
536
|
+
setSummary({
|
|
537
|
+
text: s.answerSubmitted,
|
|
538
|
+
feedback: null
|
|
539
|
+
});
|
|
540
|
+
fireInteraction("submitted", {
|
|
541
|
+
length: text.length,
|
|
542
|
+
hintsRevealed: reportedHints
|
|
543
|
+
});
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const answer = (0, _intellectif_lk_core.score)("dictation", data, response);
|
|
547
|
+
complete();
|
|
548
|
+
const timeSpent = getTimeSpent();
|
|
549
|
+
const counted = tries.record({
|
|
550
|
+
score: answer.score,
|
|
551
|
+
maxScore: answer.maxScore,
|
|
552
|
+
hintsRevealed: reportedHints
|
|
553
|
+
});
|
|
554
|
+
const mine = counted.tries[counted.tries.length - 1];
|
|
555
|
+
const costed = mine === void 0 ? answer.score : mine.scored;
|
|
556
|
+
const scoringResult = costed === answer.score ? answer : {
|
|
557
|
+
...answer,
|
|
558
|
+
score: costed,
|
|
559
|
+
passed: (0, _intellectif_lk_core.computePassThreshold)(data, costed)
|
|
560
|
+
};
|
|
561
|
+
const passed = counted.score === scoringResult.score ? scoringResult.passed : (0, _intellectif_lk_core.computePassThreshold)(data, counted.score);
|
|
562
|
+
const xapiStatement = _intellectif_lk_core.xAPIBuilder.buildAnsweredStatement({
|
|
563
|
+
actor: require_useAiHelp.ANONYMOUS_ACTOR,
|
|
564
|
+
object: {
|
|
565
|
+
id: require_useAiHelp.objectIdFor(data.id),
|
|
566
|
+
name: { [data.locale ?? "en-US"]: data.title },
|
|
567
|
+
...(0, _intellectif_lk_core.xapiDefinitionFor)(data)
|
|
568
|
+
},
|
|
569
|
+
scoringResult,
|
|
570
|
+
timeSpentMs: timeSpent,
|
|
571
|
+
response: response.text
|
|
572
|
+
});
|
|
573
|
+
setResult(answer);
|
|
574
|
+
onComplete?.(require_useAiHelp.stampTake({
|
|
575
|
+
score: counted.score,
|
|
576
|
+
maxScore: counted.maxScore,
|
|
577
|
+
passed,
|
|
578
|
+
timeSpent,
|
|
579
|
+
xapiStatement
|
|
580
|
+
}, require_useAiHelp.mintTake()));
|
|
581
|
+
const correctWords = answer.details.filter(require_useAiHelp.detailIsRight).length;
|
|
582
|
+
setSolutionShown(false);
|
|
583
|
+
setSummary({
|
|
584
|
+
text: `${s.answerSubmitted} ${s.scoreAnnouncement(Math.round(counted.score * 100), passed)} ${s.dictationWordsSummary(correctWords, answer.details.length)}`,
|
|
585
|
+
feedback: answer.feedback
|
|
586
|
+
});
|
|
587
|
+
fireInteraction("submitted", {
|
|
588
|
+
length: text.length,
|
|
589
|
+
hintsRevealed: reportedHints,
|
|
590
|
+
score: scoringResult.score
|
|
591
|
+
});
|
|
592
|
+
};
|
|
593
|
+
/** "Try again": the text stays, to be corrected; the marks go. Hints shown stay shown. */
|
|
594
|
+
const retry = () => {
|
|
595
|
+
reset("idle");
|
|
596
|
+
setSummary(null);
|
|
597
|
+
focusAfterRef.current = "answer";
|
|
598
|
+
};
|
|
599
|
+
/** "Show answer": no more tries; the question shows what a finished one shows. */
|
|
600
|
+
const closeTries = () => {
|
|
601
|
+
tries.close();
|
|
602
|
+
focusAfterRef.current = "feedback";
|
|
603
|
+
};
|
|
604
|
+
const given = isControlled ? value : defaultValue;
|
|
605
|
+
const hasResponse = given !== void 0 && given.type === "dictation";
|
|
606
|
+
const contentLang = typeof data.locale === "string" ? data.locale.replaceAll("_", "-") : void 0;
|
|
607
|
+
const localeDir = require_direction.localeDirectionOf(data.locale);
|
|
608
|
+
const alignment = (0, react.useMemo)(() => marking && transcript !== void 0 && (!isReview || hasResponse) ? (0, _intellectif_lk_core.alignDictation)(data, text) : null, [
|
|
609
|
+
marking,
|
|
610
|
+
transcript,
|
|
611
|
+
isReview,
|
|
612
|
+
hasResponse,
|
|
613
|
+
data,
|
|
614
|
+
text
|
|
615
|
+
]);
|
|
616
|
+
const sentenceDiff = (0, react.useMemo)(() => alignment !== null && alignment.attempt !== "" && solutionsShown ? (0, _intellectif_lk_core.diffDictationChars)(alignment.reference, alignment.attempt) : null, [alignment, solutionsShown]);
|
|
617
|
+
const detailWords = (0, react.useMemo)(() => isReview && alignment === null && outcome?.status === "scored" ? wordsFromDetails(outcome.details) : null, [
|
|
618
|
+
isReview,
|
|
619
|
+
alignment,
|
|
620
|
+
outcome
|
|
621
|
+
]);
|
|
622
|
+
const words = alignment?.words ?? detailWords;
|
|
623
|
+
const marksShown = marking && words !== null;
|
|
624
|
+
const contentDir = localeDir ?? require_direction.textDirectionOf(transcript ?? words?.map((word) => word.reference).join(" ") ?? "");
|
|
625
|
+
const nothingTyped = alignment !== null ? alignment.attempt === "" : words?.every((word) => word.attempt === "") ?? false;
|
|
626
|
+
const passed = isReview ? outcome?.status === "scored" || outcome?.status === "graded" ? outcome.passed : void 0 : result?.passed;
|
|
627
|
+
const reviewSummary = (0, react.useMemo)(() => {
|
|
628
|
+
if (!isReview || outcome === void 0) return null;
|
|
629
|
+
if (outcome.status === "scored") return {
|
|
630
|
+
text: s.scoreAnnouncement(Math.round(outcome.score * 100), outcome.passed),
|
|
631
|
+
feedback: outcome.feedback
|
|
632
|
+
};
|
|
633
|
+
if (outcome.status === "deferred") return {
|
|
634
|
+
text: s.notGradedYet,
|
|
635
|
+
feedback: null
|
|
636
|
+
};
|
|
637
|
+
if (outcome.status === "graded") {
|
|
638
|
+
const percent = outcome.maxScore > 0 ? Math.round(outcome.score / outcome.maxScore * 100) : Math.round(outcome.score * 100);
|
|
639
|
+
return {
|
|
640
|
+
text: s.scoreAnnouncement(percent, outcome.passed),
|
|
641
|
+
feedback: outcome.feedback ?? null
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
text: s.noGradeAvailable,
|
|
646
|
+
feedback: null
|
|
647
|
+
};
|
|
648
|
+
}, [
|
|
649
|
+
isReview,
|
|
650
|
+
outcome,
|
|
651
|
+
s
|
|
652
|
+
]);
|
|
653
|
+
const sentenceFor = (word) => {
|
|
654
|
+
switch (word.status) {
|
|
655
|
+
case "correct": return s.dictationWordCorrect(word.attempt);
|
|
656
|
+
case "incorrect": return solutionsShown ? s.dictationWordWrong(word.attempt, word.reference) : s.dictationWordWrongUnnamed(word.attempt);
|
|
657
|
+
case "missing": return solutionsShown ? s.dictationWordMissing(word.reference) : s.dictationWordMissingUnnamed;
|
|
658
|
+
default: return s.dictationWordExtra(word.attempt);
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
const acceptedTranscripts = transcript === void 0 || !Array.isArray(data.acceptedTranscripts) ? void 0 : data.acceptedTranscripts.filter((entry) => typeof entry === "string");
|
|
662
|
+
const extra = !isReview && policy.feedback && submitted && tries.counted !== null ? require_tries.triesSummary(s, scoringPolicy, tries.counted, tries.open) : "";
|
|
663
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
664
|
+
ref: formRef,
|
|
665
|
+
className: "lk-dc",
|
|
666
|
+
"aria-labelledby": VISIBLE_TEXT_RE.test(data.title) ? titleId : void 0,
|
|
667
|
+
lang: locale,
|
|
668
|
+
"data-render-mode": renderMode,
|
|
669
|
+
style: theme,
|
|
670
|
+
onSubmit: handleSubmit,
|
|
671
|
+
children: [
|
|
672
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
673
|
+
id: titleId,
|
|
674
|
+
className: "lk-dc-title",
|
|
675
|
+
dir: localeDir ?? "auto",
|
|
676
|
+
lang: contentLang,
|
|
677
|
+
children: data.title
|
|
678
|
+
}),
|
|
679
|
+
data.media !== void 0 || slowMedia !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
680
|
+
className: "lk-dc-recordings",
|
|
681
|
+
children: [data.media !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
682
|
+
className: "lk-dc-recording",
|
|
683
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", {
|
|
684
|
+
className: "lk-dc-recording-label",
|
|
685
|
+
children: s.dictationRecording
|
|
686
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ActivityMedia.ActivityMedia, {
|
|
687
|
+
media: data.media,
|
|
688
|
+
renderMode,
|
|
689
|
+
playbackGroup: recordingsGroup,
|
|
690
|
+
...mediaBudget !== void 0 ? { mediaBudget } : {},
|
|
691
|
+
...mediaStrings !== void 0 ? { mediaStrings } : {},
|
|
692
|
+
...strings !== void 0 ? { strings } : {},
|
|
693
|
+
...onInteraction !== void 0 ? { onInteraction } : {},
|
|
694
|
+
...locale !== void 0 ? { locale } : {},
|
|
695
|
+
...disabled !== void 0 ? { disabled } : {}
|
|
696
|
+
})]
|
|
697
|
+
}) : null, slowMedia !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
698
|
+
className: "lk-dc-recording",
|
|
699
|
+
"data-slow": "true",
|
|
700
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", {
|
|
701
|
+
className: "lk-dc-recording-label",
|
|
702
|
+
children: s.dictationSlowRecording
|
|
703
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ActivityMedia.ActivityMedia, {
|
|
704
|
+
media: slowMedia,
|
|
705
|
+
renderMode,
|
|
706
|
+
playbackGroup: recordingsGroup,
|
|
707
|
+
...mediaStrings !== void 0 ? { mediaStrings } : {},
|
|
708
|
+
...strings !== void 0 ? { strings } : {},
|
|
709
|
+
...locale !== void 0 ? { locale } : {},
|
|
710
|
+
...disabled !== void 0 ? { disabled } : {}
|
|
711
|
+
})]
|
|
712
|
+
}) : null]
|
|
713
|
+
}) : null,
|
|
714
|
+
hintsEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
715
|
+
className: "lk-dc-hints",
|
|
716
|
+
children: [
|
|
717
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
718
|
+
ref: revealButtonRef,
|
|
719
|
+
type: "button",
|
|
720
|
+
className: "lk-dc-hint-btn",
|
|
721
|
+
disabled: inactive,
|
|
722
|
+
"aria-disabled": hintsShown >= hintTotal || void 0,
|
|
723
|
+
onClick: revealNextHint,
|
|
724
|
+
children: s.dictationRevealNextWord(hintsShown, hintTotal)
|
|
725
|
+
}),
|
|
726
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_tries.HintCost, {
|
|
727
|
+
policy: scoringPolicy,
|
|
728
|
+
strings: s
|
|
729
|
+
}),
|
|
730
|
+
hintsShown > 0 && scoringPolicy.hintPenalty <= 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
731
|
+
type: "button",
|
|
732
|
+
className: "lk-dc-hint-reset",
|
|
733
|
+
disabled: inactive,
|
|
734
|
+
onClick: resetHints,
|
|
735
|
+
children: s.dictationResetHints
|
|
736
|
+
}) : null,
|
|
737
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
738
|
+
id: hintId,
|
|
739
|
+
className: "lk-dc-hint",
|
|
740
|
+
role: "status",
|
|
741
|
+
dir: contentDir,
|
|
742
|
+
lang: contentLang,
|
|
743
|
+
children: hintsShown > 0 ? `${hintWords.slice(0, hintsShown).join(" ")}${hintsShown < hintTotal ? " …" : ""}` : ""
|
|
744
|
+
})
|
|
745
|
+
]
|
|
746
|
+
}) : null,
|
|
747
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
748
|
+
disabled: inactive,
|
|
749
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
750
|
+
className: "lk-dc-input",
|
|
751
|
+
"aria-label": s.dictationInputLabel,
|
|
752
|
+
"aria-describedby": hintsEnabled && hintsShown > 0 ? hintId : void 0,
|
|
753
|
+
dir: "auto",
|
|
754
|
+
rows: 3,
|
|
755
|
+
maxLength: _intellectif_lk_core.DICTATION_MAX_TEXT_LENGTH,
|
|
756
|
+
autoComplete: "off",
|
|
757
|
+
autoCapitalize: "none",
|
|
758
|
+
autoCorrect: "off",
|
|
759
|
+
spellCheck: false,
|
|
760
|
+
value: text,
|
|
761
|
+
disabled: inactive,
|
|
762
|
+
"aria-disabled": inactive || void 0,
|
|
763
|
+
"data-correct": marking && passed !== void 0 && (!isReview || hasResponse) ? String(passed) : void 0,
|
|
764
|
+
onChange: (event) => handleTextChange(event.target.value)
|
|
765
|
+
}), isReview ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
766
|
+
type: "submit",
|
|
767
|
+
disabled: inactive,
|
|
768
|
+
children: isExam ? s.submitAnswers : s.checkAnswers
|
|
769
|
+
})]
|
|
770
|
+
}),
|
|
771
|
+
marksShown ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
772
|
+
className: "lk-dc-result",
|
|
773
|
+
children: [nothingTyped ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
774
|
+
className: "lk-dc-nothing",
|
|
775
|
+
role: "note",
|
|
776
|
+
children: s.dictationNothingTyped
|
|
777
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
778
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
|
|
779
|
+
className: "lk-dc-words",
|
|
780
|
+
"aria-label": s.dictationMarksLabel,
|
|
781
|
+
dir: contentDir,
|
|
782
|
+
children: words?.map((word, index) => {
|
|
783
|
+
const sentence = sentenceFor(word);
|
|
784
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
785
|
+
className: "lk-dc-word",
|
|
786
|
+
"data-state": word.status,
|
|
787
|
+
title: sentence,
|
|
788
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
789
|
+
className: "lk-visually-hidden",
|
|
790
|
+
style: VISUALLY_HIDDEN,
|
|
791
|
+
children: sentence
|
|
792
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
793
|
+
className: "lk-dc-word-text",
|
|
794
|
+
"aria-hidden": "true",
|
|
795
|
+
dir: contentDir ?? "auto",
|
|
796
|
+
lang: contentLang,
|
|
797
|
+
...word.status === "incorrect" && alignment !== null && solutionsShown ? { "data-marks": "characters" } : {},
|
|
798
|
+
children: word.status === "incorrect" && alignment !== null && solutionsShown ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CharOps, {
|
|
799
|
+
ops: (0, _intellectif_lk_core.diffDictationChars)(word.reference, word.attempt),
|
|
800
|
+
contentDir
|
|
801
|
+
}) : word.status === "missing" ? solutionsShown ? word.reference : "…" : word.attempt
|
|
802
|
+
})]
|
|
803
|
+
}, index);
|
|
804
|
+
})
|
|
805
|
+
}),
|
|
806
|
+
sentenceDiff !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
807
|
+
className: "lk-dc-diff",
|
|
808
|
+
"aria-hidden": "true",
|
|
809
|
+
dir: contentDir,
|
|
810
|
+
lang: contentLang,
|
|
811
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CharOps, {
|
|
812
|
+
ops: sentenceDiff,
|
|
813
|
+
contentDir
|
|
814
|
+
})
|
|
815
|
+
}) : null,
|
|
816
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
817
|
+
className: "lk-dc-legend",
|
|
818
|
+
children: [
|
|
819
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
820
|
+
"data-state": "correct",
|
|
821
|
+
children: s.dictationLegendCorrect
|
|
822
|
+
}),
|
|
823
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
824
|
+
"data-state": "incorrect",
|
|
825
|
+
children: s.dictationLegendWrong
|
|
826
|
+
}),
|
|
827
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
828
|
+
"data-state": "missing",
|
|
829
|
+
children: s.dictationLegendMissing
|
|
830
|
+
}),
|
|
831
|
+
alignment !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
832
|
+
"data-state": "extra",
|
|
833
|
+
children: s.dictationLegendExtra
|
|
834
|
+
}) : null
|
|
835
|
+
]
|
|
836
|
+
}),
|
|
837
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
838
|
+
className: "lk-dc-diff-note",
|
|
839
|
+
children: s.dictationDiffNote
|
|
840
|
+
})
|
|
841
|
+
] }), transcript !== void 0 && solutionsShown ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
842
|
+
type: "button",
|
|
843
|
+
className: "lk-dc-solution-toggle",
|
|
844
|
+
"aria-expanded": solutionShown,
|
|
845
|
+
"aria-controls": solutionId,
|
|
846
|
+
onClick: () => setSolutionShown((shown) => !shown),
|
|
847
|
+
children: solutionShown ? s.hideSolution : s.showSolution
|
|
848
|
+
}), solutionShown ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("blockquote", {
|
|
849
|
+
id: solutionId,
|
|
850
|
+
className: "lk-dc-solution",
|
|
851
|
+
"aria-label": s.dictationSolutionLabel,
|
|
852
|
+
dir: contentDir,
|
|
853
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
854
|
+
lang: contentLang,
|
|
855
|
+
children: transcript
|
|
856
|
+
}), acceptedTranscripts !== void 0 && acceptedTranscripts.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
857
|
+
className: "lk-dc-solution-alternatives",
|
|
858
|
+
lang: contentLang,
|
|
859
|
+
children: acceptedTranscripts.map((alternative) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: alternative }, alternative))
|
|
860
|
+
}) : null]
|
|
861
|
+
}) : null] }) : null]
|
|
862
|
+
}) : null,
|
|
863
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_FeedbackRegion.FeedbackRegion, {
|
|
864
|
+
id: `${data.id}-feedback`,
|
|
865
|
+
...scoringPolicy.retries > 0 ? { ref: feedbackRef } : {},
|
|
866
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnnouncementText, { announcement: require_FeedbackRegion.feedbackAnnouncement({
|
|
867
|
+
review: isReview,
|
|
868
|
+
feedback: policy.feedback,
|
|
869
|
+
outcome,
|
|
870
|
+
readBack: reviewSummary,
|
|
871
|
+
submitted: summary !== null,
|
|
872
|
+
result: summary === null || extra === "" ? summary : {
|
|
873
|
+
...summary,
|
|
874
|
+
text: `${summary.text} ${extra}`
|
|
875
|
+
},
|
|
876
|
+
received: {
|
|
877
|
+
text: s.answerSubmitted,
|
|
878
|
+
feedback: null
|
|
879
|
+
}
|
|
880
|
+
}) })
|
|
881
|
+
}),
|
|
882
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_tries.TryActions, {
|
|
883
|
+
tries,
|
|
884
|
+
solutions: policy.solutions && transcript !== void 0,
|
|
885
|
+
strings: s,
|
|
886
|
+
onRetry: retry,
|
|
887
|
+
onClose: closeTries
|
|
888
|
+
}),
|
|
889
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_tries.AiExplanation, {
|
|
890
|
+
ai,
|
|
891
|
+
data,
|
|
892
|
+
renderMode,
|
|
893
|
+
submitted,
|
|
894
|
+
response: responseOf(text, reportedHints),
|
|
895
|
+
outcome,
|
|
896
|
+
locale,
|
|
897
|
+
onInteraction,
|
|
898
|
+
strings: s,
|
|
899
|
+
delivery: tries.open ? {
|
|
900
|
+
...policy,
|
|
901
|
+
solutions: false
|
|
902
|
+
} : policy
|
|
903
|
+
})
|
|
904
|
+
]
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
//#endregion
|
|
908
|
+
//#region src/components/Dictation/index.tsx
|
|
909
|
+
/**
|
|
910
|
+
* Public Dictation activity: the core wrapped in `ActivityErrorBoundary` so a
|
|
911
|
+
* render failure (including the dev schema-validation throw and the
|
|
912
|
+
* redacted-in-`practice` guard) degrades to an accessible fallback instead of
|
|
913
|
+
* crashing the host.
|
|
914
|
+
*/
|
|
915
|
+
function Dictation(props) {
|
|
916
|
+
const strings = require_LkIntlProvider.useLkStrings(props.strings);
|
|
917
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ActivityErrorBoundary.ActivityErrorBoundary, {
|
|
918
|
+
activityTitle: props.data?.title,
|
|
919
|
+
strings,
|
|
920
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Dictation$1, { ...props })
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
//#endregion
|
|
924
|
+
Object.defineProperty(exports, "Dictation", {
|
|
925
|
+
enumerable: true,
|
|
926
|
+
get: function() {
|
|
927
|
+
return Dictation;
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
//# sourceMappingURL=Dictation-jECzL88E.cjs.map
|