@magmamath/students-features 1.4.6 → 1.5.0-rc.10

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 (412) hide show
  1. package/dist/commonjs/features/endOfAssignment/components/CorrectAnswer/CorrectAnswer.js +1 -6
  2. package/dist/commonjs/features/endOfAssignment/components/CorrectAnswer/CorrectAnswer.js.map +1 -1
  3. package/dist/commonjs/features/fluency/components/Fluency.js +6 -12
  4. package/dist/commonjs/features/fluency/components/Fluency.js.map +1 -1
  5. package/dist/commonjs/features/fluency/components/select-math-operator/SelectMathOperator.js +5 -13
  6. package/dist/commonjs/features/fluency/components/select-math-operator/SelectMathOperator.js.map +1 -1
  7. package/dist/commonjs/features/fluency/model/FluencyModel.js +0 -21
  8. package/dist/commonjs/features/fluency/model/FluencyModel.js.map +1 -1
  9. package/dist/commonjs/features/fluency/shared/fluency.constants.js +1 -6
  10. package/dist/commonjs/features/fluency/shared/fluency.constants.js.map +1 -1
  11. package/dist/commonjs/features/fluency/shared/fluency.helpers.js +1 -3
  12. package/dist/commonjs/features/fluency/shared/fluency.helpers.js.map +1 -1
  13. package/dist/commonjs/features/pmProgress/model/PmProgressModel.js +1 -7
  14. package/dist/commonjs/features/pmProgress/model/PmProgressModel.js.map +1 -1
  15. package/dist/commonjs/features/pmProgress/model/PmRecommendationsModel.js +4 -20
  16. package/dist/commonjs/features/pmProgress/model/PmRecommendationsModel.js.map +1 -1
  17. package/dist/commonjs/features/pmProgress/shared/pmProgress.helpers.js +1 -22
  18. package/dist/commonjs/features/pmProgress/shared/pmProgress.helpers.js.map +1 -1
  19. package/dist/commonjs/features/translation/constants.js +11 -0
  20. package/dist/commonjs/features/translation/constants.js.map +1 -0
  21. package/dist/commonjs/features/translation/helpers.js +60 -0
  22. package/dist/commonjs/features/translation/helpers.js.map +1 -0
  23. package/dist/commonjs/features/translation/index.js +105 -0
  24. package/dist/commonjs/features/translation/index.js.map +1 -0
  25. package/dist/commonjs/features/translation/model/TranslationModel.js +122 -0
  26. package/dist/commonjs/features/translation/model/TranslationModel.js.map +1 -0
  27. package/dist/commonjs/features/translation/model/TranslationPreprocessor.js +142 -0
  28. package/dist/commonjs/features/translation/model/TranslationPreprocessor.js.map +1 -0
  29. package/dist/commonjs/features/translation/model/TranslationService.js +112 -0
  30. package/dist/commonjs/features/translation/model/TranslationService.js.map +1 -0
  31. package/dist/commonjs/features/translation/preserve/wordsToPreserve.js +314 -0
  32. package/dist/commonjs/features/translation/preserve/wordsToPreserve.js.map +1 -0
  33. package/dist/commonjs/features/translation/providers/google/GoogleTranslationProvider.js +50 -0
  34. package/dist/commonjs/features/translation/providers/google/GoogleTranslationProvider.js.map +1 -0
  35. package/dist/commonjs/features/translation/providers/google/supportedLanguages.js +230 -0
  36. package/dist/commonjs/features/translation/providers/google/supportedLanguages.js.map +1 -0
  37. package/dist/commonjs/features/translation/providers/google.types.js +2 -0
  38. package/dist/commonjs/features/translation/providers/google.types.js.map +1 -0
  39. package/dist/commonjs/features/translation/providers/translation.interface.js +49 -0
  40. package/dist/commonjs/features/translation/providers/translation.interface.js.map +1 -0
  41. package/dist/commonjs/features/translation/types.js +2 -0
  42. package/dist/commonjs/features/translation/types.js.map +1 -0
  43. package/dist/commonjs/features/voice/index.js +15 -4
  44. package/dist/commonjs/features/voice/index.js.map +1 -1
  45. package/dist/commonjs/features/voice/playing/components/VoiceIcon.js +1 -1
  46. package/dist/commonjs/features/voice/playing/components/VoiceIcon.js.map +1 -1
  47. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptContent.js +44 -0
  48. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptContent.js.map +1 -0
  49. package/dist/commonjs/features/voice/playing/components/VoiceTranscription.js +107 -0
  50. package/dist/commonjs/features/voice/playing/components/VoiceTranscription.js.map +1 -0
  51. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionCollapsible.js +66 -0
  52. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionCollapsible.js.map +1 -0
  53. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionHeaderActions.js +58 -0
  54. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionHeaderActions.js.map +1 -0
  55. package/dist/commonjs/features/voice/playing/hooks/{useVoiceTranscriptionsDropdown.js → useDropdownAnimation.js} +8 -7
  56. package/dist/commonjs/features/voice/playing/hooks/useDropdownAnimation.js.map +1 -0
  57. package/dist/commonjs/features/voice/playing/hooks/useVoicePlayerLifecycle.js +53 -0
  58. package/dist/commonjs/features/voice/playing/hooks/useVoicePlayerLifecycle.js.map +1 -0
  59. package/dist/commonjs/features/voice/playing/hooks/useVoiceTranscriptionController.js +111 -0
  60. package/dist/commonjs/features/voice/playing/hooks/useVoiceTranscriptionController.js.map +1 -0
  61. package/dist/commonjs/features/voice/playing/model/{VoiceTranscriptionsDropdown.model.js → Dropdown.model.js} +4 -4
  62. package/dist/commonjs/features/voice/playing/model/Dropdown.model.js.map +1 -0
  63. package/dist/commonjs/features/voice/playing/model/PlaybackController.js +192 -0
  64. package/dist/commonjs/features/voice/playing/model/PlaybackController.js.map +1 -0
  65. package/dist/commonjs/features/voice/playing/model/Player.model.js +50 -5
  66. package/dist/commonjs/features/voice/playing/model/Player.model.js.map +1 -1
  67. package/dist/commonjs/features/voice/playing/model/{VoiceTranscriptionsCollection.js → TranscriptionsCollection.js} +4 -4
  68. package/dist/commonjs/features/voice/playing/model/TranscriptionsCollection.js.map +1 -0
  69. package/dist/commonjs/features/voice/playing/model/TranscriptionsDownloader.model.js +83 -0
  70. package/dist/commonjs/features/voice/playing/model/TranscriptionsDownloader.model.js.map +1 -0
  71. package/dist/commonjs/features/voice/playing/model/Translation.model.js +62 -0
  72. package/dist/commonjs/features/voice/playing/model/Translation.model.js.map +1 -0
  73. package/dist/commonjs/features/voice/playing/model/VoiceFileDownloader.model.js +2 -2
  74. package/dist/commonjs/features/voice/playing/model/VoiceFileDownloader.model.js.map +1 -1
  75. package/dist/commonjs/features/voice/playing/model/VoicePlayer.model.js +58 -172
  76. package/dist/commonjs/features/voice/playing/model/VoicePlayer.model.js.map +1 -1
  77. package/dist/commonjs/features/voice/recording/components/VoiceRecordDivider.js +1 -1
  78. package/dist/commonjs/features/voice/recording/components/VoiceRecordDivider.js.map +1 -1
  79. package/dist/commonjs/features/voice/types.js +12 -1
  80. package/dist/commonjs/features/voice/types.js.map +1 -1
  81. package/dist/commonjs/index.js +12 -0
  82. package/dist/commonjs/index.js.map +1 -1
  83. package/dist/module/features/endOfAssignment/components/CorrectAnswer/CorrectAnswer.js +2 -7
  84. package/dist/module/features/endOfAssignment/components/CorrectAnswer/CorrectAnswer.js.map +1 -1
  85. package/dist/module/features/fluency/components/Fluency.js +8 -14
  86. package/dist/module/features/fluency/components/Fluency.js.map +1 -1
  87. package/dist/module/features/fluency/components/select-math-operator/SelectMathOperator.js +5 -13
  88. package/dist/module/features/fluency/components/select-math-operator/SelectMathOperator.js.map +1 -1
  89. package/dist/module/features/fluency/model/FluencyModel.js +1 -22
  90. package/dist/module/features/fluency/model/FluencyModel.js.map +1 -1
  91. package/dist/module/features/fluency/shared/fluency.constants.js +0 -5
  92. package/dist/module/features/fluency/shared/fluency.constants.js.map +1 -1
  93. package/dist/module/features/fluency/shared/fluency.helpers.js +0 -1
  94. package/dist/module/features/fluency/shared/fluency.helpers.js.map +1 -1
  95. package/dist/module/features/pmProgress/model/PmProgressModel.js +1 -7
  96. package/dist/module/features/pmProgress/model/PmProgressModel.js.map +1 -1
  97. package/dist/module/features/pmProgress/model/PmRecommendationsModel.js +4 -20
  98. package/dist/module/features/pmProgress/model/PmRecommendationsModel.js.map +1 -1
  99. package/dist/module/features/pmProgress/shared/pmProgress.helpers.js +0 -20
  100. package/dist/module/features/pmProgress/shared/pmProgress.helpers.js.map +1 -1
  101. package/dist/module/features/translation/constants.js +7 -0
  102. package/dist/module/features/translation/constants.js.map +1 -0
  103. package/dist/module/features/translation/helpers.js +53 -0
  104. package/dist/module/features/translation/helpers.js.map +1 -0
  105. package/dist/module/features/translation/index.js +12 -0
  106. package/dist/module/features/translation/index.js.map +1 -0
  107. package/dist/module/features/translation/model/TranslationModel.js +117 -0
  108. package/dist/module/features/translation/model/TranslationModel.js.map +1 -0
  109. package/dist/module/features/translation/model/TranslationPreprocessor.js +137 -0
  110. package/dist/module/features/translation/model/TranslationPreprocessor.js.map +1 -0
  111. package/dist/module/features/translation/model/TranslationService.js +107 -0
  112. package/dist/module/features/translation/model/TranslationService.js.map +1 -0
  113. package/dist/module/features/translation/preserve/wordsToPreserve.js +310 -0
  114. package/dist/module/features/translation/preserve/wordsToPreserve.js.map +1 -0
  115. package/dist/module/features/translation/providers/google/GoogleTranslationProvider.js +45 -0
  116. package/dist/module/features/translation/providers/google/GoogleTranslationProvider.js.map +1 -0
  117. package/dist/module/features/translation/providers/google/supportedLanguages.js +225 -0
  118. package/dist/module/features/translation/providers/google/supportedLanguages.js.map +1 -0
  119. package/dist/module/features/translation/providers/google.types.js +2 -0
  120. package/dist/module/features/translation/providers/google.types.js.map +1 -0
  121. package/dist/module/features/translation/providers/translation.interface.js +44 -0
  122. package/dist/module/features/translation/providers/translation.interface.js.map +1 -0
  123. package/dist/module/features/translation/types.js +2 -0
  124. package/dist/module/features/translation/types.js.map +1 -0
  125. package/dist/module/features/voice/index.js +2 -1
  126. package/dist/module/features/voice/index.js.map +1 -1
  127. package/dist/module/features/voice/playing/components/VoiceIcon.js +2 -2
  128. package/dist/module/features/voice/playing/components/VoiceIcon.js.map +1 -1
  129. package/dist/module/features/voice/playing/components/VoiceTranscriptContent.js +38 -0
  130. package/dist/module/features/voice/playing/components/VoiceTranscriptContent.js.map +1 -0
  131. package/dist/module/features/voice/playing/components/VoiceTranscription.js +101 -0
  132. package/dist/module/features/voice/playing/components/VoiceTranscription.js.map +1 -0
  133. package/dist/module/features/voice/playing/components/VoiceTranscriptionCollapsible.js +58 -0
  134. package/dist/module/features/voice/playing/components/VoiceTranscriptionCollapsible.js.map +1 -0
  135. package/dist/module/features/voice/playing/components/VoiceTranscriptionHeaderActions.js +52 -0
  136. package/dist/module/features/voice/playing/components/VoiceTranscriptionHeaderActions.js.map +1 -0
  137. package/dist/module/features/voice/playing/hooks/{useVoiceTranscriptionsDropdown.js → useDropdownAnimation.js} +6 -5
  138. package/dist/module/features/voice/playing/hooks/useDropdownAnimation.js.map +1 -0
  139. package/dist/module/features/voice/playing/hooks/useVoicePlayerLifecycle.js +48 -0
  140. package/dist/module/features/voice/playing/hooks/useVoicePlayerLifecycle.js.map +1 -0
  141. package/dist/module/features/voice/playing/hooks/useVoiceTranscriptionController.js +106 -0
  142. package/dist/module/features/voice/playing/hooks/useVoiceTranscriptionController.js.map +1 -0
  143. package/dist/module/features/voice/playing/model/{VoiceTranscriptionsDropdown.model.js → Dropdown.model.js} +2 -2
  144. package/dist/module/features/voice/playing/model/Dropdown.model.js.map +1 -0
  145. package/dist/module/features/voice/playing/model/PlaybackController.js +187 -0
  146. package/dist/module/features/voice/playing/model/PlaybackController.js.map +1 -0
  147. package/dist/module/features/voice/playing/model/Player.model.js +51 -4
  148. package/dist/module/features/voice/playing/model/Player.model.js.map +1 -1
  149. package/dist/module/features/voice/playing/model/{VoiceTranscriptionsCollection.js → TranscriptionsCollection.js} +2 -2
  150. package/dist/module/features/voice/playing/model/TranscriptionsCollection.js.map +1 -0
  151. package/dist/module/features/voice/playing/model/TranscriptionsDownloader.model.js +78 -0
  152. package/dist/module/features/voice/playing/model/TranscriptionsDownloader.model.js.map +1 -0
  153. package/dist/module/features/voice/playing/model/Translation.model.js +57 -0
  154. package/dist/module/features/voice/playing/model/Translation.model.js.map +1 -0
  155. package/dist/module/features/voice/playing/model/VoiceFileDownloader.model.js +2 -2
  156. package/dist/module/features/voice/playing/model/VoiceFileDownloader.model.js.map +1 -1
  157. package/dist/module/features/voice/playing/model/VoicePlayer.model.js +60 -174
  158. package/dist/module/features/voice/playing/model/VoicePlayer.model.js.map +1 -1
  159. package/dist/module/features/voice/recording/components/VoiceRecordDivider.js +2 -2
  160. package/dist/module/features/voice/recording/components/VoiceRecordDivider.js.map +1 -1
  161. package/dist/module/features/voice/types.js +11 -0
  162. package/dist/module/features/voice/types.js.map +1 -1
  163. package/dist/module/index.js +1 -0
  164. package/dist/module/index.js.map +1 -1
  165. package/dist/typescript/commonjs/features/endOfAssignment/components/CorrectAnswer/CorrectAnswer.d.ts.map +1 -1
  166. package/dist/typescript/commonjs/features/fluency/components/Fluency.d.ts.map +1 -1
  167. package/dist/typescript/commonjs/features/fluency/components/select-math-operator/SelectMathOperator.d.ts.map +1 -1
  168. package/dist/typescript/commonjs/features/fluency/model/FluencyModel.d.ts +0 -3
  169. package/dist/typescript/commonjs/features/fluency/model/FluencyModel.d.ts.map +1 -1
  170. package/dist/typescript/commonjs/features/fluency/shared/fluency.constants.d.ts +0 -4
  171. package/dist/typescript/commonjs/features/fluency/shared/fluency.constants.d.ts.map +1 -1
  172. package/dist/typescript/commonjs/features/fluency/shared/fluency.helpers.d.ts +0 -1
  173. package/dist/typescript/commonjs/features/fluency/shared/fluency.helpers.d.ts.map +1 -1
  174. package/dist/typescript/commonjs/features/pmProgress/model/PmProgressModel.d.ts.map +1 -1
  175. package/dist/typescript/commonjs/features/pmProgress/model/PmRecommendationsModel.d.ts +38 -9
  176. package/dist/typescript/commonjs/features/pmProgress/model/PmRecommendationsModel.d.ts.map +1 -1
  177. package/dist/typescript/commonjs/features/pmProgress/shared/pmProgress.helpers.d.ts +0 -1
  178. package/dist/typescript/commonjs/features/pmProgress/shared/pmProgress.helpers.d.ts.map +1 -1
  179. package/dist/typescript/commonjs/features/translation/constants.d.ts +5 -0
  180. package/dist/typescript/commonjs/features/translation/constants.d.ts.map +1 -0
  181. package/dist/typescript/commonjs/features/translation/helpers.d.ts +9 -0
  182. package/dist/typescript/commonjs/features/translation/helpers.d.ts.map +1 -0
  183. package/dist/typescript/commonjs/features/translation/index.d.ts +12 -0
  184. package/dist/typescript/commonjs/features/translation/index.d.ts.map +1 -0
  185. package/dist/typescript/commonjs/features/translation/model/TranslationModel.d.ts +33 -0
  186. package/dist/typescript/commonjs/features/translation/model/TranslationModel.d.ts.map +1 -0
  187. package/dist/typescript/commonjs/features/translation/model/TranslationPreprocessor.d.ts +34 -0
  188. package/dist/typescript/commonjs/features/translation/model/TranslationPreprocessor.d.ts.map +1 -0
  189. package/dist/typescript/commonjs/features/translation/model/TranslationService.d.ts +40 -0
  190. package/dist/typescript/commonjs/features/translation/model/TranslationService.d.ts.map +1 -0
  191. package/dist/typescript/commonjs/features/translation/preserve/wordsToPreserve.d.ts +3 -0
  192. package/dist/typescript/commonjs/features/translation/preserve/wordsToPreserve.d.ts.map +1 -0
  193. package/dist/typescript/commonjs/features/translation/providers/google/GoogleTranslationProvider.d.ts +17 -0
  194. package/dist/typescript/commonjs/features/translation/providers/google/GoogleTranslationProvider.d.ts.map +1 -0
  195. package/dist/typescript/commonjs/features/translation/providers/google/supportedLanguages.d.ts +209 -0
  196. package/dist/typescript/commonjs/features/translation/providers/google/supportedLanguages.d.ts.map +1 -0
  197. package/dist/typescript/commonjs/features/translation/providers/google.types.d.ts +15 -0
  198. package/dist/typescript/commonjs/features/translation/providers/google.types.d.ts.map +1 -0
  199. package/dist/typescript/commonjs/features/translation/providers/translation.interface.d.ts +30 -0
  200. package/dist/typescript/commonjs/features/translation/providers/translation.interface.d.ts.map +1 -0
  201. package/dist/typescript/commonjs/features/translation/types.d.ts +27 -0
  202. package/dist/typescript/commonjs/features/translation/types.d.ts.map +1 -0
  203. package/dist/typescript/commonjs/features/voice/index.d.ts +3 -1
  204. package/dist/typescript/commonjs/features/voice/index.d.ts.map +1 -1
  205. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptContent.d.ts +8 -0
  206. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptContent.d.ts.map +1 -0
  207. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscription.d.ts +16 -0
  208. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscription.d.ts.map +1 -0
  209. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionCollapsible.d.ts +10 -0
  210. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionCollapsible.d.ts.map +1 -0
  211. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionHeaderActions.d.ts +12 -0
  212. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionHeaderActions.d.ts.map +1 -0
  213. package/dist/typescript/commonjs/features/voice/playing/hooks/{useVoiceTranscriptionsDropdown.d.ts → useDropdownAnimation.d.ts} +3 -3
  214. package/dist/typescript/commonjs/features/voice/playing/hooks/useDropdownAnimation.d.ts.map +1 -0
  215. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoicePlayerLifecycle.d.ts +15 -0
  216. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoicePlayerLifecycle.d.ts.map +1 -0
  217. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoiceTranscriptionController.d.ts +30 -0
  218. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoiceTranscriptionController.d.ts.map +1 -0
  219. package/dist/typescript/{module/features/voice/playing/model/VoiceTranscriptionsDropdown.model.d.ts → commonjs/features/voice/playing/model/Dropdown.model.d.ts} +2 -2
  220. package/dist/typescript/commonjs/features/voice/playing/model/Dropdown.model.d.ts.map +1 -0
  221. package/dist/typescript/commonjs/features/voice/playing/model/PlaybackController.d.ts +28 -0
  222. package/dist/typescript/commonjs/features/voice/playing/model/PlaybackController.d.ts.map +1 -0
  223. package/dist/typescript/commonjs/features/voice/playing/model/Player.model.d.ts +4 -0
  224. package/dist/typescript/commonjs/features/voice/playing/model/Player.model.d.ts.map +1 -1
  225. package/dist/typescript/{module/features/voice/playing/model/VoiceTranscriptionsCollection.d.ts → commonjs/features/voice/playing/model/TranscriptionsCollection.d.ts} +2 -2
  226. package/dist/typescript/commonjs/features/voice/playing/model/TranscriptionsCollection.d.ts.map +1 -0
  227. package/dist/typescript/commonjs/features/voice/playing/model/TranscriptionsDownloader.model.d.ts +19 -0
  228. package/dist/typescript/commonjs/features/voice/playing/model/TranscriptionsDownloader.model.d.ts.map +1 -0
  229. package/dist/typescript/commonjs/features/voice/playing/model/Translation.model.d.ts +25 -0
  230. package/dist/typescript/commonjs/features/voice/playing/model/Translation.model.d.ts.map +1 -0
  231. package/dist/typescript/commonjs/features/voice/playing/model/VoiceFileDownloader.model.d.ts +2 -2
  232. package/dist/typescript/commonjs/features/voice/playing/model/VoiceFileDownloader.model.d.ts.map +1 -1
  233. package/dist/typescript/commonjs/features/voice/playing/model/VoicePlayer.model.d.ts +19 -18
  234. package/dist/typescript/commonjs/features/voice/playing/model/VoicePlayer.model.d.ts.map +1 -1
  235. package/dist/typescript/commonjs/features/voice/types.d.ts +14 -2
  236. package/dist/typescript/commonjs/features/voice/types.d.ts.map +1 -1
  237. package/dist/typescript/commonjs/index.d.ts +1 -0
  238. package/dist/typescript/commonjs/index.d.ts.map +1 -1
  239. package/dist/typescript/module/features/endOfAssignment/components/CorrectAnswer/CorrectAnswer.d.ts.map +1 -1
  240. package/dist/typescript/module/features/fluency/components/Fluency.d.ts.map +1 -1
  241. package/dist/typescript/module/features/fluency/components/select-math-operator/SelectMathOperator.d.ts.map +1 -1
  242. package/dist/typescript/module/features/fluency/model/FluencyModel.d.ts +0 -3
  243. package/dist/typescript/module/features/fluency/model/FluencyModel.d.ts.map +1 -1
  244. package/dist/typescript/module/features/fluency/shared/fluency.constants.d.ts +0 -4
  245. package/dist/typescript/module/features/fluency/shared/fluency.constants.d.ts.map +1 -1
  246. package/dist/typescript/module/features/fluency/shared/fluency.helpers.d.ts +0 -1
  247. package/dist/typescript/module/features/fluency/shared/fluency.helpers.d.ts.map +1 -1
  248. package/dist/typescript/module/features/pmProgress/model/PmProgressModel.d.ts.map +1 -1
  249. package/dist/typescript/module/features/pmProgress/model/PmRecommendationsModel.d.ts +38 -9
  250. package/dist/typescript/module/features/pmProgress/model/PmRecommendationsModel.d.ts.map +1 -1
  251. package/dist/typescript/module/features/pmProgress/shared/pmProgress.helpers.d.ts +0 -1
  252. package/dist/typescript/module/features/pmProgress/shared/pmProgress.helpers.d.ts.map +1 -1
  253. package/dist/typescript/module/features/translation/constants.d.ts +5 -0
  254. package/dist/typescript/module/features/translation/constants.d.ts.map +1 -0
  255. package/dist/typescript/module/features/translation/helpers.d.ts +9 -0
  256. package/dist/typescript/module/features/translation/helpers.d.ts.map +1 -0
  257. package/dist/typescript/module/features/translation/index.d.ts +12 -0
  258. package/dist/typescript/module/features/translation/index.d.ts.map +1 -0
  259. package/dist/typescript/module/features/translation/model/TranslationModel.d.ts +33 -0
  260. package/dist/typescript/module/features/translation/model/TranslationModel.d.ts.map +1 -0
  261. package/dist/typescript/module/features/translation/model/TranslationPreprocessor.d.ts +34 -0
  262. package/dist/typescript/module/features/translation/model/TranslationPreprocessor.d.ts.map +1 -0
  263. package/dist/typescript/module/features/translation/model/TranslationService.d.ts +40 -0
  264. package/dist/typescript/module/features/translation/model/TranslationService.d.ts.map +1 -0
  265. package/dist/typescript/module/features/translation/preserve/wordsToPreserve.d.ts +3 -0
  266. package/dist/typescript/module/features/translation/preserve/wordsToPreserve.d.ts.map +1 -0
  267. package/dist/typescript/module/features/translation/providers/google/GoogleTranslationProvider.d.ts +17 -0
  268. package/dist/typescript/module/features/translation/providers/google/GoogleTranslationProvider.d.ts.map +1 -0
  269. package/dist/typescript/module/features/translation/providers/google/supportedLanguages.d.ts +209 -0
  270. package/dist/typescript/module/features/translation/providers/google/supportedLanguages.d.ts.map +1 -0
  271. package/dist/typescript/module/features/translation/providers/google.types.d.ts +15 -0
  272. package/dist/typescript/module/features/translation/providers/google.types.d.ts.map +1 -0
  273. package/dist/typescript/module/features/translation/providers/translation.interface.d.ts +30 -0
  274. package/dist/typescript/module/features/translation/providers/translation.interface.d.ts.map +1 -0
  275. package/dist/typescript/module/features/translation/types.d.ts +27 -0
  276. package/dist/typescript/module/features/translation/types.d.ts.map +1 -0
  277. package/dist/typescript/module/features/voice/index.d.ts +3 -1
  278. package/dist/typescript/module/features/voice/index.d.ts.map +1 -1
  279. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptContent.d.ts +8 -0
  280. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptContent.d.ts.map +1 -0
  281. package/dist/typescript/module/features/voice/playing/components/VoiceTranscription.d.ts +16 -0
  282. package/dist/typescript/module/features/voice/playing/components/VoiceTranscription.d.ts.map +1 -0
  283. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionCollapsible.d.ts +10 -0
  284. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionCollapsible.d.ts.map +1 -0
  285. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionHeaderActions.d.ts +12 -0
  286. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionHeaderActions.d.ts.map +1 -0
  287. package/dist/typescript/module/features/voice/playing/hooks/{useVoiceTranscriptionsDropdown.d.ts → useDropdownAnimation.d.ts} +3 -3
  288. package/dist/typescript/module/features/voice/playing/hooks/useDropdownAnimation.d.ts.map +1 -0
  289. package/dist/typescript/module/features/voice/playing/hooks/useVoicePlayerLifecycle.d.ts +15 -0
  290. package/dist/typescript/module/features/voice/playing/hooks/useVoicePlayerLifecycle.d.ts.map +1 -0
  291. package/dist/typescript/module/features/voice/playing/hooks/useVoiceTranscriptionController.d.ts +30 -0
  292. package/dist/typescript/module/features/voice/playing/hooks/useVoiceTranscriptionController.d.ts.map +1 -0
  293. package/dist/typescript/{commonjs/features/voice/playing/model/VoiceTranscriptionsDropdown.model.d.ts → module/features/voice/playing/model/Dropdown.model.d.ts} +2 -2
  294. package/dist/typescript/module/features/voice/playing/model/Dropdown.model.d.ts.map +1 -0
  295. package/dist/typescript/module/features/voice/playing/model/PlaybackController.d.ts +28 -0
  296. package/dist/typescript/module/features/voice/playing/model/PlaybackController.d.ts.map +1 -0
  297. package/dist/typescript/module/features/voice/playing/model/Player.model.d.ts +4 -0
  298. package/dist/typescript/module/features/voice/playing/model/Player.model.d.ts.map +1 -1
  299. package/dist/typescript/{commonjs/features/voice/playing/model/VoiceTranscriptionsCollection.d.ts → module/features/voice/playing/model/TranscriptionsCollection.d.ts} +2 -2
  300. package/dist/typescript/module/features/voice/playing/model/TranscriptionsCollection.d.ts.map +1 -0
  301. package/dist/typescript/module/features/voice/playing/model/TranscriptionsDownloader.model.d.ts +19 -0
  302. package/dist/typescript/module/features/voice/playing/model/TranscriptionsDownloader.model.d.ts.map +1 -0
  303. package/dist/typescript/module/features/voice/playing/model/Translation.model.d.ts +25 -0
  304. package/dist/typescript/module/features/voice/playing/model/Translation.model.d.ts.map +1 -0
  305. package/dist/typescript/module/features/voice/playing/model/VoiceFileDownloader.model.d.ts +2 -2
  306. package/dist/typescript/module/features/voice/playing/model/VoiceFileDownloader.model.d.ts.map +1 -1
  307. package/dist/typescript/module/features/voice/playing/model/VoicePlayer.model.d.ts +19 -18
  308. package/dist/typescript/module/features/voice/playing/model/VoicePlayer.model.d.ts.map +1 -1
  309. package/dist/typescript/module/features/voice/types.d.ts +14 -2
  310. package/dist/typescript/module/features/voice/types.d.ts.map +1 -1
  311. package/dist/typescript/module/index.d.ts +1 -0
  312. package/dist/typescript/module/index.d.ts.map +1 -1
  313. package/package.json +37 -10
  314. package/src/features/endOfAssignment/components/CorrectAnswer/CorrectAnswer.tsx +2 -9
  315. package/src/features/fluency/components/Fluency.tsx +9 -17
  316. package/src/features/fluency/components/select-math-operator/SelectMathOperator.tsx +5 -13
  317. package/src/features/fluency/model/FluencyModel.ts +1 -25
  318. package/src/features/fluency/shared/fluency.constants.ts +0 -5
  319. package/src/features/fluency/shared/fluency.helpers.ts +0 -3
  320. package/src/features/pmProgress/model/PmProgressModel.ts +1 -4
  321. package/src/features/pmProgress/model/PmRecommendationsModel.ts +6 -22
  322. package/src/features/pmProgress/shared/pmProgress.helpers.ts +0 -33
  323. package/src/features/translation/constants.ts +4 -0
  324. package/src/features/translation/helpers.ts +63 -0
  325. package/src/features/translation/index.ts +30 -0
  326. package/src/features/translation/model/TranslationModel.ts +174 -0
  327. package/src/features/translation/model/TranslationPreprocessor.ts +171 -0
  328. package/src/features/translation/model/TranslationService.ts +148 -0
  329. package/src/features/translation/preserve/wordsToPreserve.ts +87 -0
  330. package/src/features/translation/providers/google/GoogleTranslationProvider.ts +50 -0
  331. package/src/features/translation/providers/google/supportedLanguages.ts +229 -0
  332. package/src/features/translation/providers/google.types.ts +16 -0
  333. package/src/features/translation/providers/translation.interface.ts +63 -0
  334. package/src/features/translation/types.ts +30 -0
  335. package/src/features/voice/index.ts +3 -1
  336. package/src/features/voice/playing/components/VoiceIcon.tsx +2 -2
  337. package/src/features/voice/playing/components/VoiceTranscriptContent.tsx +44 -0
  338. package/src/features/voice/playing/components/VoiceTranscription.tsx +117 -0
  339. package/src/features/voice/playing/components/VoiceTranscriptionCollapsible.tsx +59 -0
  340. package/src/features/voice/playing/components/VoiceTranscriptionHeaderActions.tsx +61 -0
  341. package/src/features/voice/playing/hooks/{useVoiceTranscriptionsDropdown.ts → useDropdownAnimation.ts} +5 -5
  342. package/src/features/voice/playing/hooks/useVoicePlayerLifecycle.ts +64 -0
  343. package/src/features/voice/playing/hooks/useVoiceTranscriptionController.ts +148 -0
  344. package/src/features/voice/playing/model/{VoiceTranscriptionsDropdown.model.ts → Dropdown.model.ts} +2 -2
  345. package/src/features/voice/playing/model/PlaybackController.ts +221 -0
  346. package/src/features/voice/playing/model/Player.model.ts +52 -4
  347. package/src/features/voice/playing/model/{VoiceTranscriptionsCollection.ts → TranscriptionsCollection.ts} +1 -1
  348. package/src/features/voice/playing/model/TranscriptionsDownloader.model.ts +88 -0
  349. package/src/features/voice/playing/model/Translation.model.ts +75 -0
  350. package/src/features/voice/playing/model/VoiceFileDownloader.model.ts +2 -2
  351. package/src/features/voice/playing/model/VoicePlayer.model.ts +68 -192
  352. package/src/features/voice/recording/components/VoiceRecordDivider.tsx +2 -2
  353. package/src/features/voice/types.ts +21 -1
  354. package/src/i18n/.generated/schema.json +3 -7
  355. package/src/index.ts +1 -0
  356. package/dist/commonjs/features/fluency/components/FluencyStatusOverlay.js +0 -101
  357. package/dist/commonjs/features/fluency/components/FluencyStatusOverlay.js.map +0 -1
  358. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdown.js +0 -245
  359. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdown.js.map +0 -1
  360. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdownItem.js +0 -109
  361. package/dist/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdownItem.js.map +0 -1
  362. package/dist/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.js.map +0 -1
  363. package/dist/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.js +0 -34
  364. package/dist/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.js.map +0 -1
  365. package/dist/commonjs/features/voice/playing/model/TranscriptionsDownloaderModel.js +0 -66
  366. package/dist/commonjs/features/voice/playing/model/TranscriptionsDownloaderModel.js.map +0 -1
  367. package/dist/commonjs/features/voice/playing/model/VoiceTranscriptionsCollection.js.map +0 -1
  368. package/dist/commonjs/features/voice/playing/model/VoiceTranscriptionsDropdown.model.js.map +0 -1
  369. package/dist/module/features/fluency/components/FluencyStatusOverlay.js +0 -95
  370. package/dist/module/features/fluency/components/FluencyStatusOverlay.js.map +0 -1
  371. package/dist/module/features/voice/playing/components/VoiceTranscriptionsDropdown.js +0 -238
  372. package/dist/module/features/voice/playing/components/VoiceTranscriptionsDropdown.js.map +0 -1
  373. package/dist/module/features/voice/playing/components/VoiceTranscriptionsDropdownItem.js +0 -103
  374. package/dist/module/features/voice/playing/components/VoiceTranscriptionsDropdownItem.js.map +0 -1
  375. package/dist/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.js.map +0 -1
  376. package/dist/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.js +0 -29
  377. package/dist/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.js.map +0 -1
  378. package/dist/module/features/voice/playing/model/TranscriptionsDownloaderModel.js +0 -61
  379. package/dist/module/features/voice/playing/model/TranscriptionsDownloaderModel.js.map +0 -1
  380. package/dist/module/features/voice/playing/model/VoiceTranscriptionsCollection.js.map +0 -1
  381. package/dist/module/features/voice/playing/model/VoiceTranscriptionsDropdown.model.js.map +0 -1
  382. package/dist/typescript/commonjs/features/fluency/components/FluencyStatusOverlay.d.ts +0 -8
  383. package/dist/typescript/commonjs/features/fluency/components/FluencyStatusOverlay.d.ts.map +0 -1
  384. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdown.d.ts +0 -16
  385. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdown.d.ts.map +0 -1
  386. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdownItem.d.ts +0 -12
  387. package/dist/typescript/commonjs/features/voice/playing/components/VoiceTranscriptionsDropdownItem.d.ts.map +0 -1
  388. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.d.ts.map +0 -1
  389. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.d.ts +0 -11
  390. package/dist/typescript/commonjs/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.d.ts.map +0 -1
  391. package/dist/typescript/commonjs/features/voice/playing/model/TranscriptionsDownloaderModel.d.ts +0 -15
  392. package/dist/typescript/commonjs/features/voice/playing/model/TranscriptionsDownloaderModel.d.ts.map +0 -1
  393. package/dist/typescript/commonjs/features/voice/playing/model/VoiceTranscriptionsCollection.d.ts.map +0 -1
  394. package/dist/typescript/commonjs/features/voice/playing/model/VoiceTranscriptionsDropdown.model.d.ts.map +0 -1
  395. package/dist/typescript/module/features/fluency/components/FluencyStatusOverlay.d.ts +0 -8
  396. package/dist/typescript/module/features/fluency/components/FluencyStatusOverlay.d.ts.map +0 -1
  397. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionsDropdown.d.ts +0 -16
  398. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionsDropdown.d.ts.map +0 -1
  399. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionsDropdownItem.d.ts +0 -12
  400. package/dist/typescript/module/features/voice/playing/components/VoiceTranscriptionsDropdownItem.d.ts.map +0 -1
  401. package/dist/typescript/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdown.d.ts.map +0 -1
  402. package/dist/typescript/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.d.ts +0 -11
  403. package/dist/typescript/module/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.d.ts.map +0 -1
  404. package/dist/typescript/module/features/voice/playing/model/TranscriptionsDownloaderModel.d.ts +0 -15
  405. package/dist/typescript/module/features/voice/playing/model/TranscriptionsDownloaderModel.d.ts.map +0 -1
  406. package/dist/typescript/module/features/voice/playing/model/VoiceTranscriptionsCollection.d.ts.map +0 -1
  407. package/dist/typescript/module/features/voice/playing/model/VoiceTranscriptionsDropdown.model.d.ts.map +0 -1
  408. package/src/features/fluency/components/FluencyStatusOverlay.tsx +0 -101
  409. package/src/features/voice/playing/components/VoiceTranscriptionsDropdown.tsx +0 -296
  410. package/src/features/voice/playing/components/VoiceTranscriptionsDropdownItem.tsx +0 -128
  411. package/src/features/voice/playing/hooks/useVoiceTranscriptionsDropdownItemAnimation.ts +0 -40
  412. package/src/features/voice/playing/model/TranscriptionsDownloaderModel.ts +0 -80
@@ -0,0 +1,221 @@
1
+ import { createEffect } from 'effector'
2
+ import { PlayerModel, PlayerState } from './Player.model'
3
+ import { TranscriptionsCollection } from './TranscriptionsCollection'
4
+ import { VoiceFileDownloaderModel } from './VoiceFileDownloader.model'
5
+
6
+ type PlaybackControllerDeps = {
7
+ collection: TranscriptionsCollection
8
+ player: PlayerModel
9
+ downloader: VoiceFileDownloaderModel
10
+ }
11
+
12
+ export class PlaybackController {
13
+ private readonly collection: TranscriptionsCollection
14
+ private readonly player: PlayerModel
15
+ private readonly downloader: VoiceFileDownloaderModel
16
+ private currentPlayingAttempt: number | null = null
17
+ private shouldPreventPlayback = false
18
+
19
+ constructor(deps: PlaybackControllerDeps) {
20
+ this.collection = deps.collection
21
+ this.player = deps.player
22
+ this.downloader = deps.downloader
23
+ this.player.playbackFinished.watch(() => this.clearCurrentAttempt())
24
+ }
25
+
26
+ public readonly togglePlayPause = (attemptNumber: number) => {
27
+ const playerState = this.player.$playerState.getState()
28
+ if (this.currentPlayingAttempt === attemptNumber && playerState === PlayerState.PLAYING) {
29
+ this.player.pause()
30
+ return
31
+ }
32
+ this.handlePlayAttempt(attemptNumber)
33
+ }
34
+
35
+ public readonly preloadAudio = createEffect(async (attemptNumber: number) => {
36
+ const item = this.collection.get(attemptNumber)
37
+ if (!item) return
38
+ if (this.collection.hasAudio(attemptNumber)) {
39
+ this.primePlayerIfIdle(attemptNumber)
40
+ return
41
+ }
42
+ if (item.audioDownloadPromise) {
43
+ try {
44
+ await item.audioDownloadPromise
45
+ } catch {}
46
+ this.primePlayerIfIdle(attemptNumber)
47
+ return
48
+ }
49
+ try {
50
+ await this.downloadAudio(attemptNumber)
51
+ this.primePlayerIfIdle(attemptNumber)
52
+ } catch (error) {
53
+ if (error instanceof Error && error.name === 'AbortError') return
54
+ console.error('Audio preload error:', error)
55
+ }
56
+ })
57
+
58
+ private primePlayerIfIdle(attemptNumber: number) {
59
+ if (this.player.$playerState.getState() !== PlayerState.IDLE) return
60
+ if (this.currentPlayingAttempt !== null) return
61
+ const audioUri = this.collection.get(attemptNumber)?.audioUri
62
+ if (!audioUri) return
63
+ this.player.prime(audioUri)
64
+ }
65
+
66
+ // Non-destructive halt: pause + IDLE, keep audio loaded so a subsequent
67
+ // play() can resume the same uri without re-fetch. Previously this called
68
+ // player.stop() which did replace('') and set stopped=true — that corrupted
69
+ // the AudioPlayer (isLoaded stuck false) and aborted any in-flight play()'s
70
+ // waitForLoad, when the lifecycle hook re-fired stopAudio mid-play.
71
+ public halt() {
72
+ this.shouldPreventPlayback = true
73
+ this.abortAllDownloads()
74
+ if (this.player.player && this.player.$playerState.getState() !== PlayerState.IDLE) {
75
+ this.player.player.pause()
76
+ this.player.setPlayerState(PlayerState.IDLE)
77
+ }
78
+ this.clearCurrentAttempt()
79
+ }
80
+
81
+ public releaseResources() {
82
+ this.collection.getAll().forEach((item) => {
83
+ if (item.controller) {
84
+ this.downloader.abortDownload(item.controller)
85
+ }
86
+ if (item.audioUri?.startsWith('blob:')) {
87
+ try {
88
+ URL.revokeObjectURL(item.audioUri)
89
+ } catch (e) {}
90
+ }
91
+ })
92
+ }
93
+
94
+ private clearCurrentAttempt() {
95
+ this.currentPlayingAttempt = null
96
+ }
97
+
98
+ private abortAllDownloads() {
99
+ this.collection.getAll().forEach((item) => {
100
+ if (item.controller && item.audioLoading) {
101
+ this.downloader.abortDownload(item.controller)
102
+ this.collection.update(item.attemptNumber, {
103
+ audioLoading: false,
104
+ controller: undefined,
105
+ audioDownloadPromise: undefined,
106
+ })
107
+ }
108
+ })
109
+ }
110
+
111
+ private async downloadAudio(attemptNumber: number): Promise<void> {
112
+ const item = this.collection.get(attemptNumber)
113
+ if (!item) return
114
+
115
+ const { collectionItem, downloadPromise } = await this.downloader.download({
116
+ audioFileId: item.audioFileId,
117
+ attemptNumber,
118
+ })
119
+ this.collection.update(attemptNumber, collectionItem)
120
+
121
+ try {
122
+ const result = await downloadPromise
123
+ this.collection.update(attemptNumber, {
124
+ audioUri: result.uri,
125
+ audioLoading: false,
126
+ audioError: undefined,
127
+ controller: undefined,
128
+ audioDownloadPromise: undefined,
129
+ })
130
+ } catch (error) {
131
+ const isAbort = error instanceof Error && error.name === 'AbortError'
132
+ this.collection.update(attemptNumber, {
133
+ audioLoading: false,
134
+ audioError: isAbort
135
+ ? undefined
136
+ : error instanceof Error
137
+ ? error.message
138
+ : 'Failed to download audio',
139
+ controller: undefined,
140
+ audioDownloadPromise: undefined,
141
+ })
142
+ throw error
143
+ }
144
+ }
145
+
146
+ private readonly handlePlayAttempt = createEffect(async (attemptNumber: number) => {
147
+ this.shouldPreventPlayback = false
148
+ const item = this.collection.get(attemptNumber)
149
+ if (!item) return
150
+
151
+ if (this.currentPlayingAttempt !== null && this.currentPlayingAttempt !== attemptNumber) {
152
+ this.abortAllDownloads()
153
+ }
154
+
155
+ if (this.collection.hasAudio(attemptNumber)) {
156
+ this.playAudioFx(attemptNumber)
157
+ return
158
+ }
159
+
160
+ try {
161
+ this.player.setPlayerState(PlayerState.LOADING)
162
+
163
+ if (item.audioDownloadPromise) {
164
+ await item.audioDownloadPromise
165
+ } else {
166
+ await this.downloadAudio(attemptNumber)
167
+ }
168
+
169
+ if (this.shouldPreventPlayback) {
170
+ this.player.setPlayerState(PlayerState.IDLE)
171
+ return
172
+ }
173
+
174
+ this.player.setPlayerState(PlayerState.IDLE)
175
+ this.playAudioFx(attemptNumber)
176
+ } catch (error) {
177
+ if (error instanceof Error && error.name === 'AbortError') {
178
+ this.player.setPlayerState(PlayerState.IDLE)
179
+ return
180
+ }
181
+ console.error('Audio download error:', error)
182
+ this.player.setPlayerState(PlayerState.IDLE)
183
+ }
184
+ })
185
+
186
+ private readonly playAudioFx = createEffect(async (attemptNumber: number) => {
187
+ if (this.shouldPreventPlayback) return
188
+
189
+ const item = this.collection.get(attemptNumber)
190
+ if (!item?.audioUri) {
191
+ console.warn('No audio URI available for attempt:', attemptNumber)
192
+ return
193
+ }
194
+
195
+ try {
196
+ if (this.currentPlayingAttempt !== null && this.currentPlayingAttempt !== attemptNumber) {
197
+ this.player.player?.remove()
198
+ }
199
+
200
+ if (this.currentPlayingAttempt === attemptNumber && this.player.player?.playing) {
201
+ this.player.pause()
202
+ return
203
+ }
204
+
205
+ if (this.currentPlayingAttempt === attemptNumber && !this.player.player?.playing) {
206
+ this.player.resume()
207
+ return
208
+ }
209
+
210
+ this.currentPlayingAttempt = attemptNumber
211
+ await this.player.play(item.audioUri)
212
+ } catch (error) {
213
+ console.error('Error playing audio:', error)
214
+ this.player.setPlayerState(PlayerState.IDLE)
215
+
216
+ this.collection.update(attemptNumber, {
217
+ audioError: error instanceof Error ? error.message : 'Failed to play audio',
218
+ })
219
+ }
220
+ })
221
+ }
@@ -8,9 +8,23 @@ export const enum PlayerState {
8
8
  IDLE = 'idle',
9
9
  }
10
10
 
11
+ // audio.play() returns a promise that rejects with AbortError when a subsequent
12
+ // pause() interrupts it. That's expected during pause/resume cycles; suppress noise.
13
+ const safePlay = (audio: AudioPlayer) => {
14
+ const result = audio.play() as unknown
15
+ if (result && typeof (result as Promise<unknown>).catch === 'function') {
16
+ ;(result as Promise<unknown>).catch((err: unknown) => {
17
+ if ((err as { name?: string })?.name === 'AbortError') return
18
+ throw err
19
+ })
20
+ }
21
+ }
22
+
11
23
  export class PlayerModel {
12
24
  public player: AudioPlayer | null = createAudioPlayer()
13
25
  private stopped = false
26
+ private primedUri: string | null = null
27
+ private pendingSeek: number | null = null
14
28
 
15
29
  public readonly reset = createEvent()
16
30
  public readonly setPlayerState = createEvent<PlayerState>()
@@ -39,15 +53,36 @@ export class PlayerModel {
39
53
  this.stopped = true
40
54
  if (this.player) {
41
55
  this.player.replace('')
56
+ this.primedUri = null
57
+ this.pendingSeek = null
42
58
  this.setPlayerState(PlayerState.IDLE)
43
59
  } else {
44
60
  console.warn('Cannot stop: audio player not initialized')
45
61
  }
46
62
  })
47
63
 
64
+ public readonly prime = createEffect((uri: string) => {
65
+ if (!this.player) return
66
+ if (this.primedUri === uri) return
67
+ this.primedUri = uri
68
+ this.player.replace(uri)
69
+ })
70
+
71
+ public readonly seek = createEffect(async (seconds: number) => {
72
+ // When not loaded, defer the seek so play()'s waitForLoad can apply it
73
+ // before starting playback — otherwise the requested offset is lost and
74
+ // audio starts from 0 after a scrub-across-attempt that halted the player.
75
+ if (!this.player?.isLoaded) {
76
+ this.pendingSeek = seconds
77
+ return
78
+ }
79
+ this.pendingSeek = null
80
+ await this.player.seekTo(seconds)
81
+ })
82
+
48
83
  public readonly resume = createEffect(() => {
49
84
  if (this.player) {
50
- this.player.play()
85
+ safePlay(this.player)
51
86
  this.setPlayerState(PlayerState.PLAYING)
52
87
  } else {
53
88
  console.warn('Cannot resume: audio player not initialized')
@@ -61,11 +96,22 @@ export class PlayerModel {
61
96
  }
62
97
 
63
98
  this.stopped = false
64
- this.player.replace(uri)
99
+ if (this.primedUri !== uri || !this.player.isLoaded) {
100
+ this.player.replace(uri)
101
+ this.primedUri = uri
102
+ }
65
103
 
66
- const waitForLoad = () => {
104
+ const waitForLoad = async () => {
67
105
  if (this.stopped) return
68
106
  if (this.player && this.player.isLoaded) {
107
+ if (this.pendingSeek !== null) {
108
+ const target = this.pendingSeek
109
+ this.pendingSeek = null
110
+ try {
111
+ await this.player.seekTo(target)
112
+ } catch {}
113
+ if (this.stopped) return
114
+ }
69
115
  const unsubscribe = this.player.addListener('playbackStatusUpdate', (status) => {
70
116
  if (status.didJustFinish) {
71
117
  this.playbackFinished()
@@ -74,7 +120,7 @@ export class PlayerModel {
74
120
  }
75
121
  })
76
122
 
77
- this.player.play()
123
+ safePlay(this.player)
78
124
  this.setPlayerState(PlayerState.PLAYING)
79
125
  } else {
80
126
  setTimeout(waitForLoad, 10)
@@ -99,6 +145,8 @@ export class PlayerModel {
99
145
  this.player.release()
100
146
  this.player = null
101
147
  }
148
+ this.primedUri = null
149
+ this.pendingSeek = null
102
150
  this.reset()
103
151
  }
104
152
  }
@@ -1,6 +1,6 @@
1
1
  import { VoiceTranscriptionItem } from '../../types'
2
2
 
3
- export class VoiceTranscriptionsCollection {
3
+ export class TranscriptionsCollection {
4
4
  public readonly collection: Map<number, VoiceTranscriptionItem> = new Map()
5
5
 
6
6
  public add(attemptNumber: number, item: VoiceTranscriptionItem) {
@@ -0,0 +1,88 @@
1
+ import { createEffect } from 'effector'
2
+ import { TranscriptionsCollection } from './TranscriptionsCollection'
3
+ import { TranscriptionStatus, VoicePlayerApi } from '../../types'
4
+ import {
5
+ NO_AUDIO_BE_MESSAGE,
6
+ TRANSCRIPT_MAX_RETRIES,
7
+ TRANSCRIPT_RETRY_INTERVAL_MS,
8
+ } from '../../constants'
9
+
10
+ type TranscriptResult = { text: string; language?: string }
11
+
12
+ export class TranscriptionsDownloaderModel {
13
+ private readonly collection: TranscriptionsCollection
14
+ private readonly api: VoicePlayerApi
15
+ private readonly inflightFetches = new Map<string, Promise<TranscriptResult>>()
16
+ private readonly transcriptCache = new Map<string, TranscriptResult>()
17
+
18
+ constructor(collection: TranscriptionsCollection, api: VoicePlayerApi) {
19
+ this.collection = collection
20
+ this.api = api
21
+ }
22
+
23
+ private async fetchTranscriptWithRetry(audioFileId: string): Promise<TranscriptResult> {
24
+ const cached = this.transcriptCache.get(audioFileId)
25
+ if (cached) return cached
26
+
27
+ const existing = this.inflightFetches.get(audioFileId)
28
+ if (existing) return existing
29
+
30
+ const promise = this.runFetchWithRetry(audioFileId)
31
+ .then((result) => {
32
+ this.transcriptCache.set(audioFileId, result)
33
+ return result
34
+ })
35
+ .finally(() => {
36
+ this.inflightFetches.delete(audioFileId)
37
+ })
38
+ this.inflightFetches.set(audioFileId, promise)
39
+ return promise
40
+ }
41
+
42
+ private async runFetchWithRetry(audioFileId: string): Promise<TranscriptResult> {
43
+ for (let attempt = 0; attempt < TRANSCRIPT_MAX_RETRIES; attempt++) {
44
+ const response = await this.api.getAudioFileTranscript(audioFileId)
45
+
46
+ if (response.status === TranscriptionStatus.COMPLETED) {
47
+ return { text: response.text, language: response.language }
48
+ }
49
+
50
+ if (response.status === TranscriptionStatus.FAILED) {
51
+ return { text: NO_AUDIO_BE_MESSAGE }
52
+ }
53
+
54
+ if (attempt < TRANSCRIPT_MAX_RETRIES - 1) {
55
+ await new Promise((resolve) => setTimeout(resolve, TRANSCRIPT_RETRY_INTERVAL_MS))
56
+ }
57
+ }
58
+ return { text: NO_AUDIO_BE_MESSAGE }
59
+ }
60
+
61
+ public readonly loadTranscriptForAttempt = createEffect(async (attemptNumber: number) => {
62
+ const item = this.collection.get(attemptNumber)
63
+ if (!item?.audioFileId) return
64
+ if (item.transcript || item.transcriptLoading) return
65
+
66
+ this.collection.update(attemptNumber, { transcriptLoading: true })
67
+
68
+ try {
69
+ const { text, language } = await this.fetchTranscriptWithRetry(item.audioFileId)
70
+
71
+ this.collection.update(attemptNumber, {
72
+ transcript: text,
73
+ transcriptLanguage: language,
74
+ transcriptLoading: false,
75
+ transcriptError: undefined,
76
+ })
77
+ } catch (error) {
78
+ this.collection.update(attemptNumber, {
79
+ transcriptLoading: false,
80
+ transcriptError: error instanceof Error ? error.message : 'Failed to load transcript',
81
+ })
82
+ }
83
+ })
84
+
85
+ public getCachedTranscript(audioFileId: string): TranscriptResult | undefined {
86
+ return this.transcriptCache.get(audioFileId)
87
+ }
88
+ }
@@ -0,0 +1,75 @@
1
+ import { createEffect, createEvent, createStore, restore } from 'effector'
2
+ import { TranscriptionsCollection } from './TranscriptionsCollection'
3
+ import { VoicePlayerApi } from '../../types'
4
+ import { TranslationService } from '../../../translation/model/TranslationService'
5
+ import { GoogleTranslationProvider } from '../../../translation/providers/google/GoogleTranslationProvider'
6
+ import {
7
+ preservedWordsReplacers,
8
+ wordsToPreserve,
9
+ } from '../../../translation/preserve/wordsToPreserve'
10
+
11
+ type TranslateParams = {
12
+ attemptNumber: number
13
+ targetLanguage: string
14
+ }
15
+
16
+ type TranslateResult = {
17
+ attemptNumber: number
18
+ text: string
19
+ }
20
+
21
+ export class TranslationModel {
22
+ private readonly collection: TranscriptionsCollection
23
+ private readonly service: TranslationService
24
+
25
+ public readonly toggle = createEvent()
26
+ public readonly setActive = createEvent<boolean>()
27
+ public readonly clearCache = createEvent()
28
+ public readonly reset = createEvent()
29
+
30
+ public readonly $isActive = restore(this.setActive, false)
31
+ .on(this.toggle, (state) => !state)
32
+ .reset(this.reset)
33
+
34
+ public readonly translate = createEffect<TranslateParams, TranslateResult | null>(
35
+ async ({ attemptNumber, targetLanguage }) => {
36
+ const item = this.collection.get(attemptNumber)
37
+ if (!item?.transcript) return null
38
+
39
+ const text = await this.service.translate(item.transcript, {
40
+ targetLang: targetLanguage,
41
+ sourceLang: item.transcriptLanguage,
42
+ cacheKey: `voice::${attemptNumber}::${targetLanguage}`,
43
+ })
44
+
45
+ if (!text) return null
46
+
47
+ return { attemptNumber, text }
48
+ },
49
+ )
50
+
51
+ public readonly $translations = createStore<Record<number, string>>({})
52
+ .on(this.translate.doneData, (state, result) => {
53
+ if (!result) return state
54
+ return { ...state, [result.attemptNumber]: result.text }
55
+ })
56
+ .reset(this.reset, this.clearCache)
57
+
58
+ constructor(collection: TranscriptionsCollection, api: VoicePlayerApi) {
59
+ this.collection = collection
60
+ this.service = new TranslationService({
61
+ preserve: {
62
+ map: wordsToPreserve,
63
+ replacers: preservedWordsReplacers,
64
+ },
65
+ providers: [new GoogleTranslationProvider({ api: { translateRequest: api.translateText } })],
66
+ })
67
+
68
+ this.clearCache.watch(() => this.service.reset())
69
+ this.reset.watch(() => this.service.reset())
70
+ }
71
+
72
+ public getTranslation(attemptNumber: number): string | undefined {
73
+ return this.$translations.getState()[attemptNumber]
74
+ }
75
+ }
@@ -13,9 +13,9 @@ type DownloadResult = {
13
13
  }
14
14
 
15
15
  export class VoiceFileDownloaderModel {
16
- private api!: VoicePlayerApi
16
+ private readonly api: VoicePlayerApi
17
17
 
18
- public readonly setApi = (api: VoicePlayerApi) => {
18
+ constructor(api: VoicePlayerApi) {
19
19
  this.api = api
20
20
  }
21
21