@magmamath/students-features 0.9.113 → 0.9.114-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/features/pmProgress/PracticeProgress.js +264 -0
- package/dist/commonjs/features/pmProgress/PracticeProgress.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/chart.helpers.js +24 -0
- package/dist/commonjs/features/pmProgress/chart/chart.helpers.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/chart.types.js +2 -0
- package/dist/commonjs/features/pmProgress/chart/chart.types.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/components/PracticeProgressPieChart.js +44 -0
- package/dist/commonjs/features/pmProgress/chart/components/PracticeProgressPieChart.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/components/SlicesClipPaths.js +38 -0
- package/dist/commonjs/features/pmProgress/chart/components/SlicesClipPaths.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/components/SlicesPaths.js +64 -0
- package/dist/commonjs/features/pmProgress/chart/components/SlicesPaths.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/components/layers/SliceArcPattern.js +38 -0
- package/dist/commonjs/features/pmProgress/chart/components/layers/SliceArcPattern.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/components/layers/SliceBackgroundColor.js +21 -0
- package/dist/commonjs/features/pmProgress/chart/components/layers/SliceBackgroundColor.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/components/layers/SliceFillColor.js +49 -0
- package/dist/commonjs/features/pmProgress/chart/components/layers/SliceFillColor.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/model/ArcPatternGenerator.js +49 -0
- package/dist/commonjs/features/pmProgress/chart/model/ArcPatternGenerator.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/model/PieChartManager.js +25 -0
- package/dist/commonjs/features/pmProgress/chart/model/PieChartManager.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/model/SliceHighlight.js +20 -0
- package/dist/commonjs/features/pmProgress/chart/model/SliceHighlight.js.map +1 -0
- package/dist/commonjs/features/pmProgress/chart/model/SliceShapeGenerator.js +67 -0
- package/dist/commonjs/features/pmProgress/chart/model/SliceShapeGenerator.js.map +1 -0
- package/dist/commonjs/features/pmProgress/header/PracticeProgressHeader.js +180 -0
- package/dist/commonjs/features/pmProgress/header/PracticeProgressHeader.js.map +1 -0
- package/dist/commonjs/features/pmProgress/index.js +13 -0
- package/dist/commonjs/features/pmProgress/index.js.map +1 -0
- package/dist/commonjs/features/pmProgress/list/ListCard.js +128 -0
- package/dist/commonjs/features/pmProgress/list/ListCard.js.map +1 -0
- package/dist/commonjs/features/pmProgress/list/ListItem.js +128 -0
- package/dist/commonjs/features/pmProgress/list/ListItem.js.map +1 -0
- package/dist/commonjs/features/pmProgress/recs/TeacherRecs.js +17 -0
- package/dist/commonjs/features/pmProgress/recs/TeacherRecs.js.map +1 -0
- package/dist/commonjs/features/pmProgress/stats/PracticeProgressStats.js +63 -0
- package/dist/commonjs/features/pmProgress/stats/PracticeProgressStats.js.map +1 -0
- package/dist/commonjs/features/pmProgress/stats/StatsCard.js +66 -0
- package/dist/commonjs/features/pmProgress/stats/StatsCard.js.map +1 -0
- package/dist/commonjs/features/pmProgress/temp.js +74 -0
- package/dist/commonjs/features/pmProgress/temp.js.map +1 -0
- package/dist/commonjs/index.js +14 -2
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/shared/icons/PentagonIcon.js +32 -0
- package/dist/commonjs/shared/icons/PentagonIcon.js.map +1 -0
- package/dist/commonjs/shared/icons/SketchArrowRight.js +33 -0
- package/dist/commonjs/shared/icons/SketchArrowRight.js.map +1 -0
- package/dist/commonjs/shared/icons/SliceDiamondIcon.js +32 -0
- package/dist/commonjs/shared/icons/SliceDiamondIcon.js.map +1 -0
- package/dist/commonjs/shared/icons/StarIcon.js +32 -0
- package/dist/commonjs/shared/icons/StarIcon.js.map +1 -0
- package/dist/commonjs/shared/icons/TimerIcon.js +35 -0
- package/dist/commonjs/shared/icons/TimerIcon.js.map +1 -0
- package/dist/module/features/pmProgress/PracticeProgress.js +258 -0
- package/dist/module/features/pmProgress/PracticeProgress.js.map +1 -0
- package/dist/module/features/pmProgress/chart/chart.helpers.js +16 -0
- package/dist/module/features/pmProgress/chart/chart.helpers.js.map +1 -0
- package/dist/module/features/pmProgress/chart/chart.types.js +2 -0
- package/dist/module/features/pmProgress/chart/chart.types.js.map +1 -0
- package/dist/module/features/pmProgress/chart/components/PracticeProgressPieChart.js +38 -0
- package/dist/module/features/pmProgress/chart/components/PracticeProgressPieChart.js.map +1 -0
- package/dist/module/features/pmProgress/chart/components/SlicesClipPaths.js +32 -0
- package/dist/module/features/pmProgress/chart/components/SlicesClipPaths.js.map +1 -0
- package/dist/module/features/pmProgress/chart/components/SlicesPaths.js +58 -0
- package/dist/module/features/pmProgress/chart/components/SlicesPaths.js.map +1 -0
- package/dist/module/features/pmProgress/chart/components/layers/SliceArcPattern.js +32 -0
- package/dist/module/features/pmProgress/chart/components/layers/SliceArcPattern.js.map +1 -0
- package/dist/module/features/pmProgress/chart/components/layers/SliceBackgroundColor.js +15 -0
- package/dist/module/features/pmProgress/chart/components/layers/SliceBackgroundColor.js.map +1 -0
- package/dist/module/features/pmProgress/chart/components/layers/SliceFillColor.js +43 -0
- package/dist/module/features/pmProgress/chart/components/layers/SliceFillColor.js.map +1 -0
- package/dist/module/features/pmProgress/chart/model/ArcPatternGenerator.js +44 -0
- package/dist/module/features/pmProgress/chart/model/ArcPatternGenerator.js.map +1 -0
- package/dist/module/features/pmProgress/chart/model/PieChartManager.js +20 -0
- package/dist/module/features/pmProgress/chart/model/PieChartManager.js.map +1 -0
- package/dist/module/features/pmProgress/chart/model/SliceHighlight.js +15 -0
- package/dist/module/features/pmProgress/chart/model/SliceHighlight.js.map +1 -0
- package/dist/module/features/pmProgress/chart/model/SliceShapeGenerator.js +62 -0
- package/dist/module/features/pmProgress/chart/model/SliceShapeGenerator.js.map +1 -0
- package/dist/module/features/pmProgress/header/PracticeProgressHeader.js +174 -0
- package/dist/module/features/pmProgress/header/PracticeProgressHeader.js.map +1 -0
- package/dist/module/features/pmProgress/index.js +4 -0
- package/dist/module/features/pmProgress/index.js.map +1 -0
- package/dist/module/features/pmProgress/list/ListCard.js +121 -0
- package/dist/module/features/pmProgress/list/ListCard.js.map +1 -0
- package/dist/module/features/pmProgress/list/ListItem.js +122 -0
- package/dist/module/features/pmProgress/list/ListItem.js.map +1 -0
- package/dist/module/features/pmProgress/recs/TeacherRecs.js +11 -0
- package/dist/module/features/pmProgress/recs/TeacherRecs.js.map +1 -0
- package/dist/module/features/pmProgress/stats/PracticeProgressStats.js +56 -0
- package/dist/module/features/pmProgress/stats/PracticeProgressStats.js.map +1 -0
- package/dist/module/features/pmProgress/stats/StatsCard.js +60 -0
- package/dist/module/features/pmProgress/stats/StatsCard.js.map +1 -0
- package/dist/module/features/pmProgress/temp.js +70 -0
- package/dist/module/features/pmProgress/temp.js.map +1 -0
- package/dist/module/index.js +1 -0
- package/dist/module/index.js.map +1 -1
- package/dist/module/shared/icons/PentagonIcon.js +24 -0
- package/dist/module/shared/icons/PentagonIcon.js.map +1 -0
- package/dist/module/shared/icons/SketchArrowRight.js +25 -0
- package/dist/module/shared/icons/SketchArrowRight.js.map +1 -0
- package/dist/module/shared/icons/SliceDiamondIcon.js +24 -0
- package/dist/module/shared/icons/SliceDiamondIcon.js.map +1 -0
- package/dist/module/shared/icons/StarIcon.js +24 -0
- package/dist/module/shared/icons/StarIcon.js.map +1 -0
- package/dist/module/shared/icons/TimerIcon.js +27 -0
- package/dist/module/shared/icons/TimerIcon.js.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/PracticeProgress.d.ts +3 -0
- package/dist/typescript/commonjs/features/pmProgress/PracticeProgress.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/chart.helpers.d.ts +5 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/chart.helpers.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/chart.types.d.ts +35 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/chart.types.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/PracticeProgressPieChart.d.ts +9 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/PracticeProgressPieChart.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/SlicesClipPaths.d.ts +10 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/SlicesClipPaths.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/SlicesPaths.d.ts +11 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/SlicesPaths.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/layers/SliceArcPattern.d.ts +12 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/layers/SliceArcPattern.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/layers/SliceBackgroundColor.d.ts +8 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/layers/SliceBackgroundColor.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/layers/SliceFillColor.d.ts +13 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/components/layers/SliceFillColor.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/model/ArcPatternGenerator.d.ts +12 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/model/ArcPatternGenerator.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/model/PieChartManager.d.ts +16 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/model/PieChartManager.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/model/SliceHighlight.d.ts +10 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/model/SliceHighlight.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/model/SliceShapeGenerator.d.ts +15 -0
- package/dist/typescript/commonjs/features/pmProgress/chart/model/SliceShapeGenerator.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/header/PracticeProgressHeader.d.ts +12 -0
- package/dist/typescript/commonjs/features/pmProgress/header/PracticeProgressHeader.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/index.d.ts +2 -0
- package/dist/typescript/commonjs/features/pmProgress/index.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/list/ListCard.d.ts +15 -0
- package/dist/typescript/commonjs/features/pmProgress/list/ListCard.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/list/ListItem.d.ts +11 -0
- package/dist/typescript/commonjs/features/pmProgress/list/ListItem.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/recs/TeacherRecs.d.ts +3 -0
- package/dist/typescript/commonjs/features/pmProgress/recs/TeacherRecs.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/stats/PracticeProgressStats.d.ts +3 -0
- package/dist/typescript/commonjs/features/pmProgress/stats/PracticeProgressStats.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/stats/StatsCard.d.ts +10 -0
- package/dist/typescript/commonjs/features/pmProgress/stats/StatsCard.d.ts.map +1 -0
- package/dist/typescript/commonjs/features/pmProgress/temp.d.ts +4 -0
- package/dist/typescript/commonjs/features/pmProgress/temp.d.ts.map +1 -0
- package/dist/typescript/commonjs/index.d.ts +1 -0
- package/dist/typescript/commonjs/index.d.ts.map +1 -1
- package/dist/typescript/commonjs/shared/icons/PentagonIcon.d.ts +4 -0
- package/dist/typescript/commonjs/shared/icons/PentagonIcon.d.ts.map +1 -0
- package/dist/typescript/commonjs/shared/icons/SketchArrowRight.d.ts +4 -0
- package/dist/typescript/commonjs/shared/icons/SketchArrowRight.d.ts.map +1 -0
- package/dist/typescript/commonjs/shared/icons/SliceDiamondIcon.d.ts +4 -0
- package/dist/typescript/commonjs/shared/icons/SliceDiamondIcon.d.ts.map +1 -0
- package/dist/typescript/commonjs/shared/icons/StarIcon.d.ts +4 -0
- package/dist/typescript/commonjs/shared/icons/StarIcon.d.ts.map +1 -0
- package/dist/typescript/commonjs/shared/icons/TimerIcon.d.ts +4 -0
- package/dist/typescript/commonjs/shared/icons/TimerIcon.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/PracticeProgress.d.ts +3 -0
- package/dist/typescript/module/features/pmProgress/PracticeProgress.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/chart.helpers.d.ts +5 -0
- package/dist/typescript/module/features/pmProgress/chart/chart.helpers.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/chart.types.d.ts +35 -0
- package/dist/typescript/module/features/pmProgress/chart/chart.types.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/components/PracticeProgressPieChart.d.ts +9 -0
- package/dist/typescript/module/features/pmProgress/chart/components/PracticeProgressPieChart.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/components/SlicesClipPaths.d.ts +10 -0
- package/dist/typescript/module/features/pmProgress/chart/components/SlicesClipPaths.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/components/SlicesPaths.d.ts +11 -0
- package/dist/typescript/module/features/pmProgress/chart/components/SlicesPaths.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/components/layers/SliceArcPattern.d.ts +12 -0
- package/dist/typescript/module/features/pmProgress/chart/components/layers/SliceArcPattern.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/components/layers/SliceBackgroundColor.d.ts +8 -0
- package/dist/typescript/module/features/pmProgress/chart/components/layers/SliceBackgroundColor.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/components/layers/SliceFillColor.d.ts +13 -0
- package/dist/typescript/module/features/pmProgress/chart/components/layers/SliceFillColor.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/model/ArcPatternGenerator.d.ts +12 -0
- package/dist/typescript/module/features/pmProgress/chart/model/ArcPatternGenerator.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/model/PieChartManager.d.ts +16 -0
- package/dist/typescript/module/features/pmProgress/chart/model/PieChartManager.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/model/SliceHighlight.d.ts +10 -0
- package/dist/typescript/module/features/pmProgress/chart/model/SliceHighlight.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/chart/model/SliceShapeGenerator.d.ts +15 -0
- package/dist/typescript/module/features/pmProgress/chart/model/SliceShapeGenerator.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/header/PracticeProgressHeader.d.ts +12 -0
- package/dist/typescript/module/features/pmProgress/header/PracticeProgressHeader.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/index.d.ts +2 -0
- package/dist/typescript/module/features/pmProgress/index.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/list/ListCard.d.ts +15 -0
- package/dist/typescript/module/features/pmProgress/list/ListCard.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/list/ListItem.d.ts +11 -0
- package/dist/typescript/module/features/pmProgress/list/ListItem.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/recs/TeacherRecs.d.ts +3 -0
- package/dist/typescript/module/features/pmProgress/recs/TeacherRecs.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/stats/PracticeProgressStats.d.ts +3 -0
- package/dist/typescript/module/features/pmProgress/stats/PracticeProgressStats.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/stats/StatsCard.d.ts +10 -0
- package/dist/typescript/module/features/pmProgress/stats/StatsCard.d.ts.map +1 -0
- package/dist/typescript/module/features/pmProgress/temp.d.ts +4 -0
- package/dist/typescript/module/features/pmProgress/temp.d.ts.map +1 -0
- package/dist/typescript/module/index.d.ts +1 -0
- package/dist/typescript/module/index.d.ts.map +1 -1
- package/dist/typescript/module/shared/icons/PentagonIcon.d.ts +4 -0
- package/dist/typescript/module/shared/icons/PentagonIcon.d.ts.map +1 -0
- package/dist/typescript/module/shared/icons/SketchArrowRight.d.ts +4 -0
- package/dist/typescript/module/shared/icons/SketchArrowRight.d.ts.map +1 -0
- package/dist/typescript/module/shared/icons/SliceDiamondIcon.d.ts +4 -0
- package/dist/typescript/module/shared/icons/SliceDiamondIcon.d.ts.map +1 -0
- package/dist/typescript/module/shared/icons/StarIcon.d.ts +4 -0
- package/dist/typescript/module/shared/icons/StarIcon.d.ts.map +1 -0
- package/dist/typescript/module/shared/icons/TimerIcon.d.ts +4 -0
- package/dist/typescript/module/shared/icons/TimerIcon.d.ts.map +1 -0
- package/package.json +4 -2
- package/src/features/pmProgress/PracticeProgress.tsx +249 -0
- package/src/features/pmProgress/chart/chart.helpers.ts +15 -0
- package/src/features/pmProgress/chart/chart.types.ts +36 -0
- package/src/features/pmProgress/chart/components/PracticeProgressPieChart.tsx +32 -0
- package/src/features/pmProgress/chart/components/SlicesClipPaths.tsx +36 -0
- package/src/features/pmProgress/chart/components/SlicesPaths.tsx +67 -0
- package/src/features/pmProgress/chart/components/layers/SliceArcPattern.tsx +44 -0
- package/src/features/pmProgress/chart/components/layers/SliceBackgroundColor.tsx +11 -0
- package/src/features/pmProgress/chart/components/layers/SliceFillColor.tsx +55 -0
- package/src/features/pmProgress/chart/model/ArcPatternGenerator.ts +48 -0
- package/src/features/pmProgress/chart/model/PieChartManager.ts +20 -0
- package/src/features/pmProgress/chart/model/SliceHighlight.ts +17 -0
- package/src/features/pmProgress/chart/model/SliceShapeGenerator.ts +71 -0
- package/src/features/pmProgress/header/PracticeProgressHeader.tsx +178 -0
- package/src/features/pmProgress/index.ts +1 -0
- package/src/features/pmProgress/list/ListCard.tsx +133 -0
- package/src/features/pmProgress/list/ListItem.tsx +113 -0
- package/src/features/pmProgress/recs/TeacherRecs.tsx +10 -0
- package/src/features/pmProgress/stats/PracticeProgressStats.tsx +60 -0
- package/src/features/pmProgress/stats/StatsCard.tsx +58 -0
- package/src/features/pmProgress/temp.ts +67 -0
- package/src/index.ts +1 -0
- package/src/shared/icons/PentagonIcon.tsx +17 -0
- package/src/shared/icons/SketchArrowRight.tsx +18 -0
- package/src/shared/icons/SliceDiamondIcon.tsx +16 -0
- package/src/shared/icons/StarIcon.tsx +17 -0
- package/src/shared/icons/TimerIcon.tsx +21 -0
- package/dist/commonjs/features/chatbot/TTS_HIGHLIGHTING.md +0 -120
- package/dist/module/features/chatbot/TTS_HIGHLIGHTING.md +0 -120
- package/src/features/chatbot/TTS_HIGHLIGHTING.md +0 -120
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Svg, { G, Path, Defs } from 'react-native-svg'
|
|
3
|
+
import { IconProps } from './icon.types'
|
|
4
|
+
|
|
5
|
+
export const PentagonIcon = ({ size = 32, color }: IconProps) => {
|
|
6
|
+
return (
|
|
7
|
+
<Svg width={size} height={size} viewBox="0 0 32 31" fill="none">
|
|
8
|
+
<G filter="url(#filter0_dd_2255_6755)">
|
|
9
|
+
<Path
|
|
10
|
+
d="M14.025 2.573a3 3 0 013.527 0l9.788 7.111a3 3 0 011.09 3.355l-3.74 11.506a3 3 0 01-2.852 2.073H9.739a3 3 0 01-2.853-2.073L3.147 13.038a3 3 0 011.09-3.354l9.788-7.111z"
|
|
11
|
+
fill={color}
|
|
12
|
+
/>
|
|
13
|
+
</G>
|
|
14
|
+
<Defs></Defs>
|
|
15
|
+
</Svg>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Svg, { Path } from 'react-native-svg'
|
|
3
|
+
import { IconProps } from './icon.types'
|
|
4
|
+
|
|
5
|
+
export const SketchArrowRight = ({ size = 46, color = '#4D4D63' }: IconProps) => {
|
|
6
|
+
const sizeRatio = 1.65
|
|
7
|
+
return (
|
|
8
|
+
<Svg width={size} height={size * sizeRatio} viewBox="0 0 46 28" fill="none">
|
|
9
|
+
<Path
|
|
10
|
+
d="M2 14.714c17.875-3.906 41.975-1.466 41.975-1.466m0 0s-7.319-9-11.4-11.248m11.4 11.248s-8.773 6.653-10.562 12.737"
|
|
11
|
+
stroke={color}
|
|
12
|
+
strokeWidth={4}
|
|
13
|
+
strokeLinecap="round"
|
|
14
|
+
strokeLinejoin="round"
|
|
15
|
+
/>
|
|
16
|
+
</Svg>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { IconProps } from './icon.types'
|
|
3
|
+
import Svg, { G, Path } from 'react-native-svg'
|
|
4
|
+
|
|
5
|
+
export const SliceDiamondIcon = ({ color, size = 28 }: IconProps) => {
|
|
6
|
+
return (
|
|
7
|
+
<Svg width={size} height={size} viewBox="0 0 28 27" fill="none">
|
|
8
|
+
<G filter="url(#filter0_dd_1929_4175)">
|
|
9
|
+
<Path
|
|
10
|
+
d="M12.004 22.19a2.25 2.25 0 003.652 0l9.269-12.877a2.578 2.578 0 00-.27-3.329C18.677.005 8.983.005 3.005 5.984a2.578 2.578 0 00-.27 3.33l9.269 12.876z"
|
|
11
|
+
fill={color}
|
|
12
|
+
/>
|
|
13
|
+
</G>
|
|
14
|
+
</Svg>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Svg, { Path } from 'react-native-svg'
|
|
3
|
+
import { IconProps } from './icon.types'
|
|
4
|
+
import { COLORS } from '@magmamath/react-native-ui'
|
|
5
|
+
|
|
6
|
+
export const StarIcon = ({ width = 25, color = COLORS.NEUTRAL_1 }: IconProps) => {
|
|
7
|
+
return (
|
|
8
|
+
<Svg width={width} height={width} viewBox="0 0 25 25" fill="none">
|
|
9
|
+
<Path
|
|
10
|
+
fillRule="evenodd"
|
|
11
|
+
clipRule="evenodd"
|
|
12
|
+
d="M13.76.971c-.776-1.295-2.653-1.295-3.43 0L7.082 6.387l-5.565 1.39c-1.508.378-2.028 2.256-.929 3.355l4.128 4.128-1.368 6.502c-.35 1.659 1.394 2.973 2.892 2.18l5.795-3.068 3.865 2.147 2.007 1.004c1.497.748 3.196-.563 2.851-2.201l-1.381-6.564 4.128-4.128c1.099-1.099.579-2.977-.93-3.354L17.01 6.387 13.76.97zM8.796 7.416L12.046 2l3.249 5.416a2 2 0 001.23.91l5.565 1.392-4.129 4.128a2 2 0 00-.542 1.826l1.381 6.564-1.968-.984-3.827-2.126a2 2 0 00-1.907-.02l-5.794 3.068 1.368-6.502a2 2 0 00-.542-1.826L2 9.718l5.565-1.391a2 2 0 001.23-.911z"
|
|
13
|
+
fill={color}
|
|
14
|
+
/>
|
|
15
|
+
</Svg>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Svg, { Path } from 'react-native-svg'
|
|
3
|
+
import { IconProps } from './icon.types'
|
|
4
|
+
import { COLORS } from '@magmamath/react-native-ui'
|
|
5
|
+
|
|
6
|
+
export const TimerIcon = ({ size = 25, color = COLORS.NEUTRAL_1 }: IconProps) => {
|
|
7
|
+
return (
|
|
8
|
+
<Svg width={size} height={size} viewBox="0 0 21 23" fill="none">
|
|
9
|
+
<Path
|
|
10
|
+
d="M10.781 8.984a1.078 1.078 0 10-2.156 0v3.235H6.828a1.078 1.078 0 100 2.156h2.875c.596 0 1.078-.483 1.078-1.078V8.984z"
|
|
11
|
+
fill={color}
|
|
12
|
+
/>
|
|
13
|
+
<Path
|
|
14
|
+
fillRule="evenodd"
|
|
15
|
+
clipRule="evenodd"
|
|
16
|
+
d="M7.547 0a1.078 1.078 0 100 2.156h1.078v1.497C3.773 4.189 0 8.303 0 13.297 0 18.656 4.344 23 9.703 23c5.359 0 9.703-4.344 9.703-9.703 0-2.28-.787-4.377-2.103-6.033l1.034-1.058.666.666a1.078 1.078 0 101.525-1.525l-2.875-2.875a1.078 1.078 0 10-1.525 1.525l.684.684-1.029 1.054a9.663 9.663 0 00-5.002-2.082V2.156h1.078a1.078 1.078 0 000-2.156H7.547zm-5.39 13.297a7.547 7.547 0 1115.093 0 7.547 7.547 0 01-15.094 0z"
|
|
17
|
+
fill={color}
|
|
18
|
+
/>
|
|
19
|
+
</Svg>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# TTS Word-by-Word Highlighting
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Word-by-word text highlighting synchronized with text-to-speech playback in the chatbot. The feature is optional and enabled via `isWordHighlightEnabled` config flag.
|
|
6
|
-
|
|
7
|
-
## How It Works
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
┌─────────────────────────────────────────────────────────────────┐
|
|
11
|
-
│ 1. User clicks TTS button on a message │
|
|
12
|
-
│ ↓ │
|
|
13
|
-
│ 2. Text is converted to SSML with word marks │
|
|
14
|
-
│ "Hello world" → <speak><mark name="w_0"/>Hello │
|
|
15
|
-
│ <mark name="w_1"/>world</speak> │
|
|
16
|
-
│ ↓ │
|
|
17
|
-
│ 3. Google TTS API returns audio + timepoints │
|
|
18
|
-
│ [{ markName: "w_0", timeSeconds: 0.0 }, │
|
|
19
|
-
│ { markName: "w_1", timeSeconds: 0.5 }] │
|
|
20
|
-
│ ↓ │
|
|
21
|
-
│ 4. Audio plays, onTimeUpdate fires via RAF loop │
|
|
22
|
-
│ ↓ │
|
|
23
|
-
│ 5. Current time → find matching timepoint → word index │
|
|
24
|
-
│ ↓ │
|
|
25
|
-
│ 6. $highlightedWordIndex updates → UI re-renders highlighted │
|
|
26
|
-
└─────────────────────────────────────────────────────────────────┘
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Usage
|
|
30
|
-
|
|
31
|
-
```tsx
|
|
32
|
-
// In students-web model setup
|
|
33
|
-
chatModel.bot.setUpT2S({
|
|
34
|
-
audioProvider: createT2SAudioProvider(),
|
|
35
|
-
textSanitizer: removeKatexPartFromTextToSpeech,
|
|
36
|
-
isWordHighlightEnabled: true,
|
|
37
|
-
})
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Architecture
|
|
41
|
-
|
|
42
|
-
### ChatTTSModel (ChatTTSModel.ts)
|
|
43
|
-
|
|
44
|
-
Handles TTS playback and word highlighting. Key design decisions:
|
|
45
|
-
|
|
46
|
-
- **Map-based cache** — `audioCache: Map<string, AudioCache>` stores both `audioPath` and `timepoints` per message. When highlighting is off, `timepoints` is `[]`. Cache lives for the model's lifetime (cleared when user leaves the problem).
|
|
47
|
-
- **`fetchAudio()` method** — encapsulates API call + audioContent decoding. Highlight vs non-highlight differences are ternaries (SSML vs plain text, `enableTimePointing`).
|
|
48
|
-
- **`play()` is linear** — cache lookup → fetch if miss → store → activate highlight if enabled → play audio. No branching between highlight/non-highlight paths.
|
|
49
|
-
- **Pause/resume guards** — `wordHighlightEnabled` check before toggling `setIsActive(false)`/`setIsActive(true)` on pause/resume.
|
|
50
|
-
- **`wordHighlightEnabled` (private)** — stored result of `!!isWordHighlightEnabled && !!audioProvider.onTimeUpdate`. Set once at `setConfig`.
|
|
51
|
-
- **`isWordHighlightEnabled` (public getter)** — read-only access for external consumers (e.g. `Chat.tsx`).
|
|
52
|
-
|
|
53
|
-
### AudioProvider type
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
type AudioProvider = {
|
|
57
|
-
play: (audioPath: string, onPlaybackComplete: () => void) => Promise<void>
|
|
58
|
-
pause: () => void
|
|
59
|
-
resume: () => void
|
|
60
|
-
stop: () => void
|
|
61
|
-
onTimeUpdate?: OnTimeUpdate // optional — presence enables highlight capability
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
`onTimeUpdate` is optional. It's the mechanism that syncs audio playback time with word highlights via a RAF loop. If the provider doesn't have it, highlighting won't activate even if `isWordHighlightEnabled: true`.
|
|
66
|
-
|
|
67
|
-
### ChatTTSHighlightModel (ChatTTSHighlightModel.ts)
|
|
68
|
-
|
|
69
|
-
Manages highlight state using Effector stores with declarative reset. Receives `OnTimeUpdate` callback directly (not the whole `AudioProvider`).
|
|
70
|
-
|
|
71
|
-
- **`$isActive`** — gates `$highlightedWordIndex` computation. Toggled on pause/resume without resetting timepoints.
|
|
72
|
-
- **`$highlightedWordIndex`** — derived store, returns `-1` when inactive or no match. Parses mark name via `WORD_MARK_REGEX`.
|
|
73
|
-
- **`activate(timepoints, onTimeUpdate)`** — sets timepoints, enables highlighting, registers time update callback. Only triggers store updates when the highlighted word actually changes (`lastMark` comparison).
|
|
74
|
-
- **`deactivate(onTimeUpdate)`** — stops listening, resets all state via declarative `.reset()` chain.
|
|
75
|
-
- **All stores use `.reset(this.reset)`** — single `reset` event clears `$time`, `$timepoints`, and `$isActive`.
|
|
76
|
-
|
|
77
|
-
### Helpers (helpers/ttsHighlight.ts)
|
|
78
|
-
|
|
79
|
-
- **`textToSSML(text)`** — converts plain text to SSML with `<mark>` tags for word timing.
|
|
80
|
-
- **`findCurrentMark(timepoints, time)`** — iterates backwards through timepoints to find the current mark. Efficient for linear playback.
|
|
81
|
-
|
|
82
|
-
### Constants (constants.ts)
|
|
83
|
-
|
|
84
|
-
- **`WORD_MARK_REGEX`** — `/^w_(\d+)$/` — extracts word index from mark names like `w_0`, `w_1`.
|
|
85
|
-
|
|
86
|
-
### Naming conventions
|
|
87
|
-
|
|
88
|
-
| Name | Scope | Purpose |
|
|
89
|
-
|------|-------|---------|
|
|
90
|
-
| `wordHighlight` | public instance | `ChatTTSHighlightModel` instance on `ChatTTSModel` |
|
|
91
|
-
| `wordHighlightEnabled` | private field | Whether highlighting is active (set once at config) |
|
|
92
|
-
| `isWordHighlightEnabled` | public getter | Read-only access to `wordHighlightEnabled` for external consumers |
|
|
93
|
-
| `isWordHighlightEnabled` | T2SConfig field | Config flag passed to `setUpT2S()` |
|
|
94
|
-
|
|
95
|
-
## Data Flow
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
AudioProvider.onTimeUpdate(callback)
|
|
99
|
-
↓
|
|
100
|
-
ChatTTSHighlightModel.activate(timepoints, onTimeUpdate)
|
|
101
|
-
↓
|
|
102
|
-
RAF loop → audio.currentTime → findCurrentMark() → only updates when mark changes
|
|
103
|
-
↓
|
|
104
|
-
$highlightedWordIndex = parse mark name "w_N" → N (or -1 if inactive/no match)
|
|
105
|
-
↓
|
|
106
|
-
Chat.tsx: model.t2s.isWordHighlightEnabled && isPlayingMessage ? highlightedWordIndex : -1
|
|
107
|
-
↓
|
|
108
|
-
ChatMessage → MessageContent → TTSHighlightRenderer({ text, highlightedIndex })
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Key Files
|
|
112
|
-
|
|
113
|
-
| File | Purpose |
|
|
114
|
-
|------|---------|
|
|
115
|
-
| `model/ChatTTSModel.ts` | `ChatTTSModel` — TTS playback, caching, highlight orchestration |
|
|
116
|
-
| `model/ChatTTSHighlightModel.ts` | Highlight state — timepoints, current time, `$isActive`, derived word index |
|
|
117
|
-
| `helpers/ttsHighlight.ts` | `textToSSML` + `findCurrentMark` — SSML conversion and playback position lookup |
|
|
118
|
-
| `constants.ts` | `WORD_MARK_REGEX` and audio config defaults |
|
|
119
|
-
| `types/t2s.types.ts` | `AudioProvider`, `OnTimeUpdate`, `AudioStatus`, `AudioCache`, `TTSHighlightRendererProps` types |
|
|
120
|
-
| `components/Chat/Chat.tsx` | Reads `isWordHighlightEnabled` and `$highlightedWordIndex`, passes to messages |
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# TTS Word-by-Word Highlighting
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Word-by-word text highlighting synchronized with text-to-speech playback in the chatbot. The feature is optional and enabled via `isWordHighlightEnabled` config flag.
|
|
6
|
-
|
|
7
|
-
## How It Works
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
┌─────────────────────────────────────────────────────────────────┐
|
|
11
|
-
│ 1. User clicks TTS button on a message │
|
|
12
|
-
│ ↓ │
|
|
13
|
-
│ 2. Text is converted to SSML with word marks │
|
|
14
|
-
│ "Hello world" → <speak><mark name="w_0"/>Hello │
|
|
15
|
-
│ <mark name="w_1"/>world</speak> │
|
|
16
|
-
│ ↓ │
|
|
17
|
-
│ 3. Google TTS API returns audio + timepoints │
|
|
18
|
-
│ [{ markName: "w_0", timeSeconds: 0.0 }, │
|
|
19
|
-
│ { markName: "w_1", timeSeconds: 0.5 }] │
|
|
20
|
-
│ ↓ │
|
|
21
|
-
│ 4. Audio plays, onTimeUpdate fires via RAF loop │
|
|
22
|
-
│ ↓ │
|
|
23
|
-
│ 5. Current time → find matching timepoint → word index │
|
|
24
|
-
│ ↓ │
|
|
25
|
-
│ 6. $highlightedWordIndex updates → UI re-renders highlighted │
|
|
26
|
-
└─────────────────────────────────────────────────────────────────┘
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Usage
|
|
30
|
-
|
|
31
|
-
```tsx
|
|
32
|
-
// In students-web model setup
|
|
33
|
-
chatModel.bot.setUpT2S({
|
|
34
|
-
audioProvider: createT2SAudioProvider(),
|
|
35
|
-
textSanitizer: removeKatexPartFromTextToSpeech,
|
|
36
|
-
isWordHighlightEnabled: true,
|
|
37
|
-
})
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Architecture
|
|
41
|
-
|
|
42
|
-
### ChatTTSModel (ChatTTSModel.ts)
|
|
43
|
-
|
|
44
|
-
Handles TTS playback and word highlighting. Key design decisions:
|
|
45
|
-
|
|
46
|
-
- **Map-based cache** — `audioCache: Map<string, AudioCache>` stores both `audioPath` and `timepoints` per message. When highlighting is off, `timepoints` is `[]`. Cache lives for the model's lifetime (cleared when user leaves the problem).
|
|
47
|
-
- **`fetchAudio()` method** — encapsulates API call + audioContent decoding. Highlight vs non-highlight differences are ternaries (SSML vs plain text, `enableTimePointing`).
|
|
48
|
-
- **`play()` is linear** — cache lookup → fetch if miss → store → activate highlight if enabled → play audio. No branching between highlight/non-highlight paths.
|
|
49
|
-
- **Pause/resume guards** — `wordHighlightEnabled` check before toggling `setIsActive(false)`/`setIsActive(true)` on pause/resume.
|
|
50
|
-
- **`wordHighlightEnabled` (private)** — stored result of `!!isWordHighlightEnabled && !!audioProvider.onTimeUpdate`. Set once at `setConfig`.
|
|
51
|
-
- **`isWordHighlightEnabled` (public getter)** — read-only access for external consumers (e.g. `Chat.tsx`).
|
|
52
|
-
|
|
53
|
-
### AudioProvider type
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
type AudioProvider = {
|
|
57
|
-
play: (audioPath: string, onPlaybackComplete: () => void) => Promise<void>
|
|
58
|
-
pause: () => void
|
|
59
|
-
resume: () => void
|
|
60
|
-
stop: () => void
|
|
61
|
-
onTimeUpdate?: OnTimeUpdate // optional — presence enables highlight capability
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
`onTimeUpdate` is optional. It's the mechanism that syncs audio playback time with word highlights via a RAF loop. If the provider doesn't have it, highlighting won't activate even if `isWordHighlightEnabled: true`.
|
|
66
|
-
|
|
67
|
-
### ChatTTSHighlightModel (ChatTTSHighlightModel.ts)
|
|
68
|
-
|
|
69
|
-
Manages highlight state using Effector stores with declarative reset. Receives `OnTimeUpdate` callback directly (not the whole `AudioProvider`).
|
|
70
|
-
|
|
71
|
-
- **`$isActive`** — gates `$highlightedWordIndex` computation. Toggled on pause/resume without resetting timepoints.
|
|
72
|
-
- **`$highlightedWordIndex`** — derived store, returns `-1` when inactive or no match. Parses mark name via `WORD_MARK_REGEX`.
|
|
73
|
-
- **`activate(timepoints, onTimeUpdate)`** — sets timepoints, enables highlighting, registers time update callback. Only triggers store updates when the highlighted word actually changes (`lastMark` comparison).
|
|
74
|
-
- **`deactivate(onTimeUpdate)`** — stops listening, resets all state via declarative `.reset()` chain.
|
|
75
|
-
- **All stores use `.reset(this.reset)`** — single `reset` event clears `$time`, `$timepoints`, and `$isActive`.
|
|
76
|
-
|
|
77
|
-
### Helpers (helpers/ttsHighlight.ts)
|
|
78
|
-
|
|
79
|
-
- **`textToSSML(text)`** — converts plain text to SSML with `<mark>` tags for word timing.
|
|
80
|
-
- **`findCurrentMark(timepoints, time)`** — iterates backwards through timepoints to find the current mark. Efficient for linear playback.
|
|
81
|
-
|
|
82
|
-
### Constants (constants.ts)
|
|
83
|
-
|
|
84
|
-
- **`WORD_MARK_REGEX`** — `/^w_(\d+)$/` — extracts word index from mark names like `w_0`, `w_1`.
|
|
85
|
-
|
|
86
|
-
### Naming conventions
|
|
87
|
-
|
|
88
|
-
| Name | Scope | Purpose |
|
|
89
|
-
|------|-------|---------|
|
|
90
|
-
| `wordHighlight` | public instance | `ChatTTSHighlightModel` instance on `ChatTTSModel` |
|
|
91
|
-
| `wordHighlightEnabled` | private field | Whether highlighting is active (set once at config) |
|
|
92
|
-
| `isWordHighlightEnabled` | public getter | Read-only access to `wordHighlightEnabled` for external consumers |
|
|
93
|
-
| `isWordHighlightEnabled` | T2SConfig field | Config flag passed to `setUpT2S()` |
|
|
94
|
-
|
|
95
|
-
## Data Flow
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
AudioProvider.onTimeUpdate(callback)
|
|
99
|
-
↓
|
|
100
|
-
ChatTTSHighlightModel.activate(timepoints, onTimeUpdate)
|
|
101
|
-
↓
|
|
102
|
-
RAF loop → audio.currentTime → findCurrentMark() → only updates when mark changes
|
|
103
|
-
↓
|
|
104
|
-
$highlightedWordIndex = parse mark name "w_N" → N (or -1 if inactive/no match)
|
|
105
|
-
↓
|
|
106
|
-
Chat.tsx: model.t2s.isWordHighlightEnabled && isPlayingMessage ? highlightedWordIndex : -1
|
|
107
|
-
↓
|
|
108
|
-
ChatMessage → MessageContent → TTSHighlightRenderer({ text, highlightedIndex })
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Key Files
|
|
112
|
-
|
|
113
|
-
| File | Purpose |
|
|
114
|
-
|------|---------|
|
|
115
|
-
| `model/ChatTTSModel.ts` | `ChatTTSModel` — TTS playback, caching, highlight orchestration |
|
|
116
|
-
| `model/ChatTTSHighlightModel.ts` | Highlight state — timepoints, current time, `$isActive`, derived word index |
|
|
117
|
-
| `helpers/ttsHighlight.ts` | `textToSSML` + `findCurrentMark` — SSML conversion and playback position lookup |
|
|
118
|
-
| `constants.ts` | `WORD_MARK_REGEX` and audio config defaults |
|
|
119
|
-
| `types/t2s.types.ts` | `AudioProvider`, `OnTimeUpdate`, `AudioStatus`, `AudioCache`, `TTSHighlightRendererProps` types |
|
|
120
|
-
| `components/Chat/Chat.tsx` | Reads `isWordHighlightEnabled` and `$highlightedWordIndex`, passes to messages |
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# TTS Word-by-Word Highlighting
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Word-by-word text highlighting synchronized with text-to-speech playback in the chatbot. The feature is optional and enabled via `isWordHighlightEnabled` config flag.
|
|
6
|
-
|
|
7
|
-
## How It Works
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
┌─────────────────────────────────────────────────────────────────┐
|
|
11
|
-
│ 1. User clicks TTS button on a message │
|
|
12
|
-
│ ↓ │
|
|
13
|
-
│ 2. Text is converted to SSML with word marks │
|
|
14
|
-
│ "Hello world" → <speak><mark name="w_0"/>Hello │
|
|
15
|
-
│ <mark name="w_1"/>world</speak> │
|
|
16
|
-
│ ↓ │
|
|
17
|
-
│ 3. Google TTS API returns audio + timepoints │
|
|
18
|
-
│ [{ markName: "w_0", timeSeconds: 0.0 }, │
|
|
19
|
-
│ { markName: "w_1", timeSeconds: 0.5 }] │
|
|
20
|
-
│ ↓ │
|
|
21
|
-
│ 4. Audio plays, onTimeUpdate fires via RAF loop │
|
|
22
|
-
│ ↓ │
|
|
23
|
-
│ 5. Current time → find matching timepoint → word index │
|
|
24
|
-
│ ↓ │
|
|
25
|
-
│ 6. $highlightedWordIndex updates → UI re-renders highlighted │
|
|
26
|
-
└─────────────────────────────────────────────────────────────────┘
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Usage
|
|
30
|
-
|
|
31
|
-
```tsx
|
|
32
|
-
// In students-web model setup
|
|
33
|
-
chatModel.bot.setUpT2S({
|
|
34
|
-
audioProvider: createT2SAudioProvider(),
|
|
35
|
-
textSanitizer: removeKatexPartFromTextToSpeech,
|
|
36
|
-
isWordHighlightEnabled: true,
|
|
37
|
-
})
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Architecture
|
|
41
|
-
|
|
42
|
-
### ChatTTSModel (ChatTTSModel.ts)
|
|
43
|
-
|
|
44
|
-
Handles TTS playback and word highlighting. Key design decisions:
|
|
45
|
-
|
|
46
|
-
- **Map-based cache** — `audioCache: Map<string, AudioCache>` stores both `audioPath` and `timepoints` per message. When highlighting is off, `timepoints` is `[]`. Cache lives for the model's lifetime (cleared when user leaves the problem).
|
|
47
|
-
- **`fetchAudio()` method** — encapsulates API call + audioContent decoding. Highlight vs non-highlight differences are ternaries (SSML vs plain text, `enableTimePointing`).
|
|
48
|
-
- **`play()` is linear** — cache lookup → fetch if miss → store → activate highlight if enabled → play audio. No branching between highlight/non-highlight paths.
|
|
49
|
-
- **Pause/resume guards** — `wordHighlightEnabled` check before toggling `setIsActive(false)`/`setIsActive(true)` on pause/resume.
|
|
50
|
-
- **`wordHighlightEnabled` (private)** — stored result of `!!isWordHighlightEnabled && !!audioProvider.onTimeUpdate`. Set once at `setConfig`.
|
|
51
|
-
- **`isWordHighlightEnabled` (public getter)** — read-only access for external consumers (e.g. `Chat.tsx`).
|
|
52
|
-
|
|
53
|
-
### AudioProvider type
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
type AudioProvider = {
|
|
57
|
-
play: (audioPath: string, onPlaybackComplete: () => void) => Promise<void>
|
|
58
|
-
pause: () => void
|
|
59
|
-
resume: () => void
|
|
60
|
-
stop: () => void
|
|
61
|
-
onTimeUpdate?: OnTimeUpdate // optional — presence enables highlight capability
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
`onTimeUpdate` is optional. It's the mechanism that syncs audio playback time with word highlights via a RAF loop. If the provider doesn't have it, highlighting won't activate even if `isWordHighlightEnabled: true`.
|
|
66
|
-
|
|
67
|
-
### ChatTTSHighlightModel (ChatTTSHighlightModel.ts)
|
|
68
|
-
|
|
69
|
-
Manages highlight state using Effector stores with declarative reset. Receives `OnTimeUpdate` callback directly (not the whole `AudioProvider`).
|
|
70
|
-
|
|
71
|
-
- **`$isActive`** — gates `$highlightedWordIndex` computation. Toggled on pause/resume without resetting timepoints.
|
|
72
|
-
- **`$highlightedWordIndex`** — derived store, returns `-1` when inactive or no match. Parses mark name via `WORD_MARK_REGEX`.
|
|
73
|
-
- **`activate(timepoints, onTimeUpdate)`** — sets timepoints, enables highlighting, registers time update callback. Only triggers store updates when the highlighted word actually changes (`lastMark` comparison).
|
|
74
|
-
- **`deactivate(onTimeUpdate)`** — stops listening, resets all state via declarative `.reset()` chain.
|
|
75
|
-
- **All stores use `.reset(this.reset)`** — single `reset` event clears `$time`, `$timepoints`, and `$isActive`.
|
|
76
|
-
|
|
77
|
-
### Helpers (helpers/ttsHighlight.ts)
|
|
78
|
-
|
|
79
|
-
- **`textToSSML(text)`** — converts plain text to SSML with `<mark>` tags for word timing.
|
|
80
|
-
- **`findCurrentMark(timepoints, time)`** — iterates backwards through timepoints to find the current mark. Efficient for linear playback.
|
|
81
|
-
|
|
82
|
-
### Constants (constants.ts)
|
|
83
|
-
|
|
84
|
-
- **`WORD_MARK_REGEX`** — `/^w_(\d+)$/` — extracts word index from mark names like `w_0`, `w_1`.
|
|
85
|
-
|
|
86
|
-
### Naming conventions
|
|
87
|
-
|
|
88
|
-
| Name | Scope | Purpose |
|
|
89
|
-
|------|-------|---------|
|
|
90
|
-
| `wordHighlight` | public instance | `ChatTTSHighlightModel` instance on `ChatTTSModel` |
|
|
91
|
-
| `wordHighlightEnabled` | private field | Whether highlighting is active (set once at config) |
|
|
92
|
-
| `isWordHighlightEnabled` | public getter | Read-only access to `wordHighlightEnabled` for external consumers |
|
|
93
|
-
| `isWordHighlightEnabled` | T2SConfig field | Config flag passed to `setUpT2S()` |
|
|
94
|
-
|
|
95
|
-
## Data Flow
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
AudioProvider.onTimeUpdate(callback)
|
|
99
|
-
↓
|
|
100
|
-
ChatTTSHighlightModel.activate(timepoints, onTimeUpdate)
|
|
101
|
-
↓
|
|
102
|
-
RAF loop → audio.currentTime → findCurrentMark() → only updates when mark changes
|
|
103
|
-
↓
|
|
104
|
-
$highlightedWordIndex = parse mark name "w_N" → N (or -1 if inactive/no match)
|
|
105
|
-
↓
|
|
106
|
-
Chat.tsx: model.t2s.isWordHighlightEnabled && isPlayingMessage ? highlightedWordIndex : -1
|
|
107
|
-
↓
|
|
108
|
-
ChatMessage → MessageContent → TTSHighlightRenderer({ text, highlightedIndex })
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Key Files
|
|
112
|
-
|
|
113
|
-
| File | Purpose |
|
|
114
|
-
|------|---------|
|
|
115
|
-
| `model/ChatTTSModel.ts` | `ChatTTSModel` — TTS playback, caching, highlight orchestration |
|
|
116
|
-
| `model/ChatTTSHighlightModel.ts` | Highlight state — timepoints, current time, `$isActive`, derived word index |
|
|
117
|
-
| `helpers/ttsHighlight.ts` | `textToSSML` + `findCurrentMark` — SSML conversion and playback position lookup |
|
|
118
|
-
| `constants.ts` | `WORD_MARK_REGEX` and audio config defaults |
|
|
119
|
-
| `types/t2s.types.ts` | `AudioProvider`, `OnTimeUpdate`, `AudioStatus`, `AudioCache`, `TTSHighlightRendererProps` types |
|
|
120
|
-
| `components/Chat/Chat.tsx` | Reads `isWordHighlightEnabled` and `$highlightedWordIndex`, passes to messages |
|