diff --git a/content/chapters/test-heavy-stress.json b/content/chapters/test-heavy-stress.json
index 6c05959..d6b3051 100644
--- a/content/chapters/test-heavy-stress.json
+++ b/content/chapters/test-heavy-stress.json
@@ -205,8 +205,9 @@
"elaborate": { "user_language": "élaboré", "type": "adjective", "pronunciation": "/ɪˈlæbərət/" },
"sophisticated": { "user_language": "sophistiqué", "type": "adjective", "pronunciation": "/səˈfɪstɪkeɪtɪd/" }
},
- "lessons": {
- "lesson1": {
+ "texts": [
+ {
+ "id": "text1",
"title": "Foundations of Research Methodology",
"content": "Research methodology constitutes the systematic theoretical analysis of the methods applied to a field of study. It encompasses the comprehensive framework of epistemological, ontological, and phenomenological considerations that guide rigorous academic inquiry. Contemporary researchers must navigate complex paradigmatic tensions between positivist empirical approaches and constructivist interpretive methodologies. The theoretical foundations of quantitative research emphasize statistical significance, experimental control, and generalizability of findings across diverse populations. Conversely, qualitative investigation prioritizes comprehensive understanding, contextual interpretation, and the nuanced exploration of subjective phenomena. Modern academic discourse increasingly advocates for sophisticated mixed-methods approaches that synthesize both quantitative measurement and qualitative analysis. Triangulation strategies enhance validity through multiple data sources, theoretical perspectives, and methodological approaches. Researchers must demonstrate meticulous attention to bias reduction, sample representativeness, and ethical considerations throughout the investigation process. The operationalization of abstract concepts requires precise definitional frameworks and robust measurement instruments. Contemporary methodology emphasizes reproducibility, transparency, and rigorous peer review processes. Advanced statistical techniques including regression modeling, multivariate analysis, and machine learning algorithms enable sophisticated pattern recognition and predictive modeling. Researchers increasingly utilize computational processing power for extensive dataset analysis and complex simulation procedures.",
"questions": [
@@ -242,7 +243,8 @@
}
]
},
- "lesson2": {
+ {
+ "id": "text2",
"title": "Advanced Statistical Analysis and Data Interpretation",
"content": "Statistical analysis represents the cornerstone of empirical research methodology, encompassing sophisticated techniques for data exploration, hypothesis testing, and inferential reasoning. Contemporary researchers utilize advanced algorithms for pattern recognition, anomaly detection, and predictive modeling across extensive datasets. The distribution characteristics of variables determine appropriate statistical procedures, including parametric and non-parametric approaches for hypothesis evaluation. Correlation analysis reveals relationships between variables, while causation requires controlled experimental manipulation and rigorous confounding variable elimination. Regression modeling enables prediction and explanation of dependent variable variance through multiple independent predictors. Cluster analysis identifies natural groupings within populations, facilitating targeted intervention strategies and segmentation approaches. Longitudinal studies track cohorts across time, revealing developmental patterns and causal sequences. Cross-sectional comparative designs enable efficient population sampling at specific temporal points. Measurement validity encompasses content, construct, and criterion-related approaches for instrument development. Reliability coefficients assess consistency across time, interrater agreement, and internal consistency measures. Confidence intervals provide probability-based ranges for population parameter estimation. Effect sizes quantify practical significance beyond statistical significance testing. Meta-analysis synthesizes findings across multiple independent studies, enhancing generalizability and statistical power. Advanced modeling techniques including structural equation modeling, hierarchical linear modeling, and machine learning algorithms enable sophisticated hypothesis testing with complex nested data structures.",
"questions": [
@@ -278,7 +280,8 @@
}
]
},
- "lesson3": {
+ {
+ "id": "text3",
"title": "Qualitative Research and Interpretive Methodologies",
"content": "Qualitative research methodology embraces interpretive paradigms that prioritize comprehensive understanding of complex social phenomena through detailed narrative analysis and contextual interpretation. Ethnographic approaches involve extensive participant observation, immersive fieldwork, and cultural interpretation through prolonged engagement with research participants. Phenomenological investigations explore lived experiences, consciousness structures, and subjective meaning-making processes through in-depth interview techniques. Grounded theory methodology emphasizes systematic data collection, theoretical sampling, and iterative analysis for theory development from empirical observations. Hermeneutical approaches focus on textual interpretation, discourse analysis, and the historical contextualization of cultural artifacts. Narrative research examines personal stories, biographical accounts, and identity construction through temporal sequence analysis. Case study methodology provides comprehensive investigation of specific instances, organizations, or phenomena within natural settings. Action research integrates investigation with practical intervention, emphasizing collaborative participation and social change objectives. Data collection techniques include semi-structured interviews, focus group discussions, participant observation, and document analysis procedures. Coding procedures involve open, axial, and selective approaches for pattern identification and categorical development. Triangulation strategies enhance credibility through multiple data sources, investigator perspectives, and theoretical frameworks. Member checking validates interpretations through participant feedback and collaborative meaning verification. Transferability replaces generalizability through detailed contextual description and theoretical conceptualization. Confirmability ensures objectivity through reflexive journaling, audit trails, and peer debriefing processes. Advanced qualitative analysis software facilitates systematic coding, pattern recognition, and theoretical model development.",
"questions": [
@@ -314,7 +317,7 @@
}
]
}
- },
+ ],
"dialogs": [
{
"id": "conference_presentation",
@@ -477,113 +480,272 @@
]
}
],
- "sentences": {
- "practice": [
- "The methodology demonstrates rigorous scientific standards.",
- "Researchers utilize sophisticated statistical techniques for comprehensive analysis.",
- "Quantitative data provides empirical evidence for theoretical hypotheses.",
- "Qualitative interpretation reveals nuanced patterns in participant narratives.",
- "The experimental design controls confounding variables effectively.",
- "Statistical significance indicates meaningful relationships between measured variables.",
- "Longitudinal studies track developmental changes across extended timeframes.",
- "Cross-sectional comparisons examine differences between demographic populations.",
- "Triangulation strategies enhance validity through multiple data sources.",
- "Systematic literature review synthesizes existing theoretical knowledge comprehensively.",
- "Operational definitions specify measurement procedures for abstract concepts.",
- "Random sampling ensures population representativeness and generalizability.",
- "Ethical considerations protect participant welfare throughout investigations.",
- "Correlation analysis reveals associations without establishing causation.",
- "Regression modeling predicts outcomes through multiple predictor variables.",
- "The paradigm influences methodological choices and theoretical interpretations.",
- "Empirical observations support or refute proposed theoretical frameworks.",
- "Phenomenological analysis explores subjective experiences and meaning-making.",
- "Grounded theory develops from systematic data collection and analysis.",
- "Hermeneutical interpretation examines cultural texts and historical contexts.",
- "The algorithm processes large datasets for pattern recognition.",
- "Simulation modeling tests theoretical assumptions before empirical implementation.",
- "Validation procedures confirm measurement accuracy and theoretical relevance.",
- "Replication studies verify original findings across different contexts.",
- "Meta-analysis combines results from multiple independent investigations.",
- "Effect sizes quantify practical significance beyond statistical testing.",
- "Confidence intervals estimate population parameters with probability ranges.",
- "Bias reduction requires careful attention to methodological procedures.",
- "The framework integrates multiple theoretical perspectives comprehensively.",
- "Operationalization transforms abstract concepts into measurable variables.",
- "Distribution characteristics determine appropriate statistical procedures.",
- "Anomaly detection identifies unusual patterns in complex datasets.",
- "The criterion establishes standards for measurement and evaluation.",
- "Multiple criteria guide comprehensive assessment and decision-making.",
- "The phenomenon requires sophisticated analytical approaches.",
- "Various phenomena demonstrate consistent patterns across contexts.",
- "Assessment procedures evaluate participant performance objectively.",
- "Comprehensive evaluation examines multiple dimensions simultaneously.",
- "Interpretation depends on theoretical frameworks and contextual factors.",
- "Conceptualization provides foundation for empirical investigation.",
- "Epistemological assumptions influence research design and methodology.",
- "Ontological considerations determine reality conceptualization and measurement.",
- "Phenomenology examines consciousness structures and lived experiences.",
- "Hermeneutics focuses on textual interpretation and cultural understanding.",
- "Positivism emphasizes empirical observation and scientific objectivity.",
- "Constructivism recognizes subjective meaning-making and social construction.",
- "Pragmatism values practical utility and problem-solving approaches.",
- "Investigation requires systematic planning and rigorous execution.",
- "Inquiry processes involve questioning and knowledge discovery.",
- "Exploration reveals previously unknown patterns and relationships."
- ],
- "translation": [
- "The research methodology requires comprehensive theoretical analysis.",
- "Statistical procedures must follow rigorous validation standards.",
- "Experimental design controls variables through randomization techniques.",
- "Qualitative analysis examines narrative patterns systematically.",
- "Longitudinal tracking enables causal inference development.",
- "Cross-sectional comparison reveals demographic differences clearly.",
- "Triangulation enhances validity through multiple approaches.",
- "Systematic review synthesizes literature comprehensively.",
- "Operational definitions specify measurement procedures precisely.",
- "Random sampling ensures representative population selection.",
- "Ethical considerations protect all research participants.",
- "Correlation indicates association without proving causation.",
- "Regression modeling predicts outcomes accurately.",
- "Paradigmatic assumptions influence methodological choices.",
- "Empirical evidence supports theoretical propositions.",
- "Phenomenological investigation explores subjective experiences.",
- "Grounded theory emerges from systematic analysis.",
- "Hermeneutical interpretation examines cultural contexts.",
- "Advanced algorithms process complex datasets.",
- "Simulation testing validates theoretical assumptions.",
- "Replication confirms findings across contexts.",
- "Meta-analysis combines multiple studies.",
- "Effect sizes indicate practical significance.",
- "Confidence intervals estimate parameters.",
- "Bias reduction improves methodological quality.",
- "Theoretical frameworks guide comprehensive analysis.",
- "Distribution patterns influence statistical choices.",
- "Anomaly detection reveals unusual observations.",
- "Assessment criteria establish evaluation standards.",
- "Comprehensive interpretation requires contextual understanding."
- ],
- "comprehension": [
- "Modern research methodologies integrate sophisticated theoretical frameworks with rigorous empirical validation procedures for comprehensive scientific advancement.",
- "The paradigmatic tension between quantitative measurement and qualitative interpretation requires careful methodological consideration and theoretical synthesis.",
- "Statistical significance testing provides probability-based evidence for hypothesis evaluation while effect sizes quantify practical importance.",
- "Longitudinal cohort studies enable causal inference through temporal sequencing while controlling confounding variables systematically.",
- "Triangulation strategies enhance research validity through multiple data sources, theoretical perspectives, and methodological approaches.",
- "Contemporary experimental design emphasizes randomization procedures, control group comparisons, and bias reduction for optimal validity.",
- "Qualitative phenomenological analysis explores lived experiences through detailed narrative interpretation and contextual understanding.",
- "Grounded theory methodology develops theoretical frameworks through systematic data collection, coding procedures, and analytical iteration.",
- "Advanced computational algorithms enable sophisticated pattern recognition, anomaly detection, and predictive modeling across datasets.",
- "Ethical research standards require informed consent, confidentiality protection, and participant welfare throughout investigations.",
- "Meta-analytical synthesis combines findings across multiple studies for enhanced generalizability and statistical power.",
- "Measurement validation involves reliability assessment, construct validity evaluation, and criterion-related correlation analysis.",
- "Cross-cultural research requires methodological adaptation, language translation, and cultural sensitivity considerations.",
- "Replication studies verify original findings while extending theoretical understanding through methodological refinement.",
- "Systematic literature reviews synthesize existing knowledge comprehensively while identifying theoretical gaps and research opportunities.",
- "Mixed-methods approaches integrate quantitative measurement with qualitative interpretation for comprehensive understanding.",
- "Advanced statistical modeling incorporates hierarchical structures, repeated measures, and missing data procedures.",
- "Epistemological foundations influence research design, data interpretation, and theoretical development significantly.",
- "Contemporary methodology emphasizes transparency, reproducibility, and open science practices for enhanced credibility.",
- "Interdisciplinary collaboration requires methodological flexibility while maintaining rigorous scientific standards consistently."
- ]
+ "phrases": {
+ "The methodology demonstrates rigorous scientific standards": {
+ "user_language": "La méthodologie démontre des normes scientifiques rigoureuses",
+ "context": "methodology",
+ "pronunciation": "/ðə ˌmeθəˈdɒlədʒi ˈdemənstreɪts ˈrɪɡərəs ˌsaɪənˈtɪfɪk ˈstændərdz/"
+ },
+ "Researchers utilize sophisticated statistical techniques": {
+ "user_language": "Les chercheurs utilisent des techniques statistiques sophistiquées",
+ "context": "research-methods",
+ "pronunciation": "/rɪˈsɜːtʃərz ˈjuːtəlaɪz səˈfɪstɪkeɪtɪd stəˈtɪstɪkəl tekˈniːks/"
+ },
+ "Quantitative data provides empirical evidence": {
+ "user_language": "Les données quantitatives fournissent des preuves empiriques",
+ "context": "data-analysis",
+ "pronunciation": "/ˈkwɒntɪtətɪv ˈdeɪtə prəˈvaɪdz ɪmˈpɪrɪkəl ˈevɪdəns/"
+ },
+ "Qualitative interpretation reveals nuanced patterns": {
+ "user_language": "L'interprétation qualitative révèle des modèles nuancés",
+ "context": "qualitative-research",
+ "pronunciation": "/ˈkwɒlɪtətɪv ɪnˌtɜːprɪˈteɪʃən rɪˈviːlz ˈnjuːɑːnst ˈpætənz/"
+ },
+ "The experimental design controls confounding variables": {
+ "user_language": "Le design expérimental contrôle les variables confondantes",
+ "context": "experimental-design",
+ "pronunciation": "/ði ɪkˌsperɪˈmentəl dɪˈzaɪn kənˈtrəʊlz kənˈfaʊndɪŋ ˈvɛəriəbəlz/"
+ },
+ "Statistical significance indicates meaningful relationships": {
+ "user_language": "La signification statistique indique des relations significatives",
+ "context": "statistics",
+ "pronunciation": "/stəˈtɪstɪkəl sɪɡˈnɪfɪkəns ˈɪndɪkeɪts ˈmiːnɪŋfəl rɪˈleɪʃənʃɪps/"
+ },
+ "Longitudinal studies track developmental changes": {
+ "user_language": "Les études longitudinales suivent les changements développementaux",
+ "context": "research-design",
+ "pronunciation": "/ˌlɒŋɡɪˈtjuːdɪnəl ˈstʌdiz træk dɪˌveləpˈmentəl ˈtʃeɪndʒɪz/"
+ },
+ "Cross-sectional comparisons examine demographic differences": {
+ "user_language": "Les comparaisons transversales examinent les différences démographiques",
+ "context": "comparative-research",
+ "pronunciation": "/krɒs ˈsekʃənəl kəmˈpærɪsənz ɪɡˈzæmɪn ˌdeməˈɡræfɪk ˈdɪfərənsɪz/"
+ },
+ "Triangulation strategies enhance validity": {
+ "user_language": "Les stratégies de triangulation améliorent la validité",
+ "context": "validation",
+ "pronunciation": "/traɪˌæŋɡjuˈleɪʃən ˈstrætɪdʒiz ɪnˈhɑːns vəˈlɪdəti/"
+ },
+ "Systematic literature review synthesizes existing knowledge": {
+ "user_language": "La revue systématique de la littérature synthétise les connaissances existantes",
+ "context": "literature-review",
+ "pronunciation": "/ˌsɪstəˈmætɪk ˈlɪtərətʃər rɪˈvjuː ˈsɪnθəsaɪzɪz ɪɡˈzɪstɪŋ ˈnɒlɪdʒ/"
+ },
+ "Operational definitions specify measurement procedures": {
+ "user_language": "Les définitions opérationnelles spécifient les procédures de mesure",
+ "context": "measurement",
+ "pronunciation": "/ˌɒpəˈreɪʃənəl ˌdefɪˈnɪʃənz ˈspesɪfaɪ ˈmeʒərmənt prəˈsiːdʒərz/"
+ },
+ "Random sampling ensures population representativeness": {
+ "user_language": "L'échantillonnage aléatoire garantit la représentativité de la population",
+ "context": "sampling",
+ "pronunciation": "/ˈrændəm ˈsɑːmplɪŋ ɪnˈʃʊərz ˌpɒpjuˈleɪʃən ˌreprɪzenˈteɪtɪvnəs/"
+ },
+ "Ethical considerations protect participant welfare": {
+ "user_language": "Les considérations éthiques protègent le bien-être des participants",
+ "context": "ethics",
+ "pronunciation": "/ˈeθɪkəl kənˌsɪdəˈreɪʃənz prəˈtekt pɑːˈtɪsɪpənt ˈwelfeər/"
+ },
+ "Correlation analysis reveals associations without causation": {
+ "user_language": "L'analyse de corrélation révèle des associations sans causalité",
+ "context": "correlation",
+ "pronunciation": "/ˌkɒrəˈleɪʃən əˈnæləsɪs rɪˈviːlz əˌsəʊsiˈeɪʃənz wɪˈðaʊt kɔːˈzeɪʃən/"
+ },
+ "Regression modeling predicts outcomes through predictors": {
+ "user_language": "La modélisation par régression prédit les résultats par des prédicteurs",
+ "context": "predictive-modeling",
+ "pronunciation": "/rɪˈɡreʃən ˈmɒdəlɪŋ prɪˈdɪkts ˈaʊtkʌmz θruː prɪˈdɪktərz/"
+ },
+ "The paradigm influences methodological choices": {
+ "user_language": "Le paradigme influence les choix méthodologiques",
+ "context": "theoretical-framework",
+ "pronunciation": "/ðə ˈpærədaɪm ˈɪnfluənsɪz ˌmeθədəˈlɒdʒɪkəl ˈtʃɔɪsɪz/"
+ },
+ "Empirical observations support theoretical frameworks": {
+ "user_language": "Les observations empiriques soutiennent les cadres théoriques",
+ "context": "theory-testing",
+ "pronunciation": "/ɪmˈpɪrɪkəl ˌɒbzəˈveɪʃənz səˈpɔːt ˌθiːəˈretɪkəl ˈfreɪmwɜːks/"
+ },
+ "Phenomenological analysis explores subjective experiences": {
+ "user_language": "L'analyse phénoménologique explore les expériences subjectives",
+ "context": "phenomenology",
+ "pronunciation": "/fɪˌnɒmɪnəˈlɒdʒɪkəl əˈnæləsɪs ɪkˈsplɔːrz səbˈdʒektɪv ɪkˈspɪəriənsɪz/"
+ },
+ "Grounded theory develops from systematic data collection": {
+ "user_language": "La théorie ancrée se développe à partir de la collecte systématique de données",
+ "context": "grounded-theory",
+ "pronunciation": "/ˈɡraʊndɪd ˈθiːəri dɪˈveləps frɒm ˌsɪstəˈmætɪk ˈdeɪtə kəˈlekʃən/"
+ },
+ "Hermeneutical interpretation examines cultural texts": {
+ "user_language": "L'interprétation herméneutique examine les textes culturels",
+ "context": "hermeneutics",
+ "pronunciation": "/ˌhɜːmɪˈnjuːtɪkəl ɪnˌtɜːprɪˈteɪʃən ɪɡˈzæmɪnz ˈkʌltʃərəl teksts/"
+ },
+ "The algorithm processes large datasets for pattern recognition": {
+ "user_language": "L'algorithme traite de grands ensembles de données pour la reconnaissance de motifs",
+ "context": "computational-analysis",
+ "pronunciation": "/ði ˈælɡərɪðəm ˈprəʊsesɪz lɑːdʒ ˈdeɪtəsets fɔː ˈpætən ˌrekəɡˈnɪʃən/"
+ },
+ "Simulation modeling tests theoretical assumptions": {
+ "user_language": "La modélisation par simulation teste les hypothèses théoriques",
+ "context": "simulation",
+ "pronunciation": "/ˌsɪmjuˈleɪʃən ˈmɒdəlɪŋ tests ˌθiːəˈretɪkəl əˈsʌmpʃənz/"
+ },
+ "Validation procedures confirm measurement accuracy": {
+ "user_language": "Les procédures de validation confirment la précision des mesures",
+ "context": "validation",
+ "pronunciation": "/ˌvælɪˈdeɪʃən prəˈsiːdʒərz kənˈfɜːm ˈmeʒərmənt ˈækjərəsi/"
+ },
+ "Replication studies verify original findings": {
+ "user_language": "Les études de réplication vérifient les résultats originaux",
+ "context": "replication",
+ "pronunciation": "/ˌreplɪˈkeɪʃən ˈstʌdiz ˈverɪfaɪ əˈrɪdʒɪnəl ˈfaɪndɪŋz/"
+ },
+ "Meta-analysis combines results from multiple studies": {
+ "user_language": "La méta-analyse combine les résultats de plusieurs études",
+ "context": "meta-analysis",
+ "pronunciation": "/ˌmetəəˈnæləsɪs kəmˈbaɪnz rɪˈzʌlts frɒm ˈmʌltɪpəl ˈstʌdiz/"
+ },
+ "Effect sizes quantify practical significance": {
+ "user_language": "Les tailles d'effet quantifient la signification pratique",
+ "context": "effect-size",
+ "pronunciation": "/ɪˈfekt saɪzɪz ˈkwɒntɪfaɪ ˈpræktɪkəl sɪɡˈnɪfɪkəns/"
+ },
+ "Confidence intervals estimate population parameters": {
+ "user_language": "Les intervalles de confiance estiment les paramètres de population",
+ "context": "statistical-inference",
+ "pronunciation": "/ˈkɒnfɪdəns ˈɪntəvəlz ˈestɪmeɪt ˌpɒpjuˈleɪʃən pəˈræmɪtərz/"
+ },
+ "Bias reduction requires careful methodological attention": {
+ "user_language": "La réduction des biais nécessite une attention méthodologique minutieuse",
+ "context": "bias-control",
+ "pronunciation": "/ˈbaɪəs rɪˈdʌkʃən rɪˈkwaɪərz ˈkeəfəl ˌmeθədəˈlɒdʒɪkəl əˈtenʃən/"
+ },
+ "The framework integrates multiple theoretical perspectives": {
+ "user_language": "Le cadre intègre plusieurs perspectives théoriques",
+ "context": "theoretical-integration",
+ "pronunciation": "/ðə ˈfreɪmwɜːk ˈɪntɪɡreɪts ˈmʌltɪpəl ˌθiːəˈretɪkəl pərˈspektɪvz/"
+ },
+ "Operationalization transforms concepts into measurable variables": {
+ "user_language": "L'opérationnalisation transforme les concepts en variables mesurables",
+ "context": "operationalization",
+ "pronunciation": "/ˌɒpəreɪʃənəlaɪˈzeɪʃən trænsˈfɔːmz ˈkɒnsepts ˈɪntuː ˈmeʒərəbəl ˈvɛəriəbəlz/"
+ },
+ "Distribution characteristics determine statistical procedures": {
+ "user_language": "Les caractéristiques de distribution déterminent les procédures statistiques",
+ "context": "statistical-assumptions",
+ "pronunciation": "/ˌdɪstrɪˈbjuːʃən ˌkærəktəˈrɪstɪks dɪˈtɜːmɪn stəˈtɪstɪkəl prəˈsiːdʒərz/"
+ },
+ "Anomaly detection identifies unusual patterns": {
+ "user_language": "La détection d'anomalies identifie des motifs inhabituels",
+ "context": "data-quality",
+ "pronunciation": "/əˈnɒməli dɪˈtekʃən aɪˈdentɪfaɪz ʌnˈjuːʒuəl ˈpætənz/"
+ },
+ "The criterion establishes evaluation standards": {
+ "user_language": "Le critère établit des normes d'évaluation",
+ "context": "assessment",
+ "pronunciation": "/ðə kraɪˈtɪəriən ɪˈstæblɪʃɪz ɪˌvæljuˈeɪʃən ˈstændərdz/"
+ },
+ "Multiple criteria guide comprehensive assessment": {
+ "user_language": "Plusieurs critères guident l'évaluation complète",
+ "context": "evaluation",
+ "pronunciation": "/ˈmʌltɪpəl kraɪˈtɪəriə ɡaɪd ˌkɒmprɪˈhensɪv əˈsesmənt/"
+ },
+ "The phenomenon requires sophisticated analytical approaches": {
+ "user_language": "Le phénomène nécessite des approches analytiques sophistiquées",
+ "context": "phenomenon-analysis",
+ "pronunciation": "/ðə fɪˈnɒmɪnən rɪˈkwaɪərz səˈfɪstɪkeɪtɪd ˌænəˈlɪtɪkəl əˈprəʊtʃɪz/"
+ },
+ "Various phenomena demonstrate consistent patterns": {
+ "user_language": "Divers phénomènes démontrent des modèles cohérents",
+ "context": "pattern-identification",
+ "pronunciation": "/ˈvɛəriəs fɪˈnɒmɪnə ˈdemənstreɪt kənˈsɪstənt ˈpætənz/"
+ },
+ "Assessment procedures evaluate participant performance": {
+ "user_language": "Les procédures d'évaluation évaluent la performance des participants",
+ "context": "performance-evaluation",
+ "pronunciation": "/əˈsesmənt prəˈsiːdʒərz ɪˈvæljueɪt pɑːˈtɪsɪpənt pəˈfɔːməns/"
+ },
+ "Comprehensive evaluation examines multiple dimensions": {
+ "user_language": "L'évaluation complète examine plusieurs dimensions",
+ "context": "multidimensional-assessment",
+ "pronunciation": "/ˌkɒmprɪˈhensɪv ɪˌvæljuˈeɪʃən ɪɡˈzæmɪnz ˈmʌltɪpəl daɪˈmenʃənz/"
+ },
+ "Interpretation depends on theoretical frameworks": {
+ "user_language": "L'interprétation dépend des cadres théoriques",
+ "context": "theoretical-interpretation",
+ "pronunciation": "/ɪnˌtɜːprɪˈteɪʃən dɪˈpendz ɒn ˌθiːəˈretɪkəl ˈfreɪmwɜːks/"
+ },
+ "Conceptualization provides foundation for investigation": {
+ "user_language": "La conceptualisation fournit le fondement de l'investigation",
+ "context": "conceptual-framework",
+ "pronunciation": "/kənˌseptʃuəlaɪˈzeɪʃən prəˈvaɪdz faʊnˈdeɪʃən fɔːr ɪnˌvestɪˈɡeɪʃən/"
+ },
+ "Epistemological assumptions influence research design": {
+ "user_language": "Les hypothèses épistémologiques influencent le design de recherche",
+ "context": "epistemology",
+ "pronunciation": "/ɪˌpɪstɪməˈlɒdʒɪkəl əˈsʌmpʃənz ˈɪnfluəns rɪˈsɜːtʃ dɪˈzaɪn/"
+ },
+ "Ontological considerations determine reality conceptualization": {
+ "user_language": "Les considérations ontologiques déterminent la conceptualisation de la réalité",
+ "context": "ontology",
+ "pronunciation": "/ˌɒntəˈlɒdʒɪkəl kənˌsɪdəˈreɪʃənz dɪˈtɜːmɪn riˈæləti kənˌseptʃuəlaɪˈzeɪʃən/"
+ },
+ "Phenomenology examines consciousness structures": {
+ "user_language": "La phénoménologie examine les structures de conscience",
+ "context": "phenomenology",
+ "pronunciation": "/fɪˌnɒmɪˈnɒlədʒi ɪɡˈzæmɪnz ˈkɒnʃəsnəs ˈstrʌktʃərz/"
+ },
+ "Hermeneutics focuses on textual interpretation": {
+ "user_language": "L'herméneutique se concentre sur l'interprétation textuelle",
+ "context": "hermeneutics",
+ "pronunciation": "/ˌhɜːmɪˈnjuːtɪks ˈfəʊkəsɪz ɒn ˈtekstʃuəl ɪnˌtɜːprɪˈteɪʃən/"
+ },
+ "Positivism emphasizes empirical observation": {
+ "user_language": "Le positivisme met l'accent sur l'observation empirique",
+ "context": "positivism",
+ "pronunciation": "/ˈpɒzɪtɪvɪzəm ˈemfəsaɪzɪz ɪmˈpɪrɪkəl ˌɒbzəˈveɪʃən/"
+ },
+ "Constructivism recognizes subjective meaning-making": {
+ "user_language": "Le constructivisme reconnaît la création de sens subjective",
+ "context": "constructivism",
+ "pronunciation": "/kənˈstrʌktɪvɪzəm ˈrekəɡnaɪzɪz səbˈdʒektɪv ˈmiːnɪŋ meɪkɪŋ/"
+ },
+ "Pragmatism values practical utility": {
+ "user_language": "Le pragmatisme valorise l'utilité pratique",
+ "context": "pragmatism",
+ "pronunciation": "/ˈpræɡmətɪzəm ˈvæljuːz ˈpræktɪkəl juːˈtɪləti/"
+ },
+ "Investigation requires systematic planning": {
+ "user_language": "L'investigation nécessite une planification systématique",
+ "context": "research-planning",
+ "pronunciation": "/ɪnˌvestɪˈɡeɪʃən rɪˈkwaɪərz ˌsɪstəˈmætɪk ˈplænɪŋ/"
+ },
+ "Inquiry processes involve questioning": {
+ "user_language": "Les processus d'enquête impliquent le questionnement",
+ "context": "inquiry",
+ "pronunciation": "/ɪnˈkwaɪəri ˈprəʊsesɪz ɪnˈvɒlv ˈkwestʃənɪŋ/"
+ },
+ "Exploration reveals previously unknown patterns": {
+ "user_language": "L'exploration révèle des modèles précédemment inconnus",
+ "context": "exploratory-research",
+ "pronunciation": "/ˌekspləˈreɪʃən rɪˈviːlz ˈpriːviəsli ʌnˈnəʊn ˈpætənz/"
+ },
+ "Mixed-methods approaches integrate quantitative and qualitative data": {
+ "user_language": "Les approches de méthodes mixtes intègrent les données quantitatives et qualitatives",
+ "context": "mixed-methods",
+ "pronunciation": "/mɪkst ˈmeθədz əˈprəʊtʃɪz ˈɪntɪɡreɪt ˈkwɒntɪtətɪv ænd ˈkwɒlɪtətɪv ˈdeɪtə/"
+ },
+ "Contemporary methodology emphasizes transparency and reproducibility": {
+ "user_language": "La méthodologie contemporaine met l'accent sur la transparence et la reproductibilité",
+ "context": "open-science",
+ "pronunciation": "/kənˈtempərəri ˌmeθəˈdɒlədʒi ˈemfəsaɪzɪz trænsˈpærənsi ænd ˌriːprəˌdjuːsəˈbɪləti/"
+ },
+ "Interdisciplinary collaboration requires methodological flexibility": {
+ "user_language": "La collaboration interdisciplinaire nécessite une flexibilité méthodologique",
+ "context": "interdisciplinary-research",
+ "pronunciation": "/ˌɪntədɪsəˈplɪnəri kəˌlæbəˈreɪʃən rɪˈkwaɪərz ˌmeθədəˈlɒdʒɪkəl ˌfleksəˈbɪləti/"
+ }
},
"grammar": {
diff --git a/index.html b/index.html
index a74efc7..720e908 100644
--- a/index.html
+++ b/index.html
@@ -83,6 +83,9 @@
// Global navigation state
let currentBookId = null;
let currentChapterId = null;
+ // Also expose globally for progress tracking
+ window.currentBookId = null;
+ window.currentChapterId = null;
// Initialize ContentLoader and make it global
const contentLoader = new ContentLoader();
@@ -263,6 +266,7 @@
eventBus.on('navigation:chapters', async (event) => {
const bookId = event.data.path.split('/')[2];
currentBookId = bookId;
+ window.currentBookId = bookId;
try {
// Load content using ContentLoader to get reports
@@ -1931,6 +1935,81 @@
}
};
+ // Force Phrase Module (temporary testing)
+ window.forcePhraseModule = async function() {
+ try {
+ console.log('🔧 Forcing Phrase Module to load...');
+
+ // Get UnifiedDRS instance directly (not SmartPreviewOrchestrator)
+ const unifiedDRS = window.app?.getCore()?.moduleLoader?.getModule('unifiedDRS');
+ if (!unifiedDRS) {
+ console.error('❌ UnifiedDRS not found. Make sure DRS is initialized.');
+ console.log('Available modules:', Object.keys(window.app?.getCore()?.moduleLoader?._modules || {}));
+ return;
+ }
+
+ // Get current chapter content
+ const contentLoader = window.app?.getCore()?.moduleLoader?.getModule('contentLoader');
+ if (!contentLoader) {
+ console.error('❌ ContentLoader not found.');
+ return;
+ }
+
+ const chapterContent = await contentLoader.getContent(currentChapterId || 'test-heavy-stress');
+
+ // Check if chapter has phrases
+ if (!chapterContent?.phrases || Object.keys(chapterContent.phrases).length === 0) {
+ console.error('❌ No phrases found in current chapter.');
+ console.log('Available content types:', Object.keys(chapterContent || {}));
+ return;
+ }
+
+ // Get first phrase
+ const phraseKeys = Object.keys(chapterContent.phrases);
+ const firstPhraseKey = phraseKeys[0];
+ const firstPhrase = chapterContent.phrases[firstPhraseKey];
+
+ console.log(`✅ Found ${phraseKeys.length} phrases in chapter`);
+ console.log(`🎯 Loading first phrase: "${firstPhraseKey}"`);
+
+ // Create exercise config for phrase
+ const exerciseConfig = {
+ type: 'phrase',
+ phraseKey: firstPhraseKey,
+ phrase: {
+ id: firstPhraseKey,
+ english: firstPhraseKey,
+ user_language: firstPhrase.user_language,
+ context: firstPhrase.context,
+ pronunciation: firstPhrase.pronunciation
+ },
+ chapterContent: chapterContent,
+ metadata: {
+ forced: true,
+ testMode: true
+ }
+ };
+
+ console.log('📦 Exercise config:', exerciseConfig);
+
+ // Get the DRS container
+ const container = document.getElementById('drs-exercise-container');
+ if (!container) {
+ console.error('❌ DRS container not found');
+ return;
+ }
+
+ // Force start the exercise using UnifiedDRS.start()
+ await unifiedDRS.start(container, exerciseConfig);
+
+ console.log('✅ Phrase module loaded successfully!');
+
+ } catch (error) {
+ console.error('❌ Failed to force phrase module:', error);
+ console.error(error.stack);
+ }
+ };
+
// Test AI provider connectivity
window.testAIConnectivity = async function() {
try {
@@ -2075,8 +2154,24 @@
return;
}
- // Get IntelligentSequencer from the application
+ // Update global state for progress tracking
+ currentBookId = bookId;
+ currentChapterId = chapterId;
+ window.currentBookId = bookId;
+ window.currentChapterId = chapterId;
+ console.log(`📚 Smart Guide using: ${bookId}/${chapterId}`);
+
+ // Get modules from the application
const moduleLoader = window.app.getCore().moduleLoader;
+
+ // Load chapter data for progress tracking
+ const contentLoader = moduleLoader.getModule('contentLoader');
+ if (contentLoader) {
+ window.currentChapterData = await contentLoader.getContent(chapterId);
+ console.log(`📊 Chapter data loaded for progress tracking: ${Object.keys(window.currentChapterData.vocabulary || {}).length} words`);
+ }
+
+ // Get IntelligentSequencer
const intelligentSequencer = moduleLoader.getModule('intelligentSequencer');
if (!intelligentSequencer) {
@@ -2186,8 +2281,13 @@
// Helper function to get current book ID from chapter ID
window.getCurrentBookId = function() {
+ // Use the real currentBookId if available (set when loading chapters)
+ if (window.currentBookId) {
+ return window.currentBookId;
+ }
+
+ // Fallback: try to parse from chapterId (may be inaccurate for multi-part book IDs)
const chapterId = window.currentChapterId || 'sbs-7-8';
- // Parse chapter ID to extract book (e.g., "sbs-7-8" -> "sbs")
const parts = chapterId.split('-');
return parts[0] || 'sbs';
};
@@ -2294,13 +2394,48 @@
const masteredCount = allWords.filter(word => combinedMastered.has(word)).length;
const masteredWordsList = allWords.filter(word => combinedMastered.has(word));
+ // Count other content types
+ const totalPhrases = Object.keys(content.phrases || {}).length;
+ const totalLessons = Object.keys(content.lessons || {}).length;
+ const totalDialogs = Array.isArray(content.dialogs) ? content.dialogs.length : Object.keys(content.dialogs || {}).length;
+ const totalGrammar = Object.keys(content.grammar || {}).length;
+ const totalAudio = Object.keys(content.audio || {}).length;
+ const totalImages = Object.keys(content.images || {}).length;
+
+ // TODO: Load completion data for other content types
+ const completedPhrases = 0; // TODO: Track phrase completion
+ const completedLessons = 0; // TODO: Track lesson completion
+ const completedDialogs = 0; // TODO: Track dialog completion
+ const completedGrammar = 0; // TODO: Track grammar completion
+
const result = {
total: vocabCount,
discovered: discoveredCount,
mastered: masteredCount,
discoveredPercentage: vocabCount > 0 ? Math.round((discoveredCount / vocabCount) * 100) : 0,
masteredPercentage: vocabCount > 0 ? Math.round((masteredCount / vocabCount) * 100) : 0,
- masteredWordsList: masteredWordsList
+ masteredWordsList: masteredWordsList,
+ // Additional content types
+ phrases: {
+ total: totalPhrases,
+ completed: completedPhrases,
+ percentage: totalPhrases > 0 ? Math.round((completedPhrases / totalPhrases) * 100) : 0
+ },
+ lessons: {
+ total: totalLessons,
+ completed: completedLessons,
+ percentage: totalLessons > 0 ? Math.round((completedLessons / totalLessons) * 100) : 0
+ },
+ dialogs: {
+ total: totalDialogs,
+ completed: completedDialogs,
+ percentage: totalDialogs > 0 ? Math.round((completedDialogs / totalDialogs) * 100) : 0
+ },
+ grammar: {
+ total: totalGrammar,
+ completed: completedGrammar,
+ percentage: totalGrammar > 0 ? Math.round((completedGrammar / totalGrammar) * 100) : 0
+ }
};
console.log('✅ Combined progress calculated:', result);
@@ -2338,26 +2473,77 @@
const persistedData = await loadPersistedVocabularyData(currentChapterId);
console.log('📁 Fresh persisted data loaded:', persistedData);
- // Get prerequisite engine from current session
+ // Create a temporary PrerequisiteEngine for the modal
let prerequisiteEngine = null;
try {
const moduleLoader = window.app.getCore().moduleLoader;
+
+ // Try to get PrerequisiteEngine from SmartPreviewOrchestrator first
const orchestrator = moduleLoader.getModule('smartPreviewOrchestrator');
if (orchestrator) {
- prerequisiteEngine = orchestrator.sharedServices?.prerequisiteEngine || orchestrator.prerequisiteEngine;
- console.log('🔗 PrerequisiteEngine connected:', !!prerequisiteEngine);
+ const sharedServices = orchestrator.getSharedServices();
+ prerequisiteEngine = sharedServices.prerequisiteEngine;
+ console.log('🔗 PrerequisiteEngine from SmartPreviewOrchestrator:', !!prerequisiteEngine);
+ }
+
+ // If not available, create a temporary one
+ if (!prerequisiteEngine) {
+ console.log('🔧 Creating temporary PrerequisiteEngine for modal...');
+ const { default: PrerequisiteEngine } = await import('./src/DRS/services/PrerequisiteEngine.js');
+ prerequisiteEngine = new PrerequisiteEngine();
+ console.log('✅ Temporary PrerequisiteEngine created');
+ }
+
+ // Always ensure chapter is analyzed
+ if (prerequisiteEngine) {
+ // Load chapter content
+ const contentLoader = moduleLoader.getModule('contentLoader');
+ if (contentLoader) {
+ console.log('📥 Loading chapter content for:', currentChapterId);
+ const chapterContent = await contentLoader.getContent(currentChapterId);
+ console.log('📦 Chapter content loaded:', {
+ vocabulary: Object.keys(chapterContent.vocabulary || {}).length,
+ phrases: Object.keys(chapterContent.phrases || {}).length,
+ dialogs: Object.keys(chapterContent.dialogs || {}).length,
+ texts: chapterContent.texts?.length || 0,
+ audio: chapterContent.audio?.length || 0,
+ images: chapterContent.images?.length || 0,
+ grammar: Object.keys(chapterContent.grammar || {}).length
+ });
+
+ // Analyze chapter to populate contentAnalysis
+ console.log('🔬 Analyzing chapter...');
+ const analysis = prerequisiteEngine.analyzeChapter(chapterContent);
+ console.log('✅ Chapter analyzed:', analysis);
+ console.log('📊 contentAnalysis.phrases:', prerequisiteEngine.contentAnalysis?.phrases);
+ console.log('📊 contentAnalysis.dialogs:', prerequisiteEngine.contentAnalysis?.dialogs);
+ console.log('📊 contentAnalysis.texts:', prerequisiteEngine.contentAnalysis?.texts);
+ } else {
+ console.error('❌ ContentLoader not available');
+ }
}
} catch (error) {
- console.log('Could not connect to PrerequisiteEngine:', error);
+ console.error('❌ Error setting up PrerequisiteEngine:', error);
}
// Calculate fresh combined progress
const vocabularyProgress = await calculateVocabularyProgress(currentChapterId, persistedData, prerequisiteEngine);
console.log('📊 Fresh vocabulary progress:', vocabularyProgress);
+ // Get mastery progress from PrerequisiteEngine (includes all content types)
+ const masteryProgress = prerequisiteEngine ? prerequisiteEngine.getMasteryProgress() : {};
+ console.log('📊 Mastery progress from PrerequisiteEngine:', masteryProgress);
+ console.log('🔍 PrerequisiteEngine.contentAnalysis:', prerequisiteEngine?.contentAnalysis);
+
// Prepare data for modal
const progress = {
- vocabulary: vocabularyProgress
+ vocabulary: vocabularyProgress,
+ phrases: masteryProgress.phrases || { total: 0, mastered: 0, percentage: 0 },
+ texts: masteryProgress.texts || { total: 0, mastered: 0, percentage: 0 },
+ dialogs: masteryProgress.dialogs || { total: 0, mastered: 0, percentage: 0 },
+ audio: masteryProgress.audio || { total: 0, mastered: 0, percentage: 0 },
+ images: masteryProgress.images || { total: 0, mastered: 0, percentage: 0 },
+ grammar: masteryProgress.grammar || { total: 0, mastered: 0, percentage: 0 }
};
const masteredWords = vocabularyProgress.masteredWordsList;
@@ -2405,18 +2591,46 @@
${progress.vocabulary?.mastered || 0}/${progress.vocabulary?.total || 0} words (${progress.vocabulary?.masteredPercentage || 0}%)
-
Phrases
+
📝 Phrases
-
${progress.phrases?.mastered || 0}/${progress.phrases?.total || 0} phrases
+
${progress.phrases?.mastered || 0}/${progress.phrases?.total || 0} phrases (${progress.phrases?.percentage || 0}%)
-
Grammar
+
📄 Texts
-
${progress.grammar?.mastered || 0}/${progress.grammar?.total || 0} concepts
+
${progress.texts?.mastered || 0}/${progress.texts?.total || 0} texts (${progress.texts?.percentage || 0}%)
+
+
+
💬 Dialogs
+
+
${progress.dialogs?.mastered || 0}/${progress.dialogs?.total || 0} dialogs (${progress.dialogs?.percentage || 0}%)
+
+
+
🎧 Audio
+
+
${progress.audio?.mastered || 0}/${progress.audio?.total || 0} audio (${progress.audio?.percentage || 0}%)
+
+
+
🖼️ Images
+
+
${progress.images?.mastered || 0}/${progress.images?.total || 0} images (${progress.images?.percentage || 0}%)
+
+
+
📚 Grammar
+
+
${progress.grammar?.mastered || 0}/${progress.grammar?.total || 0} concepts (${progress.grammar?.percentage || 0}%)
@@ -2514,17 +2728,85 @@
}
};
- window.updateProgressBar = function(current, total) {
+ // Global chapter data cache
+ window.currentChapterData = null;
+
+ // Helper to get real content coverage percentage
+ window.getRealContentCoverage = async function() {
+ try {
+ if (!window.currentBookId || !window.currentChapterId || !window.currentChapterData) {
+ console.log('⚠️ Missing required data for coverage calculation');
+ return null;
+ }
+
+ const chapterData = window.currentChapterData;
+
+ // Load progress data
+ const { default: VocabularyProgressManager } = await import('./src/DRS/services/VocabularyProgressManager.js');
+ const progressManager = new VocabularyProgressManager();
+ const progressData = await progressManager.loadProgress(window.currentBookId, window.currentChapterId);
+
+ const discovered = Object.keys(progressData.discovered || {}).length;
+ const mastered = Object.keys(progressData.mastered || {}).length;
+
+ // Count all content types in chapter
+ const totalVocab = Object.keys(chapterData.vocabulary || {}).length;
+ const totalPhrases = Object.keys(chapterData.phrases || {}).length;
+ const totalLessons = Object.keys(chapterData.lessons || {}).length;
+ const totalDialogs = Array.isArray(chapterData.dialogs) ? chapterData.dialogs.length : Object.keys(chapterData.dialogs || {}).length;
+ const totalGrammar = Object.keys(chapterData.grammar || {}).length;
+ const totalAudio = Object.keys(chapterData.audio || {}).length;
+ const totalImages = Object.keys(chapterData.images || {}).length;
+
+ // Calculate weighted totals (from CLAUDE.md weights)
+ const totalWeight =
+ (totalVocab * 2) + // discovery (1) + mastery (1) = 2 per word
+ (totalPhrases * 6) +
+ (totalLessons * 15) +
+ (totalDialogs * 12) +
+ (totalGrammar * 6) +
+ (totalAudio * 12) +
+ (totalImages * 6);
+
+ // Calculate completed weight (for now, only vocabulary is tracked)
+ const completedWeight =
+ (discovered * 1) + // discovery = 1 point
+ (mastered * 1); // mastery = 1 point
+ // TODO: Add tracking for lessons, dialogs, grammar completion
+
+ const percentage = totalWeight > 0 ? Math.round((completedWeight / totalWeight) * 100) : 0;
+
+ console.log(`📊 Content inventory:`, {
+ vocab: totalVocab,
+ phrases: totalPhrases,
+ lessons: totalLessons,
+ dialogs: totalDialogs,
+ grammar: totalGrammar,
+ audio: totalAudio,
+ images: totalImages
+ });
+ console.log(`📊 Progress: ${completedWeight}/${totalWeight} points = ${percentage}%`);
+ console.log(`📊 Vocabulary: ${discovered} discovered, ${mastered} mastered`);
+
+ return percentage;
+ } catch (error) {
+ console.log('Could not get real content coverage:', error.message);
+ }
+ return null;
+ };
+
+ window.updateProgressBar = async function(current, total) {
const progressBar = document.getElementById('guide-progress-bar');
const progressText = document.getElementById('guide-progress-text');
+ // Get real content coverage from storage (async)
+ const percentage = await getRealContentCoverage() || 0;
+
if (progressBar) {
- const percentage = total > 0 ? (current / total) * 100 : 0;
progressBar.style.width = percentage + '%';
}
if (progressText) {
- const percentage = total > 0 ? Math.round((current / total) * 100) : 0;
progressText.textContent = `${percentage}% Content Coverage`;
}
};
@@ -2532,34 +2814,44 @@
window.updateVocabularyOverrideUI = function(originalExercise, overrideInfo) {
console.log('📚 Updating Smart Guide UI for vocabulary override');
+ // Distinguish between Word Discovery (passive) and Flashcards (active)
+ const isDiscovery = overrideInfo.mode === 'discovery';
+ const emoji = isDiscovery ? '📖' : '📚';
+ const typeName = isDiscovery ? 'Word Discovery' : 'Vocabulary Practice';
+ const modeName = isDiscovery ? 'Passive Learning' : 'Adaptive Flashcards';
+
// Update status with vocabulary override explanation
if (overrideInfo.missingWords && overrideInfo.missingWords.length > 0) {
// Content-based override: specific words needed
const wordList = overrideInfo.missingWords.slice(0, 3).join(', ');
const moreWords = overrideInfo.missingWords.length > 3 ? `... (${overrideInfo.missingWords.length - 3} more)` : '';
- updateGuideStatus(`📚 Vocabulary Practice (Required - Need: ${wordList}${moreWords})`);
+ updateGuideStatus(`${emoji} ${typeName} (Required - Need: ${wordList}${moreWords})`);
updateCurrentExerciseInfo({
- type: 'vocabulary',
+ type: isDiscovery ? 'word-discovery' : 'vocabulary',
difficulty: 'adaptive',
+ mode: modeName,
sessionPosition: originalExercise.sessionPosition,
totalInSession: originalExercise.totalInSession,
- reasoning: `Upcoming content requires ${overrideInfo.missingWords.length} undiscovered vocabulary words. Learning these words first: ${overrideInfo.missingWords.slice(0, 5).join(', ')}${overrideInfo.missingWords.length > 5 ? '...' : ''}`
+ reasoning: isDiscovery
+ ? `First-time exposure to ${overrideInfo.totalMissing || overrideInfo.missingWords.length} new words. Discover: ${overrideInfo.missingWords.slice(0, 5).join(', ')}${overrideInfo.missingWords.length > 5 ? '...' : ''}`
+ : `Upcoming content requires ${overrideInfo.missingWords.length} vocabulary words. Practice: ${overrideInfo.missingWords.slice(0, 5).join(', ')}${overrideInfo.missingWords.length > 5 ? '...' : ''}`
});
} else {
// Fallback to old percentage-based display
- updateGuideStatus(`📚 Vocabulary Practice (Required - ${overrideInfo.reason || 'Building foundation'})`);
+ updateGuideStatus(`${emoji} ${typeName} (Required - ${overrideInfo.reason || 'Building foundation'})`);
updateCurrentExerciseInfo({
- type: 'vocabulary',
+ type: isDiscovery ? 'word-discovery' : 'vocabulary',
difficulty: 'adaptive',
+ mode: modeName,
sessionPosition: originalExercise.sessionPosition,
totalInSession: originalExercise.totalInSession,
reasoning: overrideInfo.reason || `Vocabulary foundation required before ${originalExercise.type} exercises.`
});
}
- // Update progress bar to show vocabulary practice
+ // Update progress bar with real content coverage
updateProgressBar(originalExercise.sessionPosition, originalExercise.totalInSession);
};
@@ -2572,13 +2864,17 @@
infoContainer.style.display = 'block';
// Special handling for vocabulary exercises
- if (exercise.type === 'vocabulary') {
+ if (exercise.type === 'vocabulary' || exercise.type === 'word-discovery') {
+ const emoji = exercise.type === 'word-discovery' ? '📖' : '📚';
+ const typeName = exercise.type === 'word-discovery' ? 'Word Discovery' : 'Vocabulary Practice';
+ const mode = exercise.mode || (exercise.difficulty === 'adaptive' ? 'Adaptive Flashcards' : exercise.difficulty.charAt(0).toUpperCase() + exercise.difficulty.slice(1));
+
detailsElement.innerHTML = `
- Type: 📚 Vocabulary Practice
+ Type: ${emoji} ${typeName}
- Mode: ${exercise.difficulty === 'adaptive' ? 'Adaptive Flashcards' : exercise.difficulty.charAt(0).toUpperCase() + exercise.difficulty.slice(1)}
+ Mode: ${mode}
Position: ${exercise.sessionPosition}/${exercise.totalInSession}
diff --git a/saves/drs-progress-test-heavy-test-heavy-stress.json b/saves/drs-progress-test-heavy-test-heavy-stress.json
new file mode 100644
index 0000000..e401109
--- /dev/null
+++ b/saves/drs-progress-test-heavy-test-heavy-stress.json
@@ -0,0 +1,1202 @@
+{
+ "discovered": {
+ "methodology": {
+ "timestamp": "2025-10-09T05:32:55.531Z",
+ "difficulty": "unknown"
+ },
+ "hypothesis": {
+ "timestamp": "2025-10-09T05:32:55.531Z",
+ "difficulty": "unknown"
+ },
+ "analysis": {
+ "timestamp": "2025-10-09T05:32:55.531Z",
+ "difficulty": "unknown"
+ },
+ "synthesis": {
+ "timestamp": "2025-10-09T05:32:55.531Z",
+ "difficulty": "unknown"
+ },
+ "empirical": {
+ "timestamp": "2025-10-09T05:32:55.531Z",
+ "difficulty": "unknown"
+ },
+ "quantitative": {
+ "timestamp": "2025-10-09T05:32:55.531Z",
+ "difficulty": "unknown"
+ },
+ "qualitative": {
+ "timestamp": "2025-10-09T05:32:55.531Z",
+ "difficulty": "unknown"
+ },
+ "paradigm": {
+ "timestamp": "2025-10-09T05:32:55.531Z",
+ "difficulty": "unknown"
+ },
+ "theoretical": {
+ "timestamp": "2025-10-09T05:32:55.532Z",
+ "difficulty": "unknown"
+ },
+ "framework": {
+ "timestamp": "2025-10-09T05:32:55.532Z",
+ "difficulty": "unknown"
+ },
+ "variable": {
+ "timestamp": "2025-10-09T05:35:55.385Z",
+ "difficulty": "unknown"
+ },
+ "correlation": {
+ "timestamp": "2025-10-09T05:35:55.385Z",
+ "difficulty": "unknown"
+ },
+ "causation": {
+ "timestamp": "2025-10-09T05:35:55.385Z",
+ "difficulty": "unknown"
+ },
+ "statistical": {
+ "timestamp": "2025-10-09T05:35:55.385Z",
+ "difficulty": "unknown"
+ },
+ "significance": {
+ "timestamp": "2025-10-09T05:35:55.385Z",
+ "difficulty": "unknown"
+ },
+ "phenomenon": {
+ "timestamp": "2025-10-09T05:35:55.385Z",
+ "difficulty": "unknown"
+ },
+ "phenomena": {
+ "timestamp": "2025-10-09T05:35:55.385Z",
+ "difficulty": "unknown"
+ },
+ "criterion": {
+ "timestamp": "2025-10-09T05:35:55.385Z",
+ "difficulty": "unknown"
+ },
+ "criteria": {
+ "timestamp": "2025-10-09T05:35:55.385Z",
+ "difficulty": "unknown"
+ },
+ "assessment": {
+ "timestamp": "2025-10-09T05:35:55.385Z",
+ "difficulty": "unknown"
+ },
+ "evaluation": {
+ "timestamp": "2025-10-09T05:41:55.291Z",
+ "difficulty": "unknown"
+ },
+ "comprehension": {
+ "timestamp": "2025-10-09T05:41:55.291Z",
+ "difficulty": "unknown"
+ },
+ "interpretation": {
+ "timestamp": "2025-10-09T05:41:55.291Z",
+ "difficulty": "unknown"
+ },
+ "conceptualization": {
+ "timestamp": "2025-10-09T05:41:55.291Z",
+ "difficulty": "unknown"
+ },
+ "operationalization": {
+ "timestamp": "2025-10-09T05:41:55.291Z",
+ "difficulty": "unknown"
+ },
+ "epistemology": {
+ "timestamp": "2025-10-09T05:41:55.291Z",
+ "difficulty": "unknown"
+ },
+ "ontology": {
+ "timestamp": "2025-10-09T05:41:55.291Z",
+ "difficulty": "unknown"
+ },
+ "phenomenology": {
+ "timestamp": "2025-10-09T05:41:55.291Z",
+ "difficulty": "unknown"
+ },
+ "hermeneutics": {
+ "timestamp": "2025-10-09T05:41:55.291Z",
+ "difficulty": "unknown"
+ },
+ "positivism": {
+ "timestamp": "2025-10-09T05:41:55.291Z",
+ "difficulty": "unknown"
+ },
+ "constructivism": {
+ "timestamp": "2025-10-09T05:44:15.857Z",
+ "difficulty": "unknown"
+ },
+ "realism": {
+ "timestamp": "2025-10-09T05:44:15.858Z",
+ "difficulty": "unknown"
+ },
+ "pragmatism": {
+ "timestamp": "2025-10-09T05:44:15.858Z",
+ "difficulty": "unknown"
+ },
+ "research": {
+ "timestamp": "2025-10-09T05:44:15.858Z",
+ "difficulty": "unknown"
+ },
+ "investigation": {
+ "timestamp": "2025-10-09T05:44:15.858Z",
+ "difficulty": "unknown"
+ },
+ "inquiry": {
+ "timestamp": "2025-10-09T05:44:15.858Z",
+ "difficulty": "unknown"
+ },
+ "exploration": {
+ "timestamp": "2025-10-09T05:44:15.858Z",
+ "difficulty": "unknown"
+ },
+ "examination": {
+ "timestamp": "2025-10-09T05:44:15.858Z",
+ "difficulty": "unknown"
+ },
+ "observation": {
+ "timestamp": "2025-10-09T05:44:15.858Z",
+ "difficulty": "unknown"
+ },
+ "experiment": {
+ "timestamp": "2025-10-09T05:44:15.858Z",
+ "difficulty": "unknown"
+ },
+ "survey": {
+ "timestamp": "2025-10-09T05:47:05.915Z",
+ "difficulty": "unknown"
+ },
+ "interview": {
+ "timestamp": "2025-10-09T05:47:05.915Z",
+ "difficulty": "unknown"
+ },
+ "questionnaire": {
+ "timestamp": "2025-10-09T05:47:05.915Z",
+ "difficulty": "unknown"
+ },
+ "data": {
+ "timestamp": "2025-10-09T05:47:05.915Z",
+ "difficulty": "unknown"
+ },
+ "dataset": {
+ "timestamp": "2025-10-09T05:47:05.915Z",
+ "difficulty": "unknown"
+ },
+ "sample": {
+ "timestamp": "2025-10-09T05:47:05.915Z",
+ "difficulty": "unknown"
+ },
+ "population": {
+ "timestamp": "2025-10-09T05:47:05.915Z",
+ "difficulty": "unknown"
+ },
+ "participant": {
+ "timestamp": "2025-10-09T05:47:05.915Z",
+ "difficulty": "unknown"
+ },
+ "respondent": {
+ "timestamp": "2025-10-09T05:47:05.915Z",
+ "difficulty": "unknown"
+ },
+ "informant": {
+ "timestamp": "2025-10-09T05:47:05.916Z",
+ "difficulty": "unknown"
+ },
+ "subject": {
+ "timestamp": "2025-10-09T05:48:37.293Z",
+ "difficulty": "unknown"
+ },
+ "control": {
+ "timestamp": "2025-10-09T05:48:37.293Z",
+ "difficulty": "unknown"
+ },
+ "treatment": {
+ "timestamp": "2025-10-09T05:48:37.293Z",
+ "difficulty": "unknown"
+ },
+ "intervention": {
+ "timestamp": "2025-10-09T05:48:37.293Z",
+ "difficulty": "unknown"
+ },
+ "manipulation": {
+ "timestamp": "2025-10-09T05:48:37.293Z",
+ "difficulty": "unknown"
+ },
+ "randomization": {
+ "timestamp": "2025-10-09T05:48:37.293Z",
+ "difficulty": "unknown"
+ },
+ "bias": {
+ "timestamp": "2025-10-09T05:48:37.293Z",
+ "difficulty": "unknown"
+ },
+ "validity": {
+ "timestamp": "2025-10-09T05:48:37.293Z",
+ "difficulty": "unknown"
+ },
+ "reliability": {
+ "timestamp": "2025-10-09T05:48:37.293Z",
+ "difficulty": "unknown"
+ },
+ "generalizability": {
+ "timestamp": "2025-10-09T05:48:37.293Z",
+ "difficulty": "unknown"
+ },
+ "replicability": {
+ "timestamp": "2025-10-09T05:50:13.172Z",
+ "difficulty": "unknown"
+ },
+ "reproducibility": {
+ "timestamp": "2025-10-09T05:50:13.172Z",
+ "difficulty": "unknown"
+ },
+ "triangulation": {
+ "timestamp": "2025-10-09T05:50:13.172Z",
+ "difficulty": "unknown"
+ },
+ "verification": {
+ "timestamp": "2025-10-09T05:50:13.172Z",
+ "difficulty": "unknown"
+ },
+ "validation": {
+ "timestamp": "2025-10-09T05:50:13.172Z",
+ "difficulty": "unknown"
+ },
+ "calibration": {
+ "timestamp": "2025-10-09T05:50:13.172Z",
+ "difficulty": "unknown"
+ },
+ "standardization": {
+ "timestamp": "2025-10-09T05:50:13.172Z",
+ "difficulty": "unknown"
+ },
+ "normalization": {
+ "timestamp": "2025-10-09T05:50:13.172Z",
+ "difficulty": "unknown"
+ },
+ "coding": {
+ "timestamp": "2025-10-09T05:50:13.172Z",
+ "difficulty": "unknown"
+ },
+ "categorization": {
+ "timestamp": "2025-10-09T05:50:13.172Z",
+ "difficulty": "unknown"
+ },
+ "classification": {
+ "timestamp": "2025-10-09T05:50:25.781Z",
+ "difficulty": "unknown"
+ },
+ "taxonomy": {
+ "timestamp": "2025-10-09T05:50:25.781Z",
+ "difficulty": "unknown"
+ },
+ "typology": {
+ "timestamp": "2025-10-09T05:50:25.781Z",
+ "difficulty": "unknown"
+ },
+ "dimension": {
+ "timestamp": "2025-10-09T05:50:25.781Z",
+ "difficulty": "unknown"
+ },
+ "scale": {
+ "timestamp": "2025-10-09T05:50:25.781Z",
+ "difficulty": "unknown"
+ },
+ "metric": {
+ "timestamp": "2025-10-09T05:50:25.781Z",
+ "difficulty": "unknown"
+ },
+ "measurement": {
+ "timestamp": "2025-10-09T05:50:25.781Z",
+ "difficulty": "unknown"
+ },
+ "indicator": {
+ "timestamp": "2025-10-09T05:50:25.781Z",
+ "difficulty": "unknown"
+ },
+ "index": {
+ "timestamp": "2025-10-09T05:50:25.781Z",
+ "difficulty": "unknown"
+ },
+ "coefficient": {
+ "timestamp": "2025-10-09T05:50:25.781Z",
+ "difficulty": "unknown"
+ },
+ "regression": {
+ "timestamp": "2025-10-09T05:52:29.424Z",
+ "difficulty": "unknown"
+ },
+ "modeling": {
+ "timestamp": "2025-10-09T05:52:29.424Z",
+ "difficulty": "unknown"
+ },
+ "simulation": {
+ "timestamp": "2025-10-09T05:52:29.424Z",
+ "difficulty": "unknown"
+ },
+ "prediction": {
+ "timestamp": "2025-10-09T05:52:29.424Z",
+ "difficulty": "unknown"
+ },
+ "projection": {
+ "timestamp": "2025-10-09T05:52:29.424Z",
+ "difficulty": "unknown"
+ },
+ "extrapolation": {
+ "timestamp": "2025-10-09T05:52:29.424Z",
+ "difficulty": "unknown"
+ },
+ "interpolation": {
+ "timestamp": "2025-10-09T05:52:29.424Z",
+ "difficulty": "unknown"
+ },
+ "transformation": {
+ "timestamp": "2025-10-09T05:52:29.424Z",
+ "difficulty": "unknown"
+ },
+ "optimization": {
+ "timestamp": "2025-10-09T05:52:29.424Z",
+ "difficulty": "unknown"
+ },
+ "algorithm": {
+ "timestamp": "2025-10-09T05:52:29.424Z",
+ "difficulty": "unknown"
+ },
+ "computation": {
+ "timestamp": "2025-10-09T05:52:36.394Z",
+ "difficulty": "unknown"
+ },
+ "processing": {
+ "timestamp": "2025-10-09T05:52:36.394Z",
+ "difficulty": "unknown"
+ },
+ "distribution": {
+ "timestamp": "2025-10-09T05:52:36.394Z",
+ "difficulty": "unknown"
+ },
+ "frequency": {
+ "timestamp": "2025-10-09T05:52:36.394Z",
+ "difficulty": "unknown"
+ },
+ "probability": {
+ "timestamp": "2025-10-09T05:52:36.394Z",
+ "difficulty": "unknown"
+ },
+ "likelihood": {
+ "timestamp": "2025-10-09T05:52:36.394Z",
+ "difficulty": "unknown"
+ },
+ "confidence": {
+ "timestamp": "2025-10-09T05:52:36.394Z",
+ "difficulty": "unknown"
+ },
+ "interval": {
+ "timestamp": "2025-10-09T05:52:36.394Z",
+ "difficulty": "unknown"
+ },
+ "deviation": {
+ "timestamp": "2025-10-09T05:52:36.394Z",
+ "difficulty": "unknown"
+ },
+ "variance": {
+ "timestamp": "2025-10-09T05:52:36.394Z",
+ "difficulty": "unknown"
+ },
+ "median": {
+ "timestamp": "2025-10-09T05:52:43.111Z",
+ "difficulty": "unknown"
+ },
+ "mode": {
+ "timestamp": "2025-10-09T05:52:43.111Z",
+ "difficulty": "unknown"
+ },
+ "mean": {
+ "timestamp": "2025-10-09T05:52:43.111Z",
+ "difficulty": "unknown"
+ },
+ "average": {
+ "timestamp": "2025-10-09T05:52:43.111Z",
+ "difficulty": "unknown"
+ },
+ "outlier": {
+ "timestamp": "2025-10-09T05:52:43.111Z",
+ "difficulty": "unknown"
+ },
+ "anomaly": {
+ "timestamp": "2025-10-09T05:52:43.111Z",
+ "difficulty": "unknown"
+ },
+ "trend": {
+ "timestamp": "2025-10-09T05:52:43.111Z",
+ "difficulty": "unknown"
+ },
+ "pattern": {
+ "timestamp": "2025-10-09T05:52:43.111Z",
+ "difficulty": "unknown"
+ },
+ "cluster": {
+ "timestamp": "2025-10-09T05:52:43.111Z",
+ "difficulty": "unknown"
+ },
+ "segment": {
+ "timestamp": "2025-10-09T05:52:43.111Z",
+ "difficulty": "unknown"
+ },
+ "stratum": {
+ "timestamp": "2025-10-09T05:54:58.976Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989298976
+ },
+ "strata": {
+ "timestamp": "2025-10-09T05:54:58.976Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989298976
+ },
+ "cohort": {
+ "timestamp": "2025-10-09T05:54:58.976Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989298976
+ },
+ "longitudinal": {
+ "timestamp": "2025-10-09T05:54:58.976Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989298976
+ },
+ "cross-sectional": {
+ "timestamp": "2025-10-09T05:54:58.976Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989298976
+ },
+ "comparative": {
+ "timestamp": "2025-10-09T05:54:58.977Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989298977
+ },
+ "descriptive": {
+ "timestamp": "2025-10-09T05:54:58.977Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989298977
+ },
+ "exploratory": {
+ "timestamp": "2025-10-09T05:54:58.977Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989298977
+ },
+ "explanatory": {
+ "timestamp": "2025-10-09T05:54:58.977Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989298977
+ },
+ "confirmatory": {
+ "timestamp": "2025-10-09T05:54:58.977Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989298977
+ },
+ "experimental": {
+ "timestamp": "2025-10-09T05:55:08.256Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989308256
+ },
+ "quasi-experimental": {
+ "timestamp": "2025-10-09T05:55:08.256Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989308256
+ },
+ "observational": {
+ "timestamp": "2025-10-09T05:55:08.256Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989308256
+ },
+ "ethnographic": {
+ "timestamp": "2025-10-09T05:55:08.256Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989308256
+ },
+ "phenomenological": {
+ "timestamp": "2025-10-09T05:55:08.256Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989308256
+ },
+ "hermeneutical": {
+ "timestamp": "2025-10-09T05:55:08.256Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989308256
+ },
+ "grounded": {
+ "timestamp": "2025-10-09T05:55:08.256Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989308256
+ },
+ "narrative": {
+ "timestamp": "2025-10-09T05:55:08.256Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989308256
+ },
+ "discourse": {
+ "timestamp": "2025-10-09T05:55:08.257Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989308256
+ },
+ "rhetoric": {
+ "timestamp": "2025-10-09T05:55:08.257Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989308257
+ },
+ "argumentation": {
+ "timestamp": "2025-10-09T06:00:40.612Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989640612
+ },
+ "proposition": {
+ "timestamp": "2025-10-09T06:00:40.613Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989640612
+ },
+ "premise": {
+ "timestamp": "2025-10-09T06:00:40.613Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989640613
+ },
+ "conclusion": {
+ "timestamp": "2025-10-09T06:00:40.613Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989640613
+ },
+ "inference": {
+ "timestamp": "2025-10-09T06:00:40.613Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989640613
+ },
+ "deduction": {
+ "timestamp": "2025-10-09T06:00:40.613Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989640613
+ },
+ "induction": {
+ "timestamp": "2025-10-09T06:00:40.613Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989640613
+ },
+ "abduction": {
+ "timestamp": "2025-10-09T06:00:40.613Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989640613
+ },
+ "reasoning": {
+ "timestamp": "2025-10-09T06:00:40.613Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989640613
+ },
+ "logic": {
+ "timestamp": "2025-10-09T06:00:40.613Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989640613
+ },
+ "rationale": {
+ "timestamp": "2025-10-09T06:00:49.672Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989649672
+ },
+ "justification": {
+ "timestamp": "2025-10-09T06:00:49.672Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989649672
+ },
+ "substantiation": {
+ "timestamp": "2025-10-09T06:00:49.672Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989649672
+ },
+ "corroboration": {
+ "timestamp": "2025-10-09T06:00:49.672Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989649672
+ },
+ "refutation": {
+ "timestamp": "2025-10-09T06:00:49.672Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989649672
+ },
+ "contradiction": {
+ "timestamp": "2025-10-09T06:00:49.672Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989649672
+ },
+ "paradox": {
+ "timestamp": "2025-10-09T06:00:49.672Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989649672
+ },
+ "dilemma": {
+ "timestamp": "2025-10-09T06:00:49.672Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989649672
+ },
+ "ambiguity": {
+ "timestamp": "2025-10-09T06:00:49.672Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989649672
+ },
+ "uncertainty": {
+ "timestamp": "2025-10-09T06:00:49.672Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989649672
+ },
+ "complexity": {
+ "timestamp": "2025-10-09T06:02:42.076Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989762076
+ },
+ "sophisticated": {
+ "timestamp": "2025-10-09T06:02:42.076Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989762076
+ },
+ "nuanced": {
+ "timestamp": "2025-10-09T06:02:42.076Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989762076
+ },
+ "multifaceted": {
+ "timestamp": "2025-10-09T06:02:42.076Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989762076
+ },
+ "comprehensive": {
+ "timestamp": "2025-10-09T06:02:42.076Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989762076
+ },
+ "systematic": {
+ "timestamp": "2025-10-09T06:02:42.077Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989762077
+ },
+ "rigorous": {
+ "timestamp": "2025-10-09T06:02:42.077Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989762077
+ },
+ "meticulous": {
+ "timestamp": "2025-10-09T06:02:42.077Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989762077
+ },
+ "precise": {
+ "timestamp": "2025-10-09T06:02:42.077Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989762077
+ },
+ "accurate": {
+ "timestamp": "2025-10-09T06:02:42.077Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989762077
+ },
+ "robust": {
+ "timestamp": "2025-10-09T06:02:48.696Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989768696
+ },
+ "substantial": {
+ "timestamp": "2025-10-09T06:02:48.696Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989768696
+ },
+ "significant": {
+ "timestamp": "2025-10-09T06:02:48.696Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989768696
+ },
+ "considerable": {
+ "timestamp": "2025-10-09T06:02:48.696Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989768696
+ },
+ "extensive": {
+ "timestamp": "2025-10-09T06:02:48.696Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989768696
+ },
+ "intensive": {
+ "timestamp": "2025-10-09T06:02:48.696Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989768696
+ },
+ "exhaustive": {
+ "timestamp": "2025-10-09T06:02:48.696Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989768696
+ },
+ "thorough": {
+ "timestamp": "2025-10-09T06:02:48.696Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989768696
+ },
+ "detailed": {
+ "timestamp": "2025-10-09T06:02:48.696Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989768696
+ },
+ "intricate": {
+ "timestamp": "2025-10-09T06:02:48.696Z",
+ "difficulty": "unknown",
+ "sessionId": 1759989768696
+ },
+ "elaborate": {
+ "timestamp": "2025-10-09T06:11:18.813Z",
+ "difficulty": "unknown",
+ "sessionId": 1759990278812
+ }
+ },
+ "mastered": {
+ "hypothesis": {
+ "timestamp": "2025-10-09T05:36:03.512Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "methodology": {
+ "timestamp": "2025-10-09T05:36:04.081Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "empirical": {
+ "timestamp": "2025-10-09T05:36:04.444Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "analysis": {
+ "timestamp": "2025-10-09T05:36:04.765Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "synthesis": {
+ "timestamp": "2025-10-09T05:36:05.059Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "qualitative": {
+ "timestamp": "2025-10-09T05:36:06.931Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "quantitative": {
+ "timestamp": "2025-10-09T05:36:07.246Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "theoretical": {
+ "timestamp": "2025-10-09T05:36:07.559Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "framework": {
+ "timestamp": "2025-10-09T05:36:07.878Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "paradigm": {
+ "timestamp": "2025-10-09T05:36:08.182Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "statistical": {
+ "timestamp": "2025-10-09T05:36:10.392Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "variable": {
+ "timestamp": "2025-10-09T05:36:10.712Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "causation": {
+ "timestamp": "2025-10-09T05:36:11.031Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "correlation": {
+ "timestamp": "2025-10-09T05:36:11.357Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "significance": {
+ "timestamp": "2025-10-09T05:36:11.680Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "phenomenon": {
+ "timestamp": "2025-10-09T05:42:01.025Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "evaluation": {
+ "timestamp": "2025-10-09T05:42:01.571Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "positivism": {
+ "timestamp": "2025-10-09T05:42:01.933Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "epistemology": {
+ "timestamp": "2025-10-09T05:42:02.247Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "phenomenology": {
+ "timestamp": "2025-10-09T05:42:02.574Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "conceptualization": {
+ "timestamp": "2025-10-09T05:42:11.122Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "interpretation": {
+ "timestamp": "2025-10-09T05:42:11.469Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "operationalization": {
+ "timestamp": "2025-10-09T05:42:11.783Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "hermeneutics": {
+ "timestamp": "2025-10-09T05:42:12.096Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "criterion": {
+ "timestamp": "2025-10-09T05:42:12.406Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "phenomena": {
+ "timestamp": "2025-10-09T05:42:23.086Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "criteria": {
+ "timestamp": "2025-10-09T05:42:23.449Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "assessment": {
+ "timestamp": "2025-10-09T05:42:23.776Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "ontology": {
+ "timestamp": "2025-10-09T05:42:24.092Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "comprehension": {
+ "timestamp": "2025-10-09T05:42:24.409Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "exploration": {
+ "timestamp": "2025-10-09T05:44:21.115Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "investigation": {
+ "timestamp": "2025-10-09T05:44:21.415Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "observation": {
+ "timestamp": "2025-10-09T05:44:21.751Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "examination": {
+ "timestamp": "2025-10-09T05:44:22.038Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "realism": {
+ "timestamp": "2025-10-09T05:44:22.355Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "survey": {
+ "timestamp": "2025-10-09T05:47:10.768Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "interview": {
+ "timestamp": "2025-10-09T05:47:11.103Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "data": {
+ "timestamp": "2025-10-09T05:47:11.434Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "participant": {
+ "timestamp": "2025-10-09T05:47:11.743Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "sample": {
+ "timestamp": "2025-10-09T05:47:12.066Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "intervention": {
+ "timestamp": "2025-10-09T05:48:40.664Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "reliability": {
+ "timestamp": "2025-10-09T05:48:41.008Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "validity": {
+ "timestamp": "2025-10-09T05:48:41.342Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "population": {
+ "timestamp": "2025-10-09T05:48:41.692Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "dataset": {
+ "timestamp": "2025-10-09T05:48:42.121Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "replicability": {
+ "timestamp": "2025-10-09T05:50:17.064Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "questionnaire": {
+ "timestamp": "2025-10-09T05:50:17.573Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "generalizability": {
+ "timestamp": "2025-10-09T05:50:17.920Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "validation": {
+ "timestamp": "2025-10-09T05:50:18.248Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "manipulation": {
+ "timestamp": "2025-10-09T05:50:18.571Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "coefficient": {
+ "timestamp": "2025-10-09T05:50:34.737Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "metric": {
+ "timestamp": "2025-10-09T05:50:35.066Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "treatment": {
+ "timestamp": "2025-10-09T05:50:35.376Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "bias": {
+ "timestamp": "2025-10-09T05:50:35.673Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ },
+ "informant": {
+ "timestamp": "2025-10-09T05:50:35.980Z",
+ "scores": [
+ 80
+ ],
+ "difficulty": "good",
+ "masteryLevel": 1
+ }
+ },
+ "metadata": {
+ "totalWords": 0,
+ "sessionCount": 0,
+ "lastUpdate": "2025-10-09T06:11:20.818Z",
+ "version": "1.0"
+ },
+ "system": "drs",
+ "bookId": "test-heavy",
+ "chapterId": "test-heavy-stress",
+ "savedAt": "2025-10-09T06:11:20.820Z",
+ "version": "1.0"
+}
\ No newline at end of file
diff --git a/src/DRS/UnifiedDRS.js b/src/DRS/UnifiedDRS.js
index 99726f2..e4eff34 100644
--- a/src/DRS/UnifiedDRS.js
+++ b/src/DRS/UnifiedDRS.js
@@ -98,6 +98,9 @@ class UnifiedDRS extends Module {
// Config storage for vocabulary override detection
this._lastConfig = null;
+ // Shared PrerequisiteEngine (to avoid creating multiple instances)
+ this._sharedPrerequisiteEngine = null;
+
Object.seal(this);
}
@@ -177,7 +180,7 @@ class UnifiedDRS extends Module {
const exerciseType = exerciseConfig.type || this._config.exerciseTypes[0];
// Priority 1: Check if we need word discovery first (first-time exposure)
- if (this._shouldUseWordDiscovery(exerciseType, exerciseConfig)) {
+ if (await this._shouldUseWordDiscovery(exerciseType, exerciseConfig)) {
console.log(`📖 Using Word Discovery for ${exerciseType} (first-time word exposure)`);
await this._loadWordDiscoveryModule(exerciseType, exerciseConfig);
return;
@@ -873,7 +876,7 @@ class UnifiedDRS extends Module {
/**
* Check if we should use Word Discovery system first
*/
- _shouldUseWordDiscovery(exerciseType, config) {
+ async _shouldUseWordDiscovery(exerciseType, config) {
// Always use word discovery for vocabulary-discovery type
if (exerciseType === 'vocabulary-discovery') {
return true;
@@ -881,30 +884,67 @@ class UnifiedDRS extends Module {
// Force word discovery if there are undiscovered words
try {
- const moduleLoader = window.app.getCore().moduleLoader;
- const orchestrator = moduleLoader.getModule('smartPreviewOrchestrator');
+ // Get or reuse shared PrerequisiteEngine
+ let prerequisiteEngine = null;
- if (orchestrator && orchestrator.prerequisiteEngine) {
- const prerequisiteEngine = orchestrator.prerequisiteEngine;
+ try {
+ const moduleLoader = window.app.getCore().moduleLoader;
+ const orchestrator = moduleLoader.getModule('smartPreviewOrchestrator');
+ prerequisiteEngine = orchestrator?.sharedServices?.prerequisiteEngine;
+ } catch (error) {
+ console.log('Could not get prerequisiteEngine from orchestrator:', error.message);
+ }
- // Check if there are undiscovered words in the current chapter
- const chapterContent = config.chapterContent || {};
- if (chapterContent.vocabulary) {
- const allWords = Object.keys(chapterContent.vocabulary);
- const undiscoveredWords = allWords.filter(word =>
- !prerequisiteEngine.isDiscovered(word)
- );
+ // Use shared instance if available, create if needed
+ if (!prerequisiteEngine) {
+ if (this._sharedPrerequisiteEngine) {
+ console.log('♻️ Reusing shared PrerequisiteEngine for word discovery check');
+ prerequisiteEngine = this._sharedPrerequisiteEngine;
+ } else {
+ console.log('🔧 Creating shared PrerequisiteEngine for word discovery check');
+ const { default: PrerequisiteEngine } = await import('./services/PrerequisiteEngine.js');
+ prerequisiteEngine = new PrerequisiteEngine();
+ await prerequisiteEngine.init(config.bookId, config.chapterId);
+ this._sharedPrerequisiteEngine = prerequisiteEngine;
+ console.log('✅ Shared PrerequisiteEngine created and stored');
+ }
+ }
- console.log(`📖 Found ${undiscoveredWords.length} undiscovered words`);
+ // Load chapter content if not already loaded
+ let chapterContent = config.chapterContent;
+ if (!chapterContent && config.chapterId) {
+ console.log(`📚 Loading chapter content for word discovery check: ${config.chapterId}`);
+ chapterContent = await this._contentLoader.getContent(config.chapterId);
+ }
- if (undiscoveredWords.length > 0) {
- console.log(`🔄 Undiscovered words found, redirecting to word discovery`);
- return true;
- }
+ if (chapterContent && chapterContent.vocabulary) {
+ const allWords = Object.keys(chapterContent.vocabulary);
+ const undiscoveredWords = allWords.filter(word =>
+ !prerequisiteEngine.isDiscovered(word)
+ );
+
+ console.log(`📖 Word discovery check: ${undiscoveredWords.length}/${allWords.length} words undiscovered`);
+
+ if (undiscoveredWords.length > 0) {
+ console.log(`🔄 Forcing WordDiscovery for ${undiscoveredWords.length} undiscovered words`);
+
+ // Signal vocabulary override to Smart Guide UI
+ window.vocabularyOverrideActive = {
+ originalType: exerciseType,
+ reason: `Word Discovery required - ${undiscoveredWords.length} new words to discover`,
+ missingWords: undiscoveredWords.slice(0, 10), // Show first 10
+ totalMissing: undiscoveredWords.length,
+ mode: 'discovery' // Passive discovery, not flashcards
+ };
+ console.log('📖 Word Discovery override signaled to Smart Guide:', window.vocabularyOverrideActive);
+
+ return true;
+ } else {
+ console.log(`✅ All ${allWords.length} words already discovered, skipping WordDiscovery`);
}
}
} catch (error) {
- console.log('Could not check word discovery status:', error);
+ console.error('❌ Error checking word discovery status:', error);
}
return false;
@@ -967,7 +1007,7 @@ class UnifiedDRS extends Module {
console.log('📖 Extracted content for analysis:', textSources.length, 'text sources');
- // 4. Initialize dependency analyzer with enhanced persistence
+ // 4. Initialize dependency analyzer with shared PrerequisiteEngine
if (!this._dependencyAnalyzer) {
let prerequisiteEngine = null;
@@ -979,18 +1019,25 @@ class UnifiedDRS extends Module {
console.log('Could not get prerequisiteEngine from orchestrator:', error.message);
}
+ // Use shared instance if available, create if needed
if (!prerequisiteEngine) {
- console.log('🔧 Creating enhanced PrerequisiteEngine with persistence for dependency analysis');
- const { default: PrerequisiteEngine } = await import('./services/PrerequisiteEngine.js');
- prerequisiteEngine = new PrerequisiteEngine();
+ if (this._sharedPrerequisiteEngine) {
+ console.log('♻️ Reusing shared PrerequisiteEngine for dependency analysis');
+ prerequisiteEngine = this._sharedPrerequisiteEngine;
+ } else {
+ console.log('🔧 Creating shared PrerequisiteEngine for dependency analysis');
+ const { default: PrerequisiteEngine } = await import('./services/PrerequisiteEngine.js');
+ prerequisiteEngine = new PrerequisiteEngine();
- // Initialize with current chapter data
- await prerequisiteEngine.init(config.bookId, config.chapterId);
- console.log('✅ Enhanced PrerequisiteEngine initialized with persistent data');
+ // Initialize with current chapter data
+ await prerequisiteEngine.init(config.bookId, config.chapterId);
+ this._sharedPrerequisiteEngine = prerequisiteEngine;
+ console.log('✅ Shared PrerequisiteEngine created and stored');
+ }
}
this._dependencyAnalyzer = new ContentDependencyAnalyzer(prerequisiteEngine);
- console.log('🧠 ContentDependencyAnalyzer initialized with enhanced persistence');
+ console.log('🧠 ContentDependencyAnalyzer initialized with shared PrerequisiteEngine');
}
// 5. Create vocabulary module with real data
@@ -1297,7 +1344,15 @@ class UnifiedDRS extends Module {
this._components.nextButton = componentRegistry.create('Button', {
text: this._currentStep === this._totalSteps - 1 ? 'Finish' : 'Next',
type: 'primary',
- onClick: () => this._handleNext(),
+ onClick: (e) => {
+ console.log('🖱️ Next button clicked (from component)');
+ // Prevent default form submission
+ if (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ this._handleNext();
+ },
className: 'drs-next-btn'
});
@@ -1435,6 +1490,8 @@ class UnifiedDRS extends Module {
content += this._generateImageContent(step);
} else if (step.type === 'grammar' || step.type === 'fill-blank') {
content += this._generateGrammarContent(step);
+ } else if (step.type === 'phrase') {
+ content += this._generatePhraseContent(step);
} else {
// Fallback for unknown types
console.warn(`⚠️ Unknown step type: ${step.type}, falling back to text`);
@@ -1559,6 +1616,206 @@ class UnifiedDRS extends Module {
return content;
}
+ /**
+ * Generate phrase exercise content
+ */
+ _generatePhraseContent(step) {
+ // Use step.content if available (new format from ContentLoader)
+ const stepData = step.content || step;
+
+ console.log('💬 Generating phrase content:', stepData);
+
+ // Extract phrase data - handle nested structure (now comes from step.content.phrase)
+ let phraseData = stepData.phrase || stepData;
+
+ // Get the phrase text
+ const phraseText = phraseData.english || phraseData.text || phraseData.id || 'No phrase text';
+ const pronunciation = phraseData.pronunciation || '';
+ const context = phraseData.context || '';
+ const expectedTranslation = phraseData.user_language || phraseData.translation || '';
+
+ let content = `
+
+
+
+
+
💬
+
+
English Phrase
+
${phraseText}
+
+
+
+
+ ${pronunciation ? `
+
+ 🗣️
+ ${pronunciation}
+
+ ` : ''}
+
+
+ ${context ? `
+
+ 📌
+ Context:
+ ${context}
+
+ ` : ''}
+
+
+
+
+ ✍️
+ Your translation in French:
+
+
+
AI will validate your translation
+
+
+
+
+ ${expectedTranslation ? `
+
+ ${expectedTranslation}
+
+ ` : ''}
+
+
+
+ `;
+
+ return content;
+ }
+
/**
* Event handlers
*/
@@ -1566,17 +1823,18 @@ class UnifiedDRS extends Module {
console.log('📢 DRS start event received');
}
- _handleNext() {
- console.log('📋 Next button clicked');
+ async _handleNext() {
+ console.log('📋 ========== Next button clicked ==========');
+ console.log(' Active:', this._isActive);
+ console.log(' Step:', `${this._currentStep}/${this._totalSteps}`);
if (!this._isActive) {
console.log('⚠️ DRS not active, ignoring next');
return;
}
- console.log(`Current step: ${this._currentStep}/${this._totalSteps}`);
-
// Validate current step
+ console.log('🔍 Validating current step...');
const isValid = this._validateCurrentStep();
if (!isValid) {
console.log('❌ Validation failed, showing error');
@@ -1584,27 +1842,241 @@ class UnifiedDRS extends Module {
return;
}
- console.log('✅ Validation passed, moving to next step');
+ console.log('✅ Validation passed, showing loading overlay...');
- // Move to next step or finish
- if (this._currentStep < this._totalSteps - 1) {
- // Emit step completed (for the step we just finished)
- this._eventBus.emit('drs:step-completed', {
- step: this._currentStep, // The step we just completed
- total: this._totalSteps
- }, this.name);
+ // Show loading indicator during AI validation
+ const loadingOverlay = this._showAILoadingOverlay();
+ console.log(' Loading overlay created:', !!loadingOverlay);
- this._currentStep++;
- this._showCurrentStep();
- } else {
- // Emit final step completed
- this._eventBus.emit('drs:step-completed', {
- step: this._currentStep,
- total: this._totalSteps
- }, this.name);
+ try {
+ console.log('📊 Starting _storeStepResult (async)...');
+ // Store step results before moving on (now async for AI validation)
+ await this._storeStepResult();
+ console.log('✅ _storeStepResult completed!');
- this._finishExercise();
+ // Hide loading
+ console.log('🚫 Removing loading overlay...');
+ loadingOverlay.remove();
+
+ // Move to next step or finish
+ if (this._currentStep < this._totalSteps - 1) {
+ console.log('➡️ Moving to next step...');
+ // Emit step completed (for the step we just finished)
+ this._eventBus.emit('drs:step-completed', {
+ step: this._currentStep, // The step we just completed
+ total: this._totalSteps
+ }, this.name);
+
+ this._currentStep++;
+ this._showCurrentStep();
+ } else {
+ console.log('🏁 Finishing exercise...');
+ // Emit final step completed
+ this._eventBus.emit('drs:step-completed', {
+ step: this._currentStep,
+ total: this._totalSteps
+ }, this.name);
+
+ this._finishExercise();
+ }
+ } catch (error) {
+ console.error('❌ Error during validation:', error);
+ console.error(' Stack:', error.stack);
+ loadingOverlay.remove();
+ this._showValidationError();
}
+
+ console.log('📋 ========== _handleNext completed ==========');
+ }
+
+ _showAILoadingOverlay() {
+ const overlay = document.createElement('div');
+ overlay.className = 'drs-ai-loading-overlay';
+ overlay.innerHTML = `
+
+
+
+
🤖 AI Validation in Progress...
+
Analyzing your answer
+
+
+
+ `;
+ document.body.appendChild(overlay);
+ return overlay;
+ }
+
+ async _storeStepResult() {
+ // Initialize stepResults if not exists
+ if (!this._userProgress.stepResults) {
+ this._userProgress.stepResults = [];
+ }
+
+ const step = this._currentExercise.steps?.[this._currentStep] || this._currentExercise;
+ const stepData = step.content || step;
+
+ // Get user's answer
+ let userAnswer = '';
+ let isCorrect = false;
+ let expectedAnswer = '';
+ let score = 0;
+ let feedback = '';
+ let questionText = '';
+
+ // Check for radio buttons (multiple choice)
+ const radioChecked = this._container.querySelector('input[type="radio"]:checked');
+ if (radioChecked) {
+ const selectedIndex = parseInt(radioChecked.value);
+ const options = stepData.options || step.options || [];
+ userAnswer = options[selectedIndex] || `Option ${selectedIndex + 1}`;
+
+ // Check if correct
+ const correctIndex = stepData.correct_answer ?? stepData.correctAnswer ?? -1;
+ isCorrect = (selectedIndex === correctIndex);
+ expectedAnswer = options[correctIndex] || '';
+ score = isCorrect ? 100 : 0;
+ questionText = stepData.question || stepData.passage || 'Multiple choice question';
+ }
+
+ // Check for text input or textarea (phrase translation)
+ const textInput = this._container.querySelector('textarea[name="answer"], input[type="text"][name="answer"]');
+ if (textInput && textInput.value.trim()) {
+ console.log('📝 Text input found, validating...');
+ userAnswer = textInput.value.trim();
+
+ // Get phrase data for AI validation - MUST extract phraseData first!
+ const phraseData = stepData.phrase || stepData;
+ const englishPhrase = phraseData.english || phraseData.text || phraseData.id || '';
+ expectedAnswer = phraseData.user_language || phraseData.translation || stepData.expectedAnswer || '';
+ questionText = englishPhrase;
+
+ console.log('📊 Validation data:', {
+ userAnswer,
+ expectedAnswer,
+ englishPhrase,
+ hasExpected: !!expectedAnswer,
+ hasEnglish: !!englishPhrase
+ });
+
+ if (expectedAnswer && englishPhrase) {
+ // Use AI validation for phrases
+ console.log('🤖 Starting AI validation...');
+ console.log(' English:', englishPhrase);
+ console.log(' User answer:', userAnswer);
+ console.log(' Expected:', expectedAnswer);
+
+ try {
+ // Get LLMValidator from SmartPreviewOrchestrator
+ console.log('🔍 Looking for LLMValidator...');
+ const orchestrator = window.app?.getCore()?.moduleLoader?.getModule('smartPreviewOrchestrator');
+ console.log(' Orchestrator:', orchestrator ? 'Found' : 'NOT FOUND');
+
+ const validator = orchestrator?.llmValidator;
+ console.log(' Validator:', validator ? 'Found' : 'NOT FOUND');
+
+ if (validator) {
+ console.log('✅ LLMValidator found, calling validateTranslation...');
+ const validationResult = await validator.validateTranslation(
+ englishPhrase,
+ userAnswer,
+ expectedAnswer,
+ 'French'
+ );
+
+ isCorrect = validationResult.isCorrect;
+ score = validationResult.score;
+ feedback = validationResult.feedback;
+
+ console.log('✅ AI validation complete:', { isCorrect, score, feedback });
+ } else {
+ console.warn('⚠️ LLMValidator not available, using fuzzy match');
+ // Fallback to fuzzy match
+ const userLower = userAnswer.toLowerCase();
+ const expectedLower = expectedAnswer.toLowerCase();
+ isCorrect = userLower.includes(expectedLower) || expectedLower.includes(userLower);
+ score = isCorrect ? 100 : 0;
+ console.log(' Fuzzy match result:', { isCorrect, score });
+ }
+ } catch (error) {
+ console.error('❌ AI validation error:', error);
+ console.error(' Error details:', error.message, error.stack);
+ // Fallback to fuzzy match
+ const userLower = userAnswer.toLowerCase();
+ const expectedLower = expectedAnswer.toLowerCase();
+ isCorrect = userLower.includes(expectedLower) || expectedLower.includes(userLower);
+ score = isCorrect ? 100 : 0;
+ console.log(' Fallback fuzzy match result:', { isCorrect, score });
+ }
+ } else {
+ console.warn('⚠️ Missing data for AI validation, assuming correct');
+ isCorrect = true; // No expected answer, assume correct
+ score = 100;
+ }
+ }
+
+ // Store the result
+ const result = {
+ question: questionText || stepData.question || stepData.text || 'Question',
+ userAnswer,
+ expectedAnswer,
+ isCorrect,
+ feedback: feedback || stepData.feedback || stepData.explanation || '',
+ score
+ };
+
+ this._userProgress.stepResults.push(result);
+ console.log('📊 Stored step result:', result);
}
_handleHint() {
@@ -1778,16 +2250,24 @@ class UnifiedDRS extends Module {
this._userProgress.timeSpent = Date.now() - this._startTime;
const finalStats = this.getProgress();
- // Show completion message
- if (this._components.resultPanel) {
- this._components.resultPanel.setType('success');
- this._components.resultPanel.setContent(`
-
Exercise Complete!
-
Time spent: ${Math.round(finalStats.timeSpent / 1000)}s
-
Hints used: ${this._userProgress.hints}
- `);
- this._components.resultPanel.show();
- }
+ // Calculate score
+ const totalSteps = this._currentExercise.steps?.length || 1;
+ const correctAnswers = this._userProgress.stepResults?.filter(r => r.isCorrect).length || 0;
+ const scorePercentage = Math.round((correctAnswers / totalSteps) * 100);
+
+ // Calculate display duration: 2× exercise duration
+ const exerciseDuration = finalStats.timeSpent;
+ const displayDuration = exerciseDuration * 2;
+
+ console.log('🎉 Exercise completed!', {
+ score: `${correctAnswers}/${totalSteps}`,
+ percentage: `${scorePercentage}%`,
+ time: `${Math.round(finalStats.timeSpent / 1000)}s`,
+ popupDuration: `${Math.round(displayDuration / 1000)}s`
+ });
+
+ // Show detailed results in popup
+ this._showResultsPopup(finalStats, correctAnswers, totalSteps, scorePercentage, displayDuration);
// Hide next button
if (this._components.nextButton) {
@@ -1797,10 +2277,533 @@ class UnifiedDRS extends Module {
// Emit completion event
this._eventBus.emit('drs:completed', {
stats: finalStats,
- exercise: this._currentExercise
+ exercise: this._currentExercise,
+ score: scorePercentage,
+ correctAnswers,
+ totalSteps
}, this.name);
+ }
- console.log('🎉 Exercise completed!', finalStats);
+ _showResultsPopup(finalStats, correctAnswers, totalSteps, scorePercentage, displayDuration) {
+ const resultsHTML = this._generateDetailedResults(finalStats, correctAnswers, totalSteps, scorePercentage);
+
+ // Create popup overlay
+ const popup = document.createElement('div');
+ popup.className = 'drs-results-popup';
+ popup.innerHTML = `
+
+
+
+ `;
+
+ document.body.appendChild(popup);
+
+ // Countdown timer
+ let remainingSeconds = Math.ceil(displayDuration / 1000);
+ const timerElement = popup.querySelector('#drs-timer-seconds');
+
+ const countdown = setInterval(() => {
+ remainingSeconds--;
+ if (timerElement) {
+ timerElement.textContent = remainingSeconds;
+ }
+
+ if (remainingSeconds <= 0) {
+ clearInterval(countdown);
+ }
+ }, 1000);
+
+ // Helper function to close popup and load next exercise
+ const closeAndContinue = async () => {
+ clearInterval(countdown);
+ popup.style.animation = 'fadeOut 0.3s ease';
+ setTimeout(async () => {
+ popup.remove();
+ await this._loadNextExerciseFluid();
+ }, 300);
+ };
+
+ // Auto-close after duration
+ setTimeout(() => {
+ closeAndContinue();
+ }, displayDuration);
+
+ // Manual close on continue button click
+ const continueBtn = popup.querySelector('.drs-continue-btn');
+ if (continueBtn) {
+ continueBtn.onclick = () => {
+ closeAndContinue();
+ };
+ }
+ }
+
+ /**
+ * Load next exercise fluidly without page reload
+ * @private
+ */
+ async _loadNextExerciseFluid() {
+ try {
+ console.log('🔄 Loading next exercise fluidly...');
+
+ // Update progress bar with latest data
+ if (typeof window.updateProgressBar === 'function') {
+ window.updateProgressBar();
+ console.log('📊 Progress bar updated');
+ }
+
+ // Get Smart Guide components
+ const moduleLoader = window.app?.getCore()?.moduleLoader;
+ if (!moduleLoader) {
+ console.warn('⚠️ ModuleLoader not available - reloading page as fallback');
+ window.location.reload();
+ return;
+ }
+
+ const intelligentSequencer = moduleLoader.getModule('intelligentSequencer');
+ if (!intelligentSequencer) {
+ console.warn('⚠️ IntelligentSequencer not available - reloading page as fallback');
+ window.location.reload();
+ return;
+ }
+
+ console.log('🎯 Requesting next exercise from Smart Guide...');
+ window.updateGuideStatus?.('🔄 Preparing next exercise...');
+
+ // Get next exercise
+ const nextExercise = await intelligentSequencer.getNextExercise();
+
+ if (nextExercise) {
+ console.log('✅ Next exercise found:', nextExercise);
+
+ // Start next exercise if function available
+ if (typeof window.startGuidedExercise === 'function') {
+ setTimeout(() => {
+ window.startGuidedExercise(nextExercise);
+ }, 500);
+ } else {
+ console.error('❌ startGuidedExercise function not found');
+ window.location.reload();
+ }
+ } else {
+ console.log('🎉 Session completed - no more exercises');
+ window.updateGuideStatus?.('🎉 Session Complete!');
+
+ // Show completion message
+ const container = document.querySelector('.drs-container');
+ if (container) {
+ container.innerHTML = `
+
+
🎉
+
Session Complete!
+
Great work! You've completed all available exercises.
+
+ Back to Menu
+
+
+ `;
+ }
+ }
+
+ } catch (error) {
+ console.error('❌ Error loading next exercise:', error);
+ window.updateGuideStatus?.('❌ Error loading next exercise');
+
+ // Fallback to reload on error
+ console.warn('⚠️ Falling back to page reload');
+ setTimeout(() => window.location.reload(), 2000);
+ }
+ }
+
+ _generateDetailedResults(finalStats, correctAnswers, totalSteps, scorePercentage) {
+ const timeSpent = Math.round(finalStats.timeSpent / 1000);
+ const minutes = Math.floor(timeSpent / 60);
+ const seconds = timeSpent % 60;
+ const timeDisplay = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
+
+ // Get step results
+ const stepResults = this._userProgress.stepResults || [];
+
+ let html = `
+
+
+
+
+
+
+
+
${scorePercentage}%
+
Score
+
+
+
+ ✅
+ ${correctAnswers}/${totalSteps}
+ Correct
+
+
+ ⏱️
+ ${timeDisplay}
+ Time
+
+ ${this._userProgress.hints > 0 ? `
+
+ 💡
+ ${this._userProgress.hints}
+ Hints
+
+ ` : ''}
+
+
+
+
+ ${stepResults.length > 0 ? `
+
+
📝 Detailed Results
+ ${stepResults.map((result, index) => this._generateStepResult(result, index + 1)).join('')}
+
+ ` : ''}
+
+
+
+ Continue Learning →
+
+
+
+
+ `;
+
+ return html;
+ }
+
+ _generateStepResult(result, stepNumber) {
+ const isCorrect = result.isCorrect;
+ const icon = isCorrect ? '✅' : '❌';
+ const statusClass = isCorrect ? 'correct' : 'incorrect';
+
+ return `
+
+
+ ${result.question ? `
+
${result.question}
+ ` : ''}
+
+ Your answer:
+ ${result.userAnswer || 'No answer'}
+
+ ${result.expectedAnswer && !isCorrect ? `
+
+ Expected:
+ ${result.expectedAnswer}
+
+ ` : ''}
+ ${result.feedback ? `
+
+ ${isCorrect ? '👍 Great!' : '💡 Tip:'} ${result.feedback}
+
+ ` : ''}
+ ${result.score !== undefined ? `
+
+ AI Score:
+ ${result.score}/100
+
+ ` : ''}
+
+ `;
}
// ===== DEBUG & MONITORING METHODS =====
@@ -2636,19 +3639,40 @@ class UnifiedDRS extends Module {
console.log('Could not get shared services, will create fallbacks:', error);
}
- // Create PrerequisiteEngine if not available
+ // Create or reuse shared PrerequisiteEngine if not available from orchestrator
if (!prerequisiteEngine) {
- console.log('📚 Creating enhanced PrerequisiteEngine');
+ // Check if we already have a shared instance
+ if (this._sharedPrerequisiteEngine) {
+ console.log('♻️ Reusing existing shared PrerequisiteEngine');
+ prerequisiteEngine = this._sharedPrerequisiteEngine;
+ } else {
+ console.log('📚 Creating NEW shared PrerequisiteEngine (will be reused)');
- const PrerequisiteEngine = (await import('./services/PrerequisiteEngine.js')).default;
- prerequisiteEngine = new PrerequisiteEngine();
+ const PrerequisiteEngine = (await import('./services/PrerequisiteEngine.js')).default;
+ prerequisiteEngine = new PrerequisiteEngine();
- // Initialize with persistent data for this chapter
- if (config.bookId && config.chapterId) {
- await prerequisiteEngine.init(config.bookId, config.chapterId);
+ // Initialize with persistent data for this chapter
+ if (config.bookId && config.chapterId) {
+ await prerequisiteEngine.init(config.bookId, config.chapterId);
+ }
+
+ // STORE for reuse
+ this._sharedPrerequisiteEngine = prerequisiteEngine;
+
+ // Expose globally for getRealContentCoverage()
+ if (typeof window !== 'undefined') {
+ window.sharedPrerequisiteEngine = prerequisiteEngine;
+ }
+
+ console.log('✅ Shared PrerequisiteEngine created, stored for reuse, and exposed globally');
}
}
+ // Always update global reference to ensure getRealContentCoverage() has access
+ if (prerequisiteEngine && typeof window !== 'undefined') {
+ window.sharedPrerequisiteEngine = prerequisiteEngine;
+ }
+
// Create ContextMemory if not available
if (!contextMemory) {
console.log('📚 Creating fallback ContextMemory');
@@ -2672,6 +3696,75 @@ class UnifiedDRS extends Module {
};
}
+ /**
+ * Complete current exercise and let Smart Guide decide next one
+ * Called by exercise modules when they finish
+ */
+ async completeExercise(results) {
+ console.log('🏁 Exercise completed, transitioning to next...');
+ console.log('📊 Results:', results);
+
+ // Destroy current module
+ if (this._currentModule && typeof this._currentModule.destroy === 'function') {
+ await this._currentModule.destroy();
+ }
+ this._currentModule = null;
+
+ // Refresh Vocabulary Knowledge modal if it's open
+ if (typeof window.refreshVocabularyModalIfOpen === 'function') {
+ window.refreshVocabularyModalIfOpen();
+ console.log('🔄 Vocabulary modal refreshed with latest data');
+ }
+
+ // Force update progress bar with latest data
+ if (typeof window.updateProgressBar === 'function') {
+ setTimeout(() => {
+ window.updateProgressBar();
+ console.log('🔄 Progress bar refreshed with latest data');
+ }, 500); // Small delay to ensure data is persisted
+ }
+
+ // Get Smart Guide components
+ try {
+ const moduleLoader = window.app.getCore().moduleLoader;
+ const intelligentSequencer = moduleLoader.getModule('intelligentSequencer');
+
+ if (!intelligentSequencer) {
+ console.warn('⚠️ IntelligentSequencer not available');
+ return;
+ }
+
+ console.log('🎯 Requesting next exercise from Smart Guide...');
+
+ setTimeout(async () => {
+ try {
+ const nextExercise = await intelligentSequencer.getNextExercise();
+
+ if (nextExercise) {
+ console.log('🎯 Next exercise recommended:', nextExercise);
+ window.updateGuideStatus?.('🔄 Preparing next exercise...');
+
+ // Use global startGuidedExercise if available
+ if (typeof window.startGuidedExercise === 'function') {
+ setTimeout(() => window.startGuidedExercise(nextExercise), 1500);
+ } else {
+ console.error('❌ startGuidedExercise function not found');
+ }
+ } else {
+ console.log('🎉 Session completed - no more exercises');
+ window.updateGuideStatus?.('🎉 Session Complete!');
+ }
+ } catch (error) {
+ console.error('❌ Error loading next exercise:', error);
+ window.updateGuideStatus?.('❌ Error loading next exercise');
+ }
+ }, 1000);
+
+ } catch (error) {
+ console.error('❌ Error accessing Smart Guide:', error);
+ }
+ }
+
/**
* Clean up UI
*/
diff --git a/src/DRS/exercise-modules/VocabularyModule.js b/src/DRS/exercise-modules/VocabularyModule.js
index 7b807bd..59ac984 100644
--- a/src/DRS/exercise-modules/VocabularyModule.js
+++ b/src/DRS/exercise-modules/VocabularyModule.js
@@ -206,16 +206,22 @@ class VocabularyModule extends DRSExerciseInterface {
return;
}
- // Split vocabulary into groups of 5
- this.allVocabularyGroups = this._createVocabularyGroups(unmastedVocabulary, this.config.groupSize);
- this.currentGroupIndex = 0;
- this.currentVocabularyGroup = this.allVocabularyGroups[0] || [];
+ // DYNAMIC MODE: Pick 5 random words from discovered words (no pre-calculation)
+ const discoveredWords = unmastedVocabulary.filter(word => {
+ return this.prerequisiteEngine.isDiscovered(word.word);
+ });
+ console.log(`📚 Found ${discoveredWords.length} discovered words (out of ${unmastedVocabulary.length} unmastered)`);
+
+ // Take up to 5 random discovered words
+ const selectedWords = this._selectRandomWords(discoveredWords, this.config.groupSize);
+
+ this.currentVocabularyGroup = selectedWords;
this.currentWordIndex = 0;
this.groupResults = [];
this.isRevealed = false;
- console.log(`📚 Split ${unmastedVocabulary.length} unmastered words into ${this.allVocabularyGroups.length} groups of ${this.config.groupSize}`);
+ console.log(`📚 Selected ${selectedWords.length} random words for this session:`, selectedWords.map(w => w.word));
if (this.config.randomizeOrder) {
this._shuffleArray(this.currentVocabularyGroup);
@@ -459,7 +465,7 @@ class VocabularyModule extends DRSExerciseInterface {
📚 Vocabulary Practice
- Group ${this.currentGroupIndex + 1} of ${this.allVocabularyGroups.length} - Word ${this.currentWordIndex + 1} of ${totalWords}
+ Word ${this.currentWordIndex + 1} of ${totalWords}
@@ -494,9 +500,8 @@ class VocabularyModule extends DRSExerciseInterface {
const progressPercentage = totalWords > 0 ?
Math.round((this.currentWordIndex / totalWords) * 100) : 0;
- // Update text
- progressText.textContent =
- `Group ${this.currentGroupIndex + 1} of ${this.allVocabularyGroups.length} - Word ${this.currentWordIndex + 1} of ${totalWords}`;
+ // Update text (no group numbers in dynamic mode)
+ progressText.textContent = `Word ${this.currentWordIndex + 1} of ${totalWords}`;
// Update progress bar
progressFill.style.width = `${progressPercentage}%`;
@@ -862,14 +867,10 @@ class VocabularyModule extends DRSExerciseInterface {
if (accuracy >= 80) resultClass = 'results-excellent';
else if (accuracy >= 60) resultClass = 'results-good';
- // Check if there are more groups
- const hasMoreGroups = this.currentGroupIndex < this.allVocabularyGroups.length - 1;
- const buttonText = hasMoreGroups ? 'Continue to Next Group' : 'Complete Vocabulary Exercise';
- const buttonId = hasMoreGroups ? 'next-group-btn' : 'complete-btn';
-
+ // DYNAMIC MODE: Single "Continue" button - orchestrator decides next exercise
const resultsHTML = `
-
📊 Group ${this.currentGroupIndex + 1} Results
+
📊 Session Results
${accuracy}%
@@ -878,13 +879,10 @@ class VocabularyModule extends DRSExerciseInterface {
${correctCount} / ${totalCount} correct
-
- Group ${this.currentGroupIndex + 1} of ${this.allVocabularyGroups.length}
-
- ${buttonText}
+ Continue →
`;
@@ -896,17 +894,12 @@ class VocabularyModule extends DRSExerciseInterface {
if (card) card.style.display = 'none';
if (controls) controls.style.display = 'none';
- // Add button listeners
- const nextGroupBtn = document.getElementById('next-group-btn');
- const completeBtn = document.getElementById('complete-btn');
+ // Add button listener
+ const continueBtn = document.getElementById('continue-btn');
- if (nextGroupBtn) {
- nextGroupBtn.onclick = () => this._moveToNextGroup();
- }
-
- if (completeBtn) {
- completeBtn.onclick = () => {
- // Complete exercise directly through orchestrator instead of EventBus
+ if (continueBtn) {
+ continueBtn.onclick = () => {
+ // Complete exercise and let orchestrator decide next step
if (this.orchestrator && this.orchestrator.completeExercise) {
this.orchestrator.completeExercise({
moduleType: 'vocabulary',
@@ -914,7 +907,7 @@ class VocabularyModule extends DRSExerciseInterface {
progress: this.getProgress()
});
} else {
- console.log('✅ Vocabulary exercise completed');
+ console.log('✅ Vocabulary session completed, orchestrator will decide next exercise');
// Fallback: use drsDebug if available
if (window.drsDebug?.instance?.completeExercise) {
window.drsDebug.instance.completeExercise();
@@ -924,54 +917,6 @@ class VocabularyModule extends DRSExerciseInterface {
}
}
- _moveToNextGroup() {
- console.log(`🔄 Moving to next vocabulary group (${this.currentGroupIndex + 1} -> ${this.currentGroupIndex + 2})`);
-
- // Check if there's a next group
- if (this.currentGroupIndex >= this.allVocabularyGroups.length - 1) {
- console.warn('⚠️ No more vocabulary groups available');
- return;
- }
-
- // Move to next group
- this.currentGroupIndex++;
- this.currentVocabularyGroup = this.allVocabularyGroups[this.currentGroupIndex];
-
- // Verify the group exists and has words
- if (!this.currentVocabularyGroup || this.currentVocabularyGroup.length === 0) {
- console.error('❌ Next vocabulary group is empty or undefined');
- return;
- }
-
- console.log(`📚 Loaded group ${this.currentGroupIndex + 1} with ${this.currentVocabularyGroup.length} words:`,
- this.currentVocabularyGroup.map(w => w.word));
-
- this.currentWordIndex = 0;
- this.groupResults = [];
- this.isRevealed = false;
-
- // Shuffle new group if needed
- if (this.config.randomizeOrder) {
- this._shuffleArray(this.currentVocabularyGroup);
- }
-
- // Hide results and show vocabulary sections
- const resultsContainer = document.getElementById('group-results');
- const card = document.getElementById('vocabulary-card');
- const controls = document.getElementById('exercise-controls');
-
- if (resultsContainer) {
- resultsContainer.style.display = 'none';
- }
-
- // Show card and controls sections
- if (card) card.style.display = 'block';
- if (controls) controls.style.display = 'block';
-
- // Present first word of new group
- this._presentCurrentWord();
- }
-
_setInputEnabled(enabled) {
const input = document.getElementById('translation-input');
const submitBtn = document.getElementById('submit-btn');
@@ -1010,12 +955,25 @@ class VocabularyModule extends DRSExerciseInterface {
}
}
- _createVocabularyGroups(vocabulary, groupSize) {
- const groups = [];
- for (let i = 0; i < vocabulary.length; i += groupSize) {
- groups.push(vocabulary.slice(i, i + groupSize));
+ /**
+ * Select N random words from an array
+ * @param {Array} words - Array of word objects
+ * @param {number} count - Number of words to select
+ * @returns {Array} - Random selection of words
+ */
+ _selectRandomWords(words, count) {
+ if (words.length <= count) {
+ return [...words]; // Return all if not enough words
}
- return groups;
+
+ // Fisher-Yates shuffle and take first N
+ const shuffled = [...words];
+ for (let i = shuffled.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
+ }
+
+ return shuffled.slice(0, count);
}
_shuffleArray(array) {
@@ -1289,8 +1247,6 @@ class VocabularyModule extends DRSExerciseInterface {
details: {
correctWords,
totalWords,
- currentGroupIndex: this.currentGroupIndex,
- totalGroups: this.allVocabularyGroups ? this.allVocabularyGroups.length : 1,
groupResults: this.groupResults,
masteryPercentage: score
}
@@ -1364,8 +1320,7 @@ class VocabularyModule extends DRSExerciseInterface {
validation: results,
context: {
moduleType: 'vocabulary',
- groupIndex: this.currentGroupIndex,
- totalGroups: this.allVocabularyGroups ? this.allVocabularyGroups.length : 1
+ dynamicMode: true
}
});
}
@@ -1398,9 +1353,8 @@ class VocabularyModule extends DRSExerciseInterface {
...this.config,
groupSize: this.config.groupSize,
masteryThreshold: this.config.masteryThreshold,
- currentGroupIndex: this.currentGroupIndex,
- totalGroups: this.allVocabularyGroups ? this.allVocabularyGroups.length : 1,
- wordCount
+ wordCount,
+ dynamicMode: true // Flag to indicate dynamic word selection
}
};
}
diff --git a/src/DRS/exercise-modules/WordDiscoveryModule.js b/src/DRS/exercise-modules/WordDiscoveryModule.js
index 8e0fd52..0ab2bb1 100644
--- a/src/DRS/exercise-modules/WordDiscoveryModule.js
+++ b/src/DRS/exercise-modules/WordDiscoveryModule.js
@@ -111,9 +111,9 @@ class WordDiscoveryModule extends DRSExerciseInterface {
/**
* Show current word with all details
*/
- _showCurrentWord() {
+ async _showCurrentWord() {
if (this.currentWordIndex >= this.currentWords.length) {
- this._completeDiscovery();
+ await this._completeDiscovery();
return;
}
@@ -249,77 +249,74 @@ class WordDiscoveryModule extends DRSExerciseInterface {
/**
* Go to previous word
*/
- _previousWord() {
+ async _previousWord() {
if (this.currentWordIndex > 0) {
this.currentWordIndex--;
- this._showCurrentWord();
+ await this._showCurrentWord();
}
}
/**
* Go to next word
*/
- _nextWord() {
+ async _nextWord() {
if (this.currentWordIndex < this.currentWords.length - 1) {
this.currentWordIndex++;
- this._showCurrentWord();
+ await this._showCurrentWord();
}
}
/**
* Complete discovery session
*/
- _completeDiscovery() {
- // Mark all current words as discovered
- this.currentWords.forEach(wordObj => {
- this.prerequisiteEngine.markWordDiscovered(wordObj.word);
+ async _completeDiscovery() {
+ // Mark all current words as discovered (MUST await to ensure persistence)
+ for (const wordObj of this.currentWords) {
+ await this.prerequisiteEngine.markWordDiscovered(wordObj.word, {
+ sessionId: Date.now(),
+ moduleType: 'word-discovery',
+ timestamp: new Date().toISOString()
+ });
this.discoveredWords.add(wordObj.word);
- });
+ }
console.log(`✅ Discovery completed for ${this.currentWords.length} words`);
- // Show completion message
+ // Show completion message with single "Continue" button
this.container.innerHTML = `
🎉 Word Discovery Complete!
You've discovered ${this.currentWords.length} new words.
-
Now you can practice them with flashcards!
${this.currentWords.map(w => `${w.word} `).join('')}
-
- Start Flashcard Practice →
+
+ Continue →
`;
- // Auto-redirect to flashcards after a few seconds
- setTimeout(() => {
- this._redirectToFlashcards();
- }, 3000);
-
- document.getElementById('start-flashcards')?.addEventListener('click', () => {
- this._redirectToFlashcards();
+ // Attach event listener to Continue button
+ document.getElementById('continue-btn')?.addEventListener('click', () => {
+ // Complete exercise and let orchestrator decide next step
+ if (this.orchestrator && this.orchestrator.completeExercise) {
+ this.orchestrator.completeExercise({
+ moduleType: 'word-discovery',
+ wordsDiscovered: this.currentWords.length,
+ words: this.currentWords.map(w => w.word),
+ score: 100, // Discovery is always successful
+ completed: true
+ });
+ } else {
+ console.log('✅ Word Discovery completed, orchestrator will decide next exercise');
+ }
});
this.isActive = false;
}
- /**
- * Redirect to DRS VocabularyModule (flashcard system)
- */
- _redirectToFlashcards() {
- // Emit completion event to trigger DRS VocabularyModule
- this.orchestrator._eventBus.emit('exercise:completed', {
- type: 'word-discovery',
- words: this.currentWords.map(w => w.word),
- nextAction: 'vocabulary-flashcards', // This will trigger VocabularyModule in DRS
- nextExerciseType: 'vocabulary-flashcards'
- });
- }
-
/**
* Validate method (not used for discovery)
*/
diff --git a/src/DRS/exercise-modules/PhraseModule.js b/src/DRS/exercise-modules/_deprecated_PhraseModule.js
similarity index 100%
rename from src/DRS/exercise-modules/PhraseModule.js
rename to src/DRS/exercise-modules/_deprecated_PhraseModule.js
diff --git a/src/DRS/factories/ExerciseFactory.js b/src/DRS/factories/ExerciseFactory.js
index 0fb147b..0869bfb 100644
--- a/src/DRS/factories/ExerciseFactory.js
+++ b/src/DRS/factories/ExerciseFactory.js
@@ -27,8 +27,8 @@ class ExerciseFactory {
'grammar-practice-AI': 'GrammarAnalysisModule',
'translation': 'TranslationModule',
'open-response': 'OpenResponseModule',
- 'phrase': 'PhraseModule',
- 'phrase-practice': 'PhraseModule',
+ 'phrase': 'TextAnalysisModule', // REPLACED: was PhraseModule, now using TextAnalysisModule
+ 'phrase-practice': 'TextAnalysisModule', // REPLACED: was PhraseModule, now using TextAnalysisModule
'audio': 'AudioModule',
'listening-comprehension': 'AudioModule',
'listening-comprehension-AI': 'AudioModule',
diff --git a/src/DRS/services/PrerequisiteEngine.js b/src/DRS/services/PrerequisiteEngine.js
index f127805..d346b4d 100644
--- a/src/DRS/services/PrerequisiteEngine.js
+++ b/src/DRS/services/PrerequisiteEngine.js
@@ -252,6 +252,12 @@ class PrerequisiteEngine extends ProgressSystemInterface {
* @returns {Object} - Progress statistics
*/
getMasteryProgress() {
+ // Initialize sets if they don't exist
+ if (!this.masteredTexts) this.masteredTexts = new Set();
+ if (!this.masteredDialogs) this.masteredDialogs = new Set();
+ if (!this.masteredAudios) this.masteredAudios = new Set();
+ if (!this.masteredImages) this.masteredImages = new Set();
+
return {
vocabulary: {
total: this.chapterVocabulary.size,
@@ -263,6 +269,26 @@ class PrerequisiteEngine extends ProgressSystemInterface {
mastered: this.masteredPhrases.size,
percentage: Math.round((this.masteredPhrases.size / Math.max(this.contentAnalysis?.phrases.total || 1, 1)) * 100)
},
+ texts: {
+ total: this.contentAnalysis?.texts.total || 0,
+ mastered: this.masteredTexts.size,
+ percentage: Math.round((this.masteredTexts.size / Math.max(this.contentAnalysis?.texts.total || 1, 1)) * 100)
+ },
+ dialogs: {
+ total: this.contentAnalysis?.dialogs.total || 0,
+ mastered: this.masteredDialogs.size,
+ percentage: Math.round((this.masteredDialogs.size / Math.max(this.contentAnalysis?.dialogs.total || 1, 1)) * 100)
+ },
+ audio: {
+ total: this.contentAnalysis?.audio.total || 0,
+ mastered: this.masteredAudios.size,
+ percentage: Math.round((this.masteredAudios.size / Math.max(this.contentAnalysis?.audio.total || 1, 1)) * 100)
+ },
+ images: {
+ total: this.contentAnalysis?.images.total || 0,
+ mastered: this.masteredImages.size,
+ percentage: Math.round((this.masteredImages.size / Math.max(this.contentAnalysis?.images.total || 1, 1)) * 100)
+ },
grammar: {
total: this.contentAnalysis?.grammar.concepts || 0,
mastered: this.masteredGrammar.size,
@@ -697,16 +723,8 @@ class PrerequisiteEngine extends ProgressSystemInterface {
// ========================================
// ProgressSystemInterface REQUIRED METHODS
// ========================================
-
- async markWordDiscovered(word, metadata = {}) {
- this.markDiscovered(word);
- await this.progressManager.saveDiscoveredWord(word, metadata);
- }
-
- async markWordMastered(word, metadata = {}) {
- this.markMastered(word, metadata);
- await this.progressManager.saveMasteredWord(word, metadata);
- }
+ // Note: markWordDiscovered and markWordMastered are already implemented above (lines 583-655)
+ // They handle both in-memory cache and persistence
isWordDiscovered(word) {
return this.isDiscovered(word);
diff --git a/src/components/Button.js b/src/components/Button.js
index 16556fd..83a5cfc 100644
--- a/src/components/Button.js
+++ b/src/components/Button.js
@@ -33,6 +33,7 @@ class Button {
*/
_createButton() {
this.element = document.createElement('button');
+ this.element.type = 'button'; // CRITICAL: Prevent form submission (default is "submit")
this.element.className = this._getButtonClasses();
if (this.config.id) {
diff --git a/src/core/ContentLoader.js b/src/core/ContentLoader.js
index f47d5d5..564926b 100644
--- a/src/core/ContentLoader.js
+++ b/src/core/ContentLoader.js
@@ -347,6 +347,9 @@ class ContentLoader extends Module {
case 'reading':
// Reading = comprehension with real passages
return await this._generateReadingFromRealContent(realContent, difficulty);
+ case 'phrase':
+ // Phrase comprehension exercises
+ return this._generatePhraseFromRealContent(realContent, difficulty);
case 'audio':
case 'listening-comprehension-QCM':
case 'listening-comprehension-AI':
@@ -1812,6 +1815,52 @@ Return ONLY valid JSON:
};
}
+ _generatePhraseFromRealContent(realContent, difficulty) {
+ console.log('💬 Generating phrase exercise from real content');
+
+ if (!realContent.phrases || Object.keys(realContent.phrases).length === 0) {
+ throw new Error('No phrases found in content');
+ }
+
+ const phrasesEntries = Object.entries(realContent.phrases);
+ console.log(`✅ Found ${phrasesEntries.length} phrases available`);
+
+ // Select one random phrase
+ const selectedPhrases = this._selectRandomItems(phrasesEntries, 1);
+ const [phraseText, phraseData] = selectedPhrases[0];
+
+ // Wrap in steps array like text exercises to enable proper validation flow
+ const steps = [{
+ id: `phrase-step-1`,
+ type: 'phrase',
+ content: {
+ phrase: {
+ id: phraseText,
+ english: phraseText,
+ text: phraseText,
+ user_language: phraseData.user_language,
+ translation: phraseData.user_language,
+ context: phraseData.context,
+ pronunciation: phraseData.pronunciation
+ }
+ }
+ }];
+
+ return {
+ id: `phrase-exercise-${Date.now()}`,
+ type: 'phrase',
+ title: `Phrase Comprehension: ${realContent.name}`,
+ description: `Understand and analyze academic phrases from ${realContent.name}`,
+ difficulty,
+ steps, // Now has steps array like text exercises
+ metadata: {
+ source: 'real-phrases',
+ chapterInfo: realContent.metadata,
+ totalPhrasesAvailable: phrasesEntries.length
+ }
+ };
+ }
+
_generateGrammarFromRealContent(realContent, difficulty) {
if (!realContent.vocabulary) {
throw new Error('No vocabulary found in content');
diff --git a/src/core/IntelligentSequencer.js b/src/core/IntelligentSequencer.js
index a674270..ef19475 100644
--- a/src/core/IntelligentSequencer.js
+++ b/src/core/IntelligentSequencer.js
@@ -40,7 +40,7 @@ class IntelligentSequencer extends Module {
};
// Available exercise types and difficulties
- this._exerciseTypes = ['text', 'reading', 'audio', 'grammar'];
+ this._exerciseTypes = ['text', 'reading', 'phrase', 'audio', 'grammar'];
this._difficulties = ['easy', 'medium', 'hard'];
// Current session state
@@ -140,10 +140,13 @@ class IntelligentSequencer extends Module {
return null;
}
- const scoredCandidates = candidates.map(candidate => ({
- ...candidate,
- score: this._calculateExerciseScore(candidate)
- }));
+ // Calculate scores for all candidates (async)
+ const scoredCandidates = await Promise.all(
+ candidates.map(async candidate => ({
+ ...candidate,
+ score: await this._calculateExerciseScore(candidate)
+ }))
+ );
// Sort by score (higher is better)
scoredCandidates.sort((a, b) => b.score - a.score);
@@ -328,6 +331,10 @@ class IntelligentSequencer extends Module {
// Reading needs predefined exercises or content with questions
return this._hasReadingContent(content);
+ case 'phrase':
+ // Phrase comprehension needs phrases object with content
+ return content.phrases && Object.keys(content.phrases).length > 0;
+
case 'grammar':
// Grammar needs varied word types
if (!content.vocabulary) return false;
@@ -387,7 +394,7 @@ class IntelligentSequencer extends Module {
return false;
}
- _calculateExerciseScore(candidate) {
+ async _calculateExerciseScore(candidate) {
// Calculate variety score (avoid recent repetition)
const varietyScore = this._calculateVarietyScore(candidate);
@@ -397,12 +404,15 @@ class IntelligentSequencer extends Module {
// Calculate freshness score (prefer less practiced combinations)
const freshnessScore = this._calculateFreshnessScore(candidate);
- // Weighted combination
+ // NEW: Calculate content abundance bonus (prefer types with more content)
+ const abundanceBonus = await this._calculateContentAbundanceBonus(candidate.type);
+
+ // Weighted combination with abundance bonus
const totalScore = (
varietyScore * this._config.varietyWeight +
performanceScore * this._config.performanceWeight +
freshnessScore * this._config.freshnessWeight
- );
+ ) + abundanceBonus; // Add bonus directly to total
return totalScore;
}
@@ -476,6 +486,76 @@ class IntelligentSequencer extends Module {
return Math.max(1.0 - (comboCount * 0.1), 0.1);
}
+ async _calculateContentAbundanceBonus(exerciseType) {
+ // Give bonus to exercise types with MORE available content
+ // This makes phrase (53 items) more likely to appear than text (3 items)
+
+ const session = this._currentSession;
+ if (!session) return 0;
+
+ try {
+ const contentPath = `/content/chapters/${session.chapterId || session.bookId}.json`;
+ const response = await fetch(contentPath);
+
+ if (!response.ok) return 0;
+
+ const content = await response.json();
+ let itemCount = 0;
+
+ // Count available items for this exercise type
+ switch (exerciseType) {
+ case 'phrase':
+ itemCount = content.phrases ? Object.keys(content.phrases).length : 0;
+ break;
+ case 'text':
+ itemCount = content.texts ? content.texts.length : 0;
+ break;
+ case 'reading':
+ // Count texts + dialogs with questions
+ const textsWithQ = content.texts ? content.texts.filter(t => t.questions?.length > 0).length : 0;
+ const dialogsWithQ = content.dialogs ? Object.values(content.dialogs).filter(d => d.questions?.length > 0).length : 0;
+ itemCount = textsWithQ + dialogsWithQ;
+ break;
+ case 'audio':
+ // Count dialogs
+ itemCount = content.dialogs ? Object.keys(content.dialogs).length : 0;
+ break;
+ case 'grammar':
+ itemCount = content.grammar ? Object.keys(content.grammar).length : 0;
+ break;
+ default:
+ itemCount = 0;
+ }
+
+ // Calculate bonus based on item count
+ // 50+ items → 0.5 bonus (HUGE boost)
+ // 20-49 items → 0.3 bonus (big boost)
+ // 10-19 items → 0.2 bonus (moderate boost)
+ // 5-9 items → 0.1 bonus (small boost)
+ // <5 items → 0 bonus
+
+ if (itemCount >= 50) {
+ console.log(`📊 Content abundance bonus for ${exerciseType}: 0.5 (${itemCount} items)`);
+ return 0.5;
+ } else if (itemCount >= 20) {
+ console.log(`📊 Content abundance bonus for ${exerciseType}: 0.3 (${itemCount} items)`);
+ return 0.3;
+ } else if (itemCount >= 10) {
+ console.log(`📊 Content abundance bonus for ${exerciseType}: 0.2 (${itemCount} items)`);
+ return 0.2;
+ } else if (itemCount >= 5) {
+ console.log(`📊 Content abundance bonus for ${exerciseType}: 0.1 (${itemCount} items)`);
+ return 0.1;
+ }
+
+ return 0;
+
+ } catch (error) {
+ console.warn(`⚠️ Error calculating content abundance:`, error);
+ return 0;
+ }
+ }
+
_updatePerformanceTracking(record) {
const type = record.type;