Class_generator/docs/creating-new-module.md
StillHammer 325b97060c Add LEDU Chinese course content and documentation
Add comprehensive Chinese reading course (乐读) with 4 chapters of vocabulary, texts, and exercises. Include architecture documentation for module development and progress tracking system.

Content:
- LEDU book metadata with 12 chapter outline
- Chapter 1: Food culture (民以食为天) - 45+ vocabulary, etiquette
- Chapter 2: Shopping (货比三家) - comparative shopping vocabulary
- Chapter 3: Sports & fitness (生命在于运动) - exercise habits
- Chapter 4: Additional vocabulary and grammar

Documentation:
- Architecture principles and patterns
- Module creation guide (Game, DRS, Progress)
- Interface system (C++ style contracts)
- Progress tracking and prerequisites

Game Enhancements:
- MarioEducational helper classes (Physics, Renderer, Sound, Enemies)
- VocabularyModule TTS improvements
- Updated CLAUDE.md with project status

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 07:25:53 +08:00

313 lines
8.4 KiB
Markdown

# Creating New Modules
## 🎮 Game Module Template
### Basic Structure
```javascript
import Module from '../core/Module.js';
class GameName extends Module {
constructor(name, dependencies, config) {
super(name, ['eventBus']); // Declare dependencies
// Validate dependencies
if (!dependencies.eventBus) {
throw new Error('GameName requires EventBus dependency');
}
this._eventBus = dependencies.eventBus;
this._config = config;
Object.seal(this); // Prevent modification
}
async init() {
this._validateNotDestroyed();
// Set up event listeners
this._eventBus.on('game:start', this._handleStart.bind(this), this.name);
this._setInitialized();
}
async destroy() {
this._validateNotDestroyed();
// Cleanup: remove event listeners, DOM elements, timers
this._eventBus.off('game:start', this._handleStart, this.name);
this._setDestroyed();
}
// Private methods
_handleStart(event) {
this._validateInitialized();
// Game logic here
}
}
export default GameName;
```
### Registration in Application.js
```javascript
modules: [
{
name: 'gameName',
path: './games/GameName.js',
dependencies: ['eventBus'],
config: {
difficulty: 'medium',
scoreToWin: 100
}
}
]
```
## 📋 DRS Exercise Module Template
### Using DRSExerciseInterface
```javascript
import DRSExerciseInterface from '../DRS/interfaces/DRSExerciseInterface.js';
class MyExercise extends DRSExerciseInterface {
constructor() {
super('MyExercise');
// Internal state
this.score = 0;
this.attempts = 0;
this.startTime = null;
this.container = null;
}
// ⚠️ REQUIRED - Initialize exercise
async init(config, content) {
this.config = config;
this.content = content;
this.startTime = Date.now();
// Validate content
if (!content || !content.question) {
throw new Error('MyExercise requires content with question');
}
}
// ⚠️ REQUIRED - Render UI
async render(container) {
this.container = container;
container.innerHTML = `
<div class="exercise-container">
<h2>${this.content.question}</h2>
<input type="text" id="answer-input" />
<button id="submit-btn">Submit</button>
</div>
`;
// Event listeners
container.querySelector('#submit-btn').addEventListener('click', () => {
const answer = container.querySelector('#answer-input').value;
this.handleUserInput('submit', { answer });
});
}
// ⚠️ REQUIRED - Clean up
async destroy() {
if (this.container) {
this.container.innerHTML = '';
}
}
// ⚠️ REQUIRED - Validate answer
async validate(userAnswer) {
this.attempts++;
const isCorrect = userAnswer.toLowerCase() === this.content.correctAnswer.toLowerCase();
const score = isCorrect ? 100 - (this.attempts - 1) * 10 : 0;
return {
isCorrect,
score: Math.max(score, 0),
feedback: isCorrect ? 'Correct!' : 'Try again',
explanation: `The correct answer is: ${this.content.correctAnswer}`
};
}
// ⚠️ REQUIRED - Get results
getResults() {
return {
score: this.score,
attempts: this.attempts,
timeSpent: Date.now() - this.startTime,
completed: this.score > 0,
details: {
question: this.content.question,
correctAnswer: this.content.correctAnswer
}
};
}
// ⚠️ REQUIRED - Handle user input
handleUserInput(event, data) {
if (event === 'submit') {
this.validate(data.answer).then(result => {
this.score = result.score;
this.displayFeedback(result);
});
}
}
// ⚠️ REQUIRED - Mark as completed
async markCompleted(results) {
// Save to progress system
await window.app.getCore().progressTracker.markExerciseCompleted(
'my-exercise',
this.content.id,
results
);
}
// ⚠️ REQUIRED - Get progress
getProgress() {
return {
percentage: this.score > 0 ? 100 : 0,
currentStep: 1,
totalSteps: 1,
itemsCompleted: this.score > 0 ? 1 : 0,
itemsTotal: 1
};
}
// ⚠️ REQUIRED - Get exercise type
getExerciseType() {
return 'my-exercise';
}
// ⚠️ REQUIRED - Get exercise config
getExerciseConfig() {
return {
type: 'my-exercise',
difficulty: this.config?.difficulty || 'medium',
estimatedTime: 120, // seconds
prerequisites: [],
metadata: {
hasAI: false,
requiresInternet: false
}
};
}
// Helper methods
displayFeedback(result) {
const feedbackDiv = this.container.querySelector('.feedback') ||
document.createElement('div');
feedbackDiv.className = 'feedback';
feedbackDiv.textContent = result.feedback;
if (!this.container.querySelector('.feedback')) {
this.container.appendChild(feedbackDiv);
}
}
}
export default MyExercise;
```
## 📊 Progress Item Template
### Using ProgressItemInterface
```javascript
import ProgressItemInterface from '../DRS/interfaces/ProgressItemInterface.js';
class MyCustomItem extends ProgressItemInterface {
constructor(id, metadata) {
super('my-custom-item', id, metadata);
}
// ⚠️ REQUIRED - Validate item data
validate() {
if (!this.metadata.requiredField) {
throw new Error('MyCustomItem requires requiredField in metadata');
}
return true;
}
// ⚠️ REQUIRED - Convert to JSON
serialize() {
return {
...this._getBaseSerialization(),
customData: this.metadata.custom,
timestamp: Date.now()
};
}
// ⚠️ REQUIRED - Return item weight
getWeight() {
return ProgressItemInterface.WEIGHTS['my-custom-item'] || 5;
}
// ⚠️ REQUIRED - Check prerequisites
canComplete(userProgress) {
// Check if user has completed prerequisites
const prerequisite = this.metadata.prerequisite;
if (prerequisite) {
return userProgress.hasCompleted(prerequisite);
}
return true;
}
}
export default MyCustomItem;
```
## ✅ Checklist for New Modules
### For Game Modules
- [ ] Extends `Module` base class
- [ ] Validates dependencies in constructor
- [ ] Uses `Object.seal(this)` at end of constructor
- [ ] Implements `init()` and calls `_setInitialized()`
- [ ] Implements `destroy()` and calls `_setDestroyed()`
- [ ] Uses EventBus for all communication
- [ ] No direct access to other modules
- [ ] Registered in `Application.js` modules array
### For DRS Exercise Modules
- [ ] Extends `DRSExerciseInterface`
- [ ] Implements all 10 required methods
- [ ] Validates content in `init()`
- [ ] Cleans up in `destroy()`
- [ ] Returns correct format from `validate()`
- [ ] Integrates with progress system
- [ ] Tested with ImplementationValidator
### For Progress Items
- [ ] Extends `ProgressItemInterface`
- [ ] Implements all 4 required methods
- [ ] Validates data correctly
- [ ] Returns proper weight
- [ ] Checks prerequisites properly
- [ ] Added to ImplementationValidator
## 🚨 Common Mistakes to Avoid
1. **Forgetting Object.seal()** - Module can be modified externally
2. **Not validating dependencies** - Module fails at runtime
3. **Direct module access** - Use EventBus instead
4. **Missing required methods** - Red screen error at startup
5. **Not cleaning up** - Memory leaks on destroy
6. **Hardcoded paths** - Use dynamic content loading
7. **Skipping ImplementationValidator** - Interface violations not caught
## 📚 Examples in Codebase
- **Game Module**: `src/games/FlashcardLearning.js`
- **DRS Exercise**: `src/DRS/exercise-modules/VocabularyModule.js`
- **Progress Item**: `src/DRS/services/ProgressItemInterface.js`
- **Validation**: `src/DRS/services/ImplementationValidator.js`