🔧 FIX: Remove corrupted code from constructor
- Fixed ReferenceError: answer is not defined - Cleaned up corrupted code fragments in KidsAIExplorer constructor - Removed duplicate/misplaced response generation logic - Application now initializes properly without errors
This commit is contained in:
@@ -1,77 +0,0 @@
|
||||
# 🌐 Public Domain Setup Complete!
|
||||
|
||||
## ✅ **Your KidsAI Explorer is now live on the public domain!**
|
||||
|
||||
### 🚀 **Access URLs**
|
||||
|
||||
- **Main Application**: https://portal.egonetix.de/kidsai/
|
||||
- **Testing Interface**: https://portal.egonetix.de/kidsai/test-ai.html
|
||||
- **API Health Check**: https://portal.egonetix.de/api/health
|
||||
|
||||
### 🔧 **What Was Fixed**
|
||||
|
||||
1. **Nginx Proxy Configuration**: Updated port from 3000 → 3002
|
||||
2. **API Routing**: All `/api/*` requests now properly routed to backend
|
||||
3. **Static File Serving**: Added dedicated location block for KidsAI files
|
||||
4. **Timeout Settings**: Increased timeouts for AI processing
|
||||
5. **Service Setup**: Created systemd service for reliable production deployment
|
||||
|
||||
### 📊 **Technical Details**
|
||||
|
||||
#### Nginx Configuration
|
||||
```nginx
|
||||
# API Proxy (port 3002)
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:3002;
|
||||
# Enhanced headers and timeouts for AI processing
|
||||
}
|
||||
|
||||
# Static Files
|
||||
location /kidsai/ {
|
||||
alias /var/www/html/kidsai/;
|
||||
try_files $uri $uri/ /kidsai/index.html;
|
||||
}
|
||||
```
|
||||
|
||||
#### Production Service
|
||||
```bash
|
||||
# Service status
|
||||
systemctl status kidsai-explorer
|
||||
|
||||
# Service logs
|
||||
journalctl -f -u kidsai-explorer
|
||||
|
||||
# Restart service
|
||||
systemctl restart kidsai-explorer
|
||||
```
|
||||
|
||||
### ✅ **Verification Tests**
|
||||
|
||||
All endpoints tested and working:
|
||||
|
||||
- ✅ **Health Check**: `GET /api/health`
|
||||
- ✅ **AI Questions**: `POST /api/ask`
|
||||
- ✅ **Answer Reveal**: `POST /api/reveal-answer`
|
||||
- ✅ **Static Files**: CSS, JS, images all loading
|
||||
- ✅ **HTTPS**: Secure connection working
|
||||
|
||||
### 🎯 **Production Ready Features**
|
||||
|
||||
- **Auto-restart**: Service restarts on failure
|
||||
- **Security**: Proper user isolation and permissions
|
||||
- **Logging**: Centralized logging via systemd
|
||||
- **Performance**: Optimized caching for static assets
|
||||
- **SSL**: Secure HTTPS communication
|
||||
- **High Availability**: Multiple fallback layers for AI
|
||||
|
||||
### 🌟 **Your KidsAI Explorer is now live!**
|
||||
|
||||
**Visit**: https://portal.egonetix.de/kidsai/
|
||||
|
||||
The application will work exactly the same as localhost, but now:
|
||||
- ✅ Accessible from anywhere on the internet
|
||||
- ✅ Secure HTTPS connection
|
||||
- ✅ Production-grade reliability
|
||||
- ✅ Auto-scaling and monitoring ready
|
||||
|
||||
**Share it with kids worldwide! 🚀🌍**
|
||||
|
||||
@@ -11,55 +11,7 @@ class KidsAIExplorer {
|
||||
this.questionInput = document.getElementById('question-input');
|
||||
this.askButton = document.getElementById('ask-button');
|
||||
this.thinkingSection = document.getElementById('thinking-section');
|
||||
this.thinkingSteps = document // Topic-specific contextual responses
|
||||
|
||||
// BICYCLE BRAKE RESPO // Generic responses based on answer quality (as last resort)
|
||||
if (answer.length > 25) {
|
||||
return "🌟 I appreciate your detailed explanation! You're really working through this systematically.";
|
||||
} else if (answer.length > 15) {
|
||||
return "👍 Good thinking! I can see you're considering different aspects of this.";
|
||||
} else if (answer.length > 8) {
|
||||
return "💭 Interesting! Can you help me understand your reasoning a bit more?";
|
||||
} else if (answer.length > 3) {
|
||||
return "🤔 I see what you're thinking. Can you elaborate on that?";
|
||||
} else {
|
||||
return "💡 Feel free to share any thoughts - every idea helps us learn!";
|
||||
} if (questionLower.includes('bicycle') && questionLower.includes('brake')) {
|
||||
if (answerLower.includes('slow') || answerLower.includes('stop')) {
|
||||
return "🚴♂️ Exactly right! You understand that brakes slow you down and stop you. That's the basic function we need to understand!";
|
||||
} else if (answerLower.includes('wheel') || answerLower.includes('rim')) {
|
||||
return "🎯 Great technical thinking! You understand how the brake mechanism works on the wheel!";
|
||||
} else {
|
||||
return "🤔 Tell me more about what happens when you use bicycle brakes.";
|
||||
}
|
||||
}
|
||||
|
||||
// CAR BRAKE RESPONSES - This is the key fix!
|
||||
if (questionLower.includes('car') && (questionLower.includes('brake') || questionLower.includes('slow down') || questionLower.includes('stop'))) {
|
||||
if ((answerLower.includes('brake') && answerLower.includes('?')) || answer.trim().toLowerCase() === 'the brake') {
|
||||
return "🎯 EXACTLY! 🚗 Yes, it's the brake system! Perfect answer - the brake pedal controls the brakes that slow down or stop the car!";
|
||||
} else if (answerLower.includes('brake') && answerLower.includes('pedal')) {
|
||||
return "🏆 Perfect! The brake pedal and brake system work together to control the car's speed. You nailed it!";
|
||||
} else if (answerLower.includes('brake')) {
|
||||
return "🎯 YES! The brakes! You got it exactly right! The brake system is what slows down and stops the car!";
|
||||
} else if (answerLower.includes('wheel') || answerLower.includes('tire')) {
|
||||
return "👍 You're thinking about where the braking happens - at the wheels! The brake system applies pressure there.";
|
||||
} else if (answerLower.includes('pedal')) {
|
||||
return "🦶 Good! You're thinking about pedals. Which specific pedal controls stopping?";
|
||||
} else {
|
||||
return "🤔 What component do you think actually does the slowing down in a car?";
|
||||
}
|
||||
}
|
||||
|
||||
if (questionLower.includes('bicycle') || questionLower.includes('pedal') || questionLower.includes('chain')) {
|
||||
if (answerLower.includes('pedal') || answerLower.includes('chain') || answerLower.includes('wheel')) {
|
||||
return "🚴♂️ Excellent! You understand the connection between pedals, chain, and wheels. That mechanical connection concept is key!";
|
||||
} else if (answerLower.includes('gear') || answerLower.includes('speed')) {
|
||||
return "👍 Yes! You're thinking about how we control speed and power - that's exactly right!";
|
||||
} else if (answerLower.includes('slow') || answerLower.includes('stop')) {
|
||||
return "🚴♂️ Right! You understand the basic control functions of bicycles!";
|
||||
}
|
||||
}entById('thinking-steps');
|
||||
this.thinkingSteps = document.getElementById('thinking-steps');
|
||||
this.loadingOverlay = document.getElementById('loading');
|
||||
this.suggestionCards = document.querySelectorAll('.suggestion-card');
|
||||
this.langButtons = document.querySelectorAll('.lang-btn');
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
894
html/kidsai/script.js.backup
Normal file
894
html/kidsai/script.js.backup
Normal file
@@ -0,0 +1,894 @@
|
||||
// KidsAI Explorer - Interactive Learning Assistant
|
||||
console.log('🚀 KidsAI script.js is loading...');
|
||||
|
||||
class KidsAIExplorer {
|
||||
constructor() {
|
||||
this.currentLanguage = localStorage.getItem('kidsai-language') || 'en';
|
||||
|
||||
// Get DOM elements with error handling
|
||||
this.questionInput = document.getElementById('question-input');
|
||||
this.askButton = document.getElementById('ask-button');
|
||||
this.thinkingSection = document.getElementById('thinking-section');
|
||||
this.thinkingSteps = document.getElementById('thinking-steps');
|
||||
this.actionButtons = document.getElementById('action-buttons');
|
||||
this.loadingOverlay = document.getElementById('loading');
|
||||
this.suggestionCards = document.querySelectorAll('.suggestion-card');
|
||||
this.langButtons = document.querySelectorAll('.lang-btn');
|
||||
|
||||
// Debug: Log what elements we found
|
||||
console.log('🔍 DOM Elements found:');
|
||||
console.log('questionInput:', this.questionInput);
|
||||
console.log('askButton:', this.askButton);
|
||||
console.log('suggestionCards count:', this.suggestionCards.length);
|
||||
|
||||
// Ensure loading is hidden initially
|
||||
if (this.loadingOverlay) {
|
||||
this.loadingOverlay.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Initialize only if we have the required elements
|
||||
if (this.questionInput && this.askButton) {
|
||||
this.initializeLanguage();
|
||||
this.initializeEventListeners();
|
||||
this.initializeAnimations();
|
||||
} else {
|
||||
console.error('Required DOM elements not found');
|
||||
}
|
||||
}
|
||||
|
||||
initializeLanguage() {
|
||||
// Set active language button
|
||||
this.langButtons.forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.lang === this.currentLanguage);
|
||||
});
|
||||
|
||||
// Apply translations
|
||||
this.applyTranslations();
|
||||
}
|
||||
|
||||
applyTranslations() {
|
||||
try {
|
||||
const t = translations[this.currentLanguage];
|
||||
if (!t) {
|
||||
console.warn('Translations not available for language:', this.currentLanguage);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update all elements with data-translate attribute
|
||||
document.querySelectorAll('[data-translate]').forEach(element => {
|
||||
const key = element.getAttribute('data-translate');
|
||||
if (t[key]) {
|
||||
element.textContent = t[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Update placeholder
|
||||
const placeholder = document.querySelector('[data-translate-placeholder]');
|
||||
if (placeholder) {
|
||||
const key = placeholder.getAttribute('data-translate-placeholder');
|
||||
if (t[key]) {
|
||||
placeholder.placeholder = t[key];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error applying translations:', error);
|
||||
}
|
||||
}
|
||||
|
||||
switchLanguage(lang) {
|
||||
this.currentLanguage = lang;
|
||||
localStorage.setItem('kidsai-language', lang);
|
||||
|
||||
// Update active button
|
||||
this.langButtons.forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.lang === lang);
|
||||
});
|
||||
|
||||
// Apply translations
|
||||
this.applyTranslations();
|
||||
|
||||
// Clear thinking section if visible
|
||||
if (!this.thinkingSection.classList.contains('hidden')) {
|
||||
this.thinkingSection.classList.add('hidden');
|
||||
this.actionButtons.classList.add('hidden');
|
||||
this.thinkingSteps.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
initializeEventListeners() {
|
||||
console.log('Initializing event listeners');
|
||||
|
||||
// Language switching
|
||||
this.langButtons.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
this.switchLanguage(btn.dataset.lang);
|
||||
});
|
||||
});
|
||||
|
||||
// Main question submission
|
||||
if (this.askButton) {
|
||||
console.log('Adding click listener to ask button');
|
||||
this.askButton.addEventListener('click', () => this.handleQuestion());
|
||||
}
|
||||
|
||||
if (this.questionInput) {
|
||||
console.log('Adding keypress listener to question input');
|
||||
this.questionInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
this.handleQuestion();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Suggestion cards
|
||||
console.log('Adding listeners to suggestion cards:', this.suggestionCards.length);
|
||||
this.suggestionCards.forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
console.log('Suggestion card clicked');
|
||||
const questionKey = `data-question-${this.currentLanguage}`;
|
||||
const question = card.getAttribute(questionKey);
|
||||
console.log('Setting question:', question);
|
||||
this.questionInput.value = question;
|
||||
this.handleQuestion();
|
||||
});
|
||||
});
|
||||
|
||||
// Action buttons
|
||||
document.getElementById('research-btn').addEventListener('click', () => {
|
||||
this.showActionGuidance('research');
|
||||
});
|
||||
|
||||
document.getElementById('experiment-btn').addEventListener('click', () => {
|
||||
this.showActionGuidance('experiment');
|
||||
});
|
||||
|
||||
document.getElementById('discuss-btn').addEventListener('click', () => {
|
||||
this.showActionGuidance('discuss');
|
||||
});
|
||||
}
|
||||
|
||||
initializeAnimations() {
|
||||
// Add floating animation to suggestion cards
|
||||
this.suggestionCards.forEach((card, index) => {
|
||||
card.style.animationDelay = `${index * 0.1}s`;
|
||||
card.style.animation = 'fadeInUp 0.6s ease-out both';
|
||||
});
|
||||
}
|
||||
|
||||
async handleQuestion() {
|
||||
console.log('handleQuestion called');
|
||||
const question = this.questionInput.value.trim();
|
||||
console.log('Question:', question);
|
||||
|
||||
if (!question) {
|
||||
this.showMessage('Please ask me something first! 🤔', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading();
|
||||
|
||||
try {
|
||||
// Call the AI backend
|
||||
const response = await fetch('/api/ask', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
question: question,
|
||||
language: this.currentLanguage
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Display AI-powered guidance
|
||||
this.displayAIGuidance(data.guidance, data.fallback);
|
||||
} else {
|
||||
throw new Error(data.error || 'Unknown error');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error getting AI guidance:', error);
|
||||
|
||||
// Fallback to local guidance if backend fails
|
||||
this.showMessage('Using offline guidance...', 'info');
|
||||
const localGuidance = this.generateThinkingGuidance(question);
|
||||
this.displayThinkingProcess(localGuidance);
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
displayAIGuidance(guidance, isFallback) {
|
||||
// Clear any previous content
|
||||
this.thinkingSteps.innerHTML = '';
|
||||
this.currentStep = 0;
|
||||
this.userAnswers = [];
|
||||
|
||||
// Create conversation container
|
||||
const conversationContainer = document.createElement('div');
|
||||
conversationContainer.className = 'conversation-container';
|
||||
this.thinkingSteps.appendChild(conversationContainer);
|
||||
|
||||
// Show initial encouragement
|
||||
const welcomeStep = document.createElement('div');
|
||||
welcomeStep.className = 'conversation-step visible';
|
||||
welcomeStep.innerHTML = `
|
||||
<div class="ai-message teacher">
|
||||
<p>${guidance.encouragement}</p>
|
||||
${isFallback ? '<small>💻 Offline mode</small>' : '<small>✨ AI-powered</small>'}
|
||||
</div>
|
||||
`;
|
||||
conversationContainer.appendChild(welcomeStep);
|
||||
|
||||
// Store guidance for step-by-step interaction
|
||||
this.currentGuidance = guidance;
|
||||
this.conversationContainer = conversationContainer;
|
||||
|
||||
// Start the first question after a delay
|
||||
setTimeout(() => {
|
||||
this.showNextQuestion();
|
||||
}, 1500);
|
||||
|
||||
// Show the thinking section
|
||||
setTimeout(() => {
|
||||
this.thinkingSection.classList.remove('hidden');
|
||||
}, 500);
|
||||
}
|
||||
|
||||
showNextQuestion() {
|
||||
if (this.currentStep >= this.currentGuidance.steps.length) {
|
||||
this.showCompletionMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
const step = this.currentGuidance.steps[this.currentStep];
|
||||
const stepDiv = document.createElement('div');
|
||||
stepDiv.className = 'conversation-step';
|
||||
stepDiv.innerHTML = `
|
||||
<div class="progress-timeline">
|
||||
<div class="progress-step current">
|
||||
<div class="step-indicator">${step.id}</div>
|
||||
<div class="ai-message">
|
||||
<p>${step.text}</p>
|
||||
</div>
|
||||
<div class="user-input-area" id="input-area-${this.currentStep}">
|
||||
<textarea
|
||||
placeholder="${this.currentLanguage === 'de' ? 'Schreibe deine Gedanken hier...' : 'Write your thoughts here...'}"
|
||||
rows="3"
|
||||
id="user-input-${this.currentStep}"
|
||||
></textarea>
|
||||
<div class="input-actions">
|
||||
<span class="input-hint">${this.currentLanguage === 'de' ? '💡 Nimm dir Zeit zum Nachdenken!' : '💡 Take your time to think!'}</span>
|
||||
<button class="reply-btn" onclick="kidsAI.submitAnswer(${this.currentStep})">
|
||||
<span>💬</span>
|
||||
${this.currentLanguage === 'de' ? 'Antworten' : 'Reply'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.conversationContainer.appendChild(stepDiv);
|
||||
|
||||
// Animate in
|
||||
setTimeout(() => {
|
||||
stepDiv.classList.add('visible');
|
||||
}, 100);
|
||||
|
||||
// Scroll to the new question
|
||||
setTimeout(() => {
|
||||
stepDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}, 600);
|
||||
}
|
||||
|
||||
submitAnswer(stepIndex) {
|
||||
const textarea = document.getElementById(`user-input-${stepIndex}`);
|
||||
const inputArea = document.getElementById(`input-area-${stepIndex}`);
|
||||
const answer = textarea.value.trim();
|
||||
|
||||
if (!answer) {
|
||||
this.showMessage(
|
||||
this.currentLanguage === 'de' ? 'Bitte schreibe deine Gedanken auf! 🤔' : 'Please write down your thoughts! 🤔',
|
||||
'warning'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the answer
|
||||
this.userAnswers[stepIndex] = answer;
|
||||
|
||||
// Mark as answered
|
||||
inputArea.classList.add('answered');
|
||||
textarea.disabled = true;
|
||||
|
||||
// Show encouragement
|
||||
const encouragement = document.createElement('div');
|
||||
encouragement.className = 'encouragement-feedback';
|
||||
encouragement.innerHTML = this.getEncouragementMessage();
|
||||
inputArea.appendChild(encouragement);
|
||||
|
||||
// Move to next question
|
||||
this.currentStep++;
|
||||
setTimeout(() => {
|
||||
this.showNextQuestion();
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
getEncouragementMessage() {
|
||||
const messages = this.currentLanguage === 'de' ? [
|
||||
'🌟 Toll! Du denkst wirklich gut nach!',
|
||||
'👏 Ausgezeichnet! Weiter so!',
|
||||
'🎯 Super Antwort! Du bist auf dem richtigen Weg!',
|
||||
'✨ Fantastisch! Du lernst wie ein echter Forscher!'
|
||||
] : [
|
||||
'🌟 Great! You\'re thinking really well!',
|
||||
'👏 Excellent! Keep it up!',
|
||||
'🎯 Super answer! You\'re on the right track!',
|
||||
'✨ Fantastic! You\'re learning like a real scientist!'
|
||||
];
|
||||
|
||||
return messages[Math.floor(Math.random() * messages.length)];
|
||||
}
|
||||
|
||||
showCompletionMessage() {
|
||||
const completionDiv = document.createElement('div');
|
||||
completionDiv.className = 'conversation-step';
|
||||
completionDiv.innerHTML = `
|
||||
<div class="ai-message teacher">
|
||||
<h4>${this.currentLanguage === 'de' ? '🎉 Fantastisch gemacht!' : '🎉 Fantastic Job!'}</h4>
|
||||
<p>${this.currentLanguage === 'de'
|
||||
? 'Du hast wirklich gut nachgedacht! Jetzt bist du bereit, die richtige Antwort zu entdecken.'
|
||||
: 'You\'ve done some great thinking! Now you\'re ready to discover the actual answer.'}</p>
|
||||
</div>
|
||||
<div class="next-question-prompt">
|
||||
<button id="reveal-answer-btn" class="reply-btn" style="font-size: 1.1rem; padding: 15px 30px;">
|
||||
<span>🎯</span>
|
||||
${this.currentLanguage === 'de' ? 'Antwort zeigen!' : 'Reveal Answer!'}
|
||||
</button>
|
||||
<div id="answer-content" class="answer-content hidden" style="margin-top: 20px;">
|
||||
<!-- Answer will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.conversationContainer.appendChild(completionDiv);
|
||||
|
||||
// Animate in
|
||||
setTimeout(() => {
|
||||
completionDiv.classList.add('visible');
|
||||
}, 100);
|
||||
|
||||
// Add click handler for reveal button
|
||||
setTimeout(() => {
|
||||
document.getElementById('reveal-answer-btn').addEventListener('click', () => {
|
||||
this.revealAnswer();
|
||||
});
|
||||
}, 200);
|
||||
|
||||
// Show action buttons
|
||||
this.actionButtons.classList.remove('hidden');
|
||||
}
|
||||
|
||||
async revealAnswer() {
|
||||
const revealBtn = document.getElementById('reveal-answer-btn');
|
||||
const answerContent = document.getElementById('answer-content');
|
||||
const currentQuestion = this.questionInput.value.trim();
|
||||
|
||||
// Show loading state
|
||||
revealBtn.innerHTML = `<span>⏳</span> ${this.currentLanguage === 'de' ? 'Lädt...' : 'Loading...'}`;
|
||||
revealBtn.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/reveal-answer', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
question: currentQuestion,
|
||||
language: this.currentLanguage,
|
||||
userAnswers: this.userAnswers || []
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Hide the reveal button
|
||||
revealBtn.style.display = 'none';
|
||||
|
||||
// Show the answer with animation
|
||||
answerContent.innerHTML = `
|
||||
<div class="final-answer">
|
||||
<h4>${this.currentLanguage === 'de' ? '🎓 Die Antwort:' : '🎓 The Answer:'}</h4>
|
||||
<div class="answer-text">${data.answer}</div>
|
||||
${data.explanation ? `
|
||||
<div class="answer-explanation">
|
||||
<h5>${this.currentLanguage === 'de' ? '💡 Warum ist das so?' : '💡 Why is this so?'}</h5>
|
||||
<p>${data.explanation}</p>
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="learning-encouragement">
|
||||
<p>${this.currentLanguage === 'de'
|
||||
? '🌟 Großartig! Du hast durch deine eigenen Gedanken gelernt!'
|
||||
: '🌟 Great! You learned by thinking it through yourself!'}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
answerContent.classList.remove('hidden');
|
||||
answerContent.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
|
||||
} else {
|
||||
throw new Error(data.error || 'Failed to get answer');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error revealing answer:', error);
|
||||
|
||||
// Fallback answer display
|
||||
revealBtn.innerHTML = `<span>🎯</span> ${this.currentLanguage === 'de' ? 'Antwort zeigen!' : 'Reveal Answer!'}`;
|
||||
revealBtn.disabled = false;
|
||||
|
||||
answerContent.innerHTML = `
|
||||
<div class="final-answer error">
|
||||
<h4>${this.currentLanguage === 'de' ? '🤔 Hmm...' : '🤔 Hmm...'}</h4>
|
||||
<p>${this.currentLanguage === 'de'
|
||||
? 'Ich kann die Antwort gerade nicht laden. Aber du hast schon toll nachgedacht! Frag gerne einen Erwachsenen oder schaue in einem Buch nach.'
|
||||
: 'I can\'t load the answer right now. But you\'ve done great thinking! Feel free to ask an adult or look it up in a book.'}</p>
|
||||
</div>
|
||||
`;
|
||||
answerContent.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
generateThinkingGuidance(question) {
|
||||
// This is where the AI magic happens - generating guided questions instead of answers
|
||||
const questionLower = question.toLowerCase();
|
||||
const t = translations[this.currentLanguage];
|
||||
|
||||
// Categories of questions and their thinking frameworks
|
||||
const frameworks = t.thinkingFrameworks;
|
||||
|
||||
// Determine which framework to use
|
||||
let selectedFramework = frameworks.general;
|
||||
|
||||
// Check for science keywords (works for both languages)
|
||||
const scienceKeywords = ['why', 'warum', 'how', 'wie', 'what happens', 'was passiert', 'science', 'wissenschaft', 'nature', 'natur', 'sky', 'himmel', 'water', 'wasser', 'animals', 'tiere', 'plants', 'pflanzen', 'earth', 'erde', 'space', 'weltall'];
|
||||
const mathKeywords = ['calculate', 'rechnen', 'math', 'mathe', 'numbers', 'zahlen', 'count', 'zählen', 'add', 'addieren', 'subtract', 'subtrahieren', 'multiply', 'multiplizieren', 'divide', 'dividieren', 'solve', 'lösen'];
|
||||
const techKeywords = ['computer', 'internet', 'phone', 'telefon', 'robot', 'roboter', 'machine', 'maschine', 'technology', 'technologie', 'digital'];
|
||||
|
||||
if (scienceKeywords.some(keyword => questionLower.includes(keyword))) {
|
||||
selectedFramework = frameworks.science;
|
||||
} else if (mathKeywords.some(keyword => questionLower.includes(keyword))) {
|
||||
selectedFramework = frameworks.math;
|
||||
} else if (techKeywords.some(keyword => questionLower.includes(keyword))) {
|
||||
selectedFramework = frameworks.technology;
|
||||
}
|
||||
|
||||
return {
|
||||
question: question,
|
||||
framework: selectedFramework,
|
||||
encouragement: this.getEncouragement(),
|
||||
actionSuggestions: this.getActionSuggestions(questionLower)
|
||||
};
|
||||
}
|
||||
|
||||
getEncouragement() {
|
||||
const t = translations[this.currentLanguage];
|
||||
const encouragements = t.encouragements;
|
||||
return encouragements[Math.floor(Math.random() * encouragements.length)];
|
||||
}
|
||||
|
||||
getActionSuggestions(questionLower) {
|
||||
const t = translations[this.currentLanguage];
|
||||
const suggestions = {
|
||||
research: [],
|
||||
experiment: [],
|
||||
discuss: []
|
||||
};
|
||||
|
||||
// Add specific suggestions based on question content (works for both languages)
|
||||
if (questionLower.includes('plant') || questionLower.includes('grow') || questionLower.includes('pflanze') || questionLower.includes('wachsen')) {
|
||||
if (this.currentLanguage === 'de') {
|
||||
suggestions.research.push("Informiere dich über den Lebenszyklus von Pflanzen");
|
||||
suggestions.experiment.push("Versuche, Samen unter verschiedenen Bedingungen wachsen zu lassen");
|
||||
suggestions.discuss.push("Frag einen Gärtner nach Pflanzenpflege");
|
||||
} else {
|
||||
suggestions.research.push("Look up the life cycle of plants");
|
||||
suggestions.experiment.push("Try growing seeds in different conditions");
|
||||
suggestions.discuss.push("Ask a gardener about plant care");
|
||||
}
|
||||
}
|
||||
|
||||
if (questionLower.includes('sky') || questionLower.includes('blue') || questionLower.includes('himmel') || questionLower.includes('blau')) {
|
||||
if (this.currentLanguage === 'de') {
|
||||
suggestions.research.push("Lerne über Licht und wie es sich bewegt");
|
||||
suggestions.experiment.push("Benutze ein Prisma, um Licht in Farben aufzuteilen");
|
||||
suggestions.discuss.push("Sprich mit einem Wissenschaftslehrer über Licht");
|
||||
} else {
|
||||
suggestions.research.push("Learn about light and how it travels");
|
||||
suggestions.experiment.push("Use a prism to split light into colors");
|
||||
suggestions.discuss.push("Talk to a science teacher about light");
|
||||
}
|
||||
}
|
||||
|
||||
// Default suggestions if none match
|
||||
if (suggestions.research.length === 0) {
|
||||
if (this.currentLanguage === 'de') {
|
||||
suggestions.research = [
|
||||
"Suche nach kinderfreundlichen Artikeln über dein Thema",
|
||||
"Finde Bücher in der Bibliothek über dieses Thema",
|
||||
"Schaue Lernvideos (mit einem Erwachsenen)"
|
||||
];
|
||||
} else {
|
||||
suggestions.research = [
|
||||
"Search for kid-friendly articles about your topic",
|
||||
"Find books in the library about this subject",
|
||||
"Watch educational videos (with a grown-up)"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (suggestions.experiment.length === 0) {
|
||||
if (this.currentLanguage === 'de') {
|
||||
suggestions.experiment = [
|
||||
"Denke an sichere Wege, deine Ideen zu testen",
|
||||
"Mache Beobachtungen und schreibe sie auf",
|
||||
"Versuche einfache Experimente mit Haushaltsgegenständen"
|
||||
];
|
||||
} else {
|
||||
suggestions.experiment = [
|
||||
"Think of safe ways to test your ideas",
|
||||
"Make observations and take notes",
|
||||
"Try simple experiments with household items"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (suggestions.discuss.length === 0) {
|
||||
if (this.currentLanguage === 'de') {
|
||||
suggestions.discuss = [
|
||||
"Frag deinen Lehrer, was er denkt",
|
||||
"Sprich mit Familienmitgliedern über ihre Erfahrungen",
|
||||
"Teile deine Frage mit Freunden"
|
||||
];
|
||||
} else {
|
||||
suggestions.discuss = [
|
||||
"Ask your teacher what they think",
|
||||
"Talk to family members about their experiences",
|
||||
"Share your question with friends"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
displayThinkingProcess(guidance) {
|
||||
const t = translations[this.currentLanguage];
|
||||
|
||||
// Clear previous content
|
||||
this.thinkingSteps.innerHTML = '';
|
||||
|
||||
// Add encouragement
|
||||
const encouragementDiv = document.createElement('div');
|
||||
encouragementDiv.className = 'thinking-step highlight';
|
||||
encouragementDiv.innerHTML = `
|
||||
<h4>🎉 ${guidance.encouragement}</h4>
|
||||
<p>${this.currentLanguage === 'de' ? 'Lass uns das Schritt für Schritt erforschen und dir helfen, ein fantastischer Problemlöser zu werden!' : 'Let\'s explore this step by step and help you become a fantastic problem solver!'}</p>
|
||||
`;
|
||||
this.thinkingSteps.appendChild(encouragementDiv);
|
||||
|
||||
// Add thinking steps with animation delay
|
||||
guidance.framework.steps.forEach((step, index) => {
|
||||
setTimeout(() => {
|
||||
const stepDiv = document.createElement('div');
|
||||
stepDiv.className = 'thinking-step';
|
||||
stepDiv.innerHTML = `
|
||||
<h4>${step.title}</h4>
|
||||
<p>${step.content}</p>
|
||||
`;
|
||||
this.thinkingSteps.appendChild(stepDiv);
|
||||
|
||||
// Show action buttons after all steps are displayed
|
||||
if (index === guidance.framework.steps.length - 1) {
|
||||
setTimeout(() => {
|
||||
this.actionButtons.classList.remove('hidden');
|
||||
this.actionButtons.style.animation = 'fadeInUp 0.6s ease-out';
|
||||
|
||||
// Store suggestions for action buttons
|
||||
this.currentSuggestions = guidance.actionSuggestions;
|
||||
}, 300);
|
||||
}
|
||||
}, index * 500);
|
||||
});
|
||||
|
||||
// Show thinking section
|
||||
this.thinkingSection.classList.remove('hidden');
|
||||
this.thinkingSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
showActionGuidance(actionType) {
|
||||
const t = translations[this.currentLanguage];
|
||||
const actionTitles = t.actionTitles;
|
||||
|
||||
const suggestions = this.currentSuggestions[actionType];
|
||||
|
||||
let content = `<div class="action-guidance">
|
||||
<h4>${actionTitles[actionType]}</h4>
|
||||
<ul>`;
|
||||
|
||||
suggestions.forEach(suggestion => {
|
||||
content += `<li>${suggestion}</li>`;
|
||||
});
|
||||
|
||||
content += `</ul>
|
||||
<p><strong>${this.currentLanguage === 'de' ? 'Denk daran:' : 'Remember:'}</strong> ${t.actionReminder}</p>
|
||||
</div>`;
|
||||
|
||||
// Create and show modal or add to thinking steps
|
||||
const guidanceDiv = document.createElement('div');
|
||||
guidanceDiv.className = 'thinking-step highlight';
|
||||
guidanceDiv.innerHTML = content;
|
||||
guidanceDiv.style.animation = 'slideInLeft 0.5s ease-out';
|
||||
|
||||
this.thinkingSteps.appendChild(guidanceDiv);
|
||||
guidanceDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
if (this.loadingOverlay) {
|
||||
this.loadingOverlay.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
if (this.loadingOverlay) {
|
||||
this.loadingOverlay.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
showMessage(message, type = 'info') {
|
||||
// Create a temporary message element
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = `message-popup ${type}`;
|
||||
messageDiv.textContent = message;
|
||||
messageDiv.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: ${type === 'error' ? '#fed7d7' : type === 'warning' ? '#fef5e7' : '#e6fffa'};
|
||||
color: ${type === 'error' ? '#c53030' : type === 'warning' ? '#d69e2e' : '#00695c'};
|
||||
padding: 15px 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||
z-index: 1001;
|
||||
animation: slideInRight 0.3s ease-out;
|
||||
max-width: 300px;
|
||||
`;
|
||||
|
||||
document.body.appendChild(messageDiv);
|
||||
|
||||
// Remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
messageDiv.style.animation = 'slideOutRight 0.3s ease-out';
|
||||
setTimeout(() => messageDiv.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the application when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('DOM loaded, initializing KidsAI Explorer...');
|
||||
|
||||
// Hide loading screen immediately on page load
|
||||
const loadingOverlay = document.getElementById('loading');
|
||||
if (loadingOverlay) {
|
||||
loadingOverlay.classList.add('hidden');
|
||||
console.log('Loading overlay hidden on page load');
|
||||
}
|
||||
|
||||
// Emergency fallback - hide loading after 1 second regardless
|
||||
setTimeout(() => {
|
||||
if (loadingOverlay && !loadingOverlay.classList.contains('hidden')) {
|
||||
loadingOverlay.classList.add('hidden');
|
||||
console.log('Emergency: Loading overlay hidden by timeout');
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Check if translations are loaded
|
||||
if (typeof translations === 'undefined') {
|
||||
console.error('Translations not loaded! Creating fallback...');
|
||||
window.translations = {
|
||||
en: {
|
||||
title: "KidsAI Explorer",
|
||||
tagline: "Think, Learn, Discover Together!",
|
||||
encouragements: ["Great question! You're thinking like a real scientist! 🔬"],
|
||||
thinkingFrameworks: {
|
||||
general: {
|
||||
steps: [
|
||||
{
|
||||
title: "🎯 Let's break this down",
|
||||
content: "What's the main thing you want to understand?"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
actionTitles: {
|
||||
research: "🔍 Research Ideas",
|
||||
experiment: "🧪 Experiment Ideas",
|
||||
discuss: "💬 Discussion Ideas"
|
||||
},
|
||||
actionReminder: "Remember: The goal is to discover the answer yourself!"
|
||||
},
|
||||
de: {
|
||||
title: "KidsAI Explorer",
|
||||
tagline: "Denken, Lernen, Entdecken - Zusammen!",
|
||||
encouragements: ["Tolle Frage! Du denkst wie ein echter Wissenschaftler! 🔬"],
|
||||
thinkingFrameworks: {
|
||||
general: {
|
||||
steps: [
|
||||
{
|
||||
title: "🎯 Lass uns das aufteilen",
|
||||
content: "Was ist das Wichtigste, was du verstehen möchtest?"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
actionTitles: {
|
||||
research: "🔍 Forschungsideen",
|
||||
experiment: "🧪 Experiment-Ideen",
|
||||
discuss: "💬 Diskussionsideen"
|
||||
},
|
||||
actionReminder: "Denk daran: Das Ziel ist es, die Antwort selbst zu entdecken!"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Global instance for external access
|
||||
let kidsAI;
|
||||
|
||||
// Initialize when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('🚀 DOM loaded, initializing KidsAI...');
|
||||
|
||||
// Add a visible indicator
|
||||
const body = document.body;
|
||||
const indicator = document.createElement('div');
|
||||
indicator.id = 'debug-indicator';
|
||||
indicator.innerHTML = '✅ KidsAI Loading...';
|
||||
indicator.style.cssText = 'position:fixed;top:10px;left:10px;background:green;color:white;padding:5px;z-index:9999;border-radius:5px;font-size:12px;';
|
||||
body.appendChild(indicator);
|
||||
|
||||
try {
|
||||
kidsAI = new KidsAIExplorer();
|
||||
window.kidsAI = kidsAI; // Make it globally accessible
|
||||
console.log('KidsAI Explorer initialized successfully');
|
||||
indicator.innerHTML = '✅ KidsAI Ready!';
|
||||
indicator.style.background = 'green';
|
||||
} catch (error) {
|
||||
console.error('Error initializing KidsAIExplorer:', error);
|
||||
indicator.innerHTML = '❌ KidsAI Error: ' + error.message;
|
||||
indicator.style.background = 'red';
|
||||
// Ensure loading is hidden even if there's an error
|
||||
const loadingOverlay = document.getElementById('loading');
|
||||
if (loadingOverlay) {
|
||||
loadingOverlay.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Add some fun interactive effects
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Add sparkle effect on click
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('ask-btn') || e.target.classList.contains('action-btn')) {
|
||||
createSparkleEffect(e.target);
|
||||
}
|
||||
});
|
||||
|
||||
// Add hover sound effect simulation (visual feedback)
|
||||
const interactiveElements = document.querySelectorAll('.suggestion-card, .ask-btn, .action-btn');
|
||||
interactiveElements.forEach(element => {
|
||||
element.addEventListener('mouseenter', () => {
|
||||
element.style.transform = element.style.transform || 'translateY(-2px)';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createSparkleEffect(element) {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const sparkle = document.createElement('div');
|
||||
sparkle.innerHTML = '✨';
|
||||
sparkle.style.cssText = `
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
font-size: 1rem;
|
||||
animation: sparkle 1s ease-out forwards;
|
||||
z-index: 1000;
|
||||
`;
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
sparkle.style.left = (rect.left + Math.random() * rect.width) + 'px';
|
||||
sparkle.style.top = (rect.top + Math.random() * rect.height) + 'px';
|
||||
|
||||
document.body.appendChild(sparkle);
|
||||
|
||||
setTimeout(() => sparkle.remove(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Add sparkle animation CSS
|
||||
const sparkleCSS = `
|
||||
@keyframes sparkle {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(0);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: translateY(-20px) scale(1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateY(-40px) scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(100px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOutRight {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateX(100px);
|
||||
}
|
||||
}
|
||||
|
||||
.action-guidance ul {
|
||||
margin: 15px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.action-guidance li {
|
||||
margin: 8px 0;
|
||||
color: #4a5568;
|
||||
font-weight: 500;
|
||||
}
|
||||
`;
|
||||
|
||||
// Inject the sparkle CSS
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.textContent = sparkleCSS;
|
||||
document.head.appendChild(styleSheet);
|
||||
897
html/kidsai/script_broken.js
Normal file
897
html/kidsai/script_broken.js
Normal file
@@ -0,0 +1,897 @@
|
||||
// KidsAI Explorer - Interactive Learning Assistant
|
||||
class KidsAIExplorer {
|
||||
constructor() {
|
||||
this.currentLanguage = localStorage.getItem('kidsai-language') || 'en';
|
||||
|
||||
// Get DOM elements with error handling
|
||||
this.questionInput = document.getElementById('question-input');
|
||||
this.askButton = document.getElementById('ask-button');
|
||||
this.thinkingSection = document.getElementById('thinking-section');
|
||||
this.thinkingSteps = document.getElementById('thinking-steps');
|
||||
this.actionButtons = document.getElementById('action-buttons');
|
||||
this.loadingOverlay = document.getElementById('loading');
|
||||
this.suggestionCards = document.querySelectorAll('.suggestion-card');
|
||||
this.langButtons = document.querySelectorAll('.lang-btn');
|
||||
|
||||
// Ensure loading is hidden initially
|
||||
if (this.loadingOverlay) {
|
||||
this.loadingOverlay.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Initialize only if we have the required elements
|
||||
if (this.questionInput && this.askButton) {
|
||||
this.initializeLanguage();
|
||||
this.initializeEventListeners();
|
||||
this.initializeAnimations();
|
||||
} else {
|
||||
console.error('Required DOM elements not found');
|
||||
}
|
||||
}
|
||||
|
||||
initializeLanguage() {
|
||||
// Set active language button
|
||||
this.langButtons.forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.lang === this.currentLanguage);
|
||||
});
|
||||
|
||||
// Apply translations
|
||||
this.applyTranslations();
|
||||
}
|
||||
|
||||
applyTranslations() {
|
||||
try {
|
||||
const t = translations[this.currentLanguage];
|
||||
if (!t) {
|
||||
console.warn('Translations not available for language:', this.currentLanguage);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update all elements with data-translate attribute
|
||||
document.querySelectorAll('[data-translate]').forEach(element => {
|
||||
const key = element.getAttribute('data-translate');
|
||||
if (t[key]) {
|
||||
element.textContent = t[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Update placeholder
|
||||
const placeholder = document.querySelector('[data-translate-placeholder]');
|
||||
if (placeholder) {
|
||||
const key = placeholder.getAttribute('data-translate-placeholder');
|
||||
if (t[key]) {
|
||||
placeholder.placeholder = t[key];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error applying translations:', error);
|
||||
}
|
||||
}
|
||||
|
||||
switchLanguage(lang) {
|
||||
this.currentLanguage = lang;
|
||||
localStorage.setItem('kidsai-language', lang);
|
||||
|
||||
// Update active button
|
||||
this.langButtons.forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.lang === lang);
|
||||
});
|
||||
|
||||
// Apply translations
|
||||
this.applyTranslations();
|
||||
|
||||
// Clear thinking section if visible
|
||||
if (!this.thinkingSection.classList.contains('hidden')) {
|
||||
this.thinkingSection.classList.add('hidden');
|
||||
this.actionButtons.classList.add('hidden');
|
||||
this.thinkingSteps.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
initializeEventListeners() {
|
||||
// Language switching
|
||||
this.langButtons.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
this.switchLanguage(btn.dataset.lang);
|
||||
});
|
||||
});
|
||||
|
||||
// Main question submission
|
||||
this.askButton.addEventListener('click', () => this.handleQuestion());
|
||||
this.questionInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
this.handleQuestion();
|
||||
}
|
||||
});
|
||||
|
||||
// Suggestion cards
|
||||
this.suggestionCards.forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
const questionKey = `data-question-${this.currentLanguage}`;
|
||||
const question = card.getAttribute(questionKey);
|
||||
this.questionInput.value = question;
|
||||
this.handleQuestion();
|
||||
});
|
||||
});
|
||||
|
||||
// Action buttons
|
||||
document.getElementById('research-btn').addEventListener('click', () => {
|
||||
this.showActionGuidance('research');
|
||||
});
|
||||
|
||||
document.getElementById('experiment-btn').addEventListener('click', () => {
|
||||
this.showActionGuidance('experiment');
|
||||
});
|
||||
|
||||
document.getElementById('discuss-btn').addEventListener('click', () => {
|
||||
this.showActionGuidance('discuss');
|
||||
});
|
||||
}
|
||||
|
||||
initializeAnimations() {
|
||||
// Add floating animation to suggestion cards
|
||||
this.suggestionCards.forEach((card, index) => {
|
||||
card.style.animationDelay = `${index * 0.1}s`;
|
||||
card.style.animation = 'fadeInUp 0.6s ease-out both';
|
||||
});
|
||||
}
|
||||
|
||||
async handleQuestion() {
|
||||
const question = this.questionInput.value.trim();
|
||||
if (!question) {
|
||||
this.showMessage('Please ask me something first! 🤔', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
this.showLoading();
|
||||
|
||||
try {
|
||||
// Call the AI backend
|
||||
const response = await fetch('/api/ask', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
question: question,
|
||||
language: this.currentLanguage
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Display AI-powered guidance
|
||||
this.displayAIGuidance(data.guidance, data.fallback);
|
||||
} else {
|
||||
throw new Error(data.error || 'Unknown error');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error getting AI guidance:', error);
|
||||
|
||||
// Fallback to local guidance if backend fails
|
||||
this.showMessage('Using offline guidance...', 'info');
|
||||
const localGuidance = this.generateThinkingGuidance(question);
|
||||
this.displayThinkingProcess(localGuidance);
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
displayAIGuidance(guidance, isFallback) {
|
||||
// Clear any previous content
|
||||
this.thinkingSteps.innerHTML = '';
|
||||
this.currentStep = 0;
|
||||
this.userAnswers = [];
|
||||
|
||||
// Create conversation container
|
||||
const conversationContainer = document.createElement('div');
|
||||
conversationContainer.className = 'conversation-container';
|
||||
this.thinkingSteps.appendChild(conversationContainer);
|
||||
|
||||
// Show initial encouragement
|
||||
const welcomeStep = document.createElement('div');
|
||||
welcomeStep.className = 'conversation-step visible';
|
||||
welcomeStep.innerHTML = `
|
||||
<div class="ai-message teacher">
|
||||
<p>${guidance.encouragement}</p>
|
||||
${isFallback ? '<small>💻 Offline mode</small>' : '<small>✨ AI-powered</small>'}
|
||||
</div>
|
||||
`;
|
||||
conversationContainer.appendChild(welcomeStep);
|
||||
|
||||
// Store guidance for step-by-step interaction
|
||||
this.currentGuidance = guidance;
|
||||
this.conversationContainer = conversationContainer;
|
||||
|
||||
// Start the first question after a delay
|
||||
setTimeout(() => {
|
||||
this.showNextQuestion();
|
||||
}, 1500);
|
||||
|
||||
// Show the thinking section
|
||||
setTimeout(() => {
|
||||
this.thinkingSection.classList.remove('hidden');
|
||||
}, 500);
|
||||
}
|
||||
|
||||
showNextQuestion() {
|
||||
if (this.currentStep >= this.currentGuidance.steps.length) {
|
||||
this.showCompletionMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
const step = this.currentGuidance.steps[this.currentStep];
|
||||
const stepDiv = document.createElement('div');
|
||||
stepDiv.className = 'conversation-step';
|
||||
stepDiv.innerHTML = `
|
||||
<div class="progress-timeline">
|
||||
<div class="progress-step current">
|
||||
<div class="step-indicator">${step.id}</div>
|
||||
<div class="ai-message">
|
||||
<p>${step.text}</p>
|
||||
</div>
|
||||
<div class="user-input-area" id="input-area-${this.currentStep}">
|
||||
<textarea
|
||||
placeholder="${this.currentLanguage === 'de' ? 'Schreibe deine Gedanken hier...' : 'Write your thoughts here...'}"
|
||||
rows="3"
|
||||
id="user-input-${this.currentStep}"
|
||||
></textarea>
|
||||
<div class="input-actions">
|
||||
<span class="input-hint">${this.currentLanguage === 'de' ? '💡 Nimm dir Zeit zum Nachdenken!' : '💡 Take your time to think!'}</span>
|
||||
<button class="reply-btn" onclick="kidsAI.submitAnswer(${this.currentStep})">
|
||||
<span>💬</span>
|
||||
${this.currentLanguage === 'de' ? 'Antworten' : 'Reply'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.conversationContainer.appendChild(stepDiv);
|
||||
|
||||
// Animate in
|
||||
setTimeout(() => {
|
||||
stepDiv.classList.add('visible');
|
||||
}, 100);
|
||||
|
||||
// Scroll to the new question
|
||||
setTimeout(() => {
|
||||
stepDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}, 600);
|
||||
}
|
||||
|
||||
submitAnswer(stepIndex) {
|
||||
const textarea = document.getElementById(`user-input-${stepIndex}`);
|
||||
const inputArea = document.getElementById(`input-area-${stepIndex}`);
|
||||
const answer = textarea.value.trim();
|
||||
|
||||
if (!answer) {
|
||||
this.showMessage(
|
||||
this.currentLanguage === 'de' ? 'Bitte schreibe deine Gedanken auf! 🤔' : 'Please write down your thoughts! 🤔',
|
||||
'warning'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the answer
|
||||
this.userAnswers[stepIndex] = answer;
|
||||
|
||||
// Mark as answered
|
||||
inputArea.classList.add('answered');
|
||||
textarea.disabled = true;
|
||||
|
||||
// Show encouragement
|
||||
const encouragement = document.createElement('div');
|
||||
encouragement.className = 'encouragement-feedback';
|
||||
encouragement.innerHTML = this.getEncouragementMessage();
|
||||
inputArea.appendChild(encouragement);
|
||||
|
||||
// Move to next question
|
||||
this.currentStep++;
|
||||
setTimeout(() => {
|
||||
this.showNextQuestion();
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
getEncouragementMessage() {
|
||||
const messages = this.currentLanguage === 'de' ? [
|
||||
'🌟 Toll! Du denkst wirklich gut nach!',
|
||||
'👏 Ausgezeichnet! Weiter so!',
|
||||
'🎯 Super Antwort! Du bist auf dem richtigen Weg!',
|
||||
'✨ Fantastisch! Du lernst wie ein echter Forscher!'
|
||||
] : [
|
||||
'🌟 Great! You\'re thinking really well!',
|
||||
'👏 Excellent! Keep it up!',
|
||||
'🎯 Super answer! You\'re on the right track!',
|
||||
'✨ Fantastic! You\'re learning like a real scientist!'
|
||||
];
|
||||
|
||||
return messages[Math.floor(Math.random() * messages.length)];
|
||||
}
|
||||
|
||||
showCompletionMessage() {
|
||||
const completionDiv = document.createElement('div');
|
||||
completionDiv.className = 'conversation-step';
|
||||
completionDiv.innerHTML = `
|
||||
<div class="ai-message teacher">
|
||||
<h4>${this.currentLanguage === 'de' ? '🎉 Fantastisch gemacht!' : '🎉 Fantastic Job!'}</h4>
|
||||
<p>${this.currentLanguage === 'de'
|
||||
? 'Du hast wirklich gut nachgedacht! Jetzt bist du bereit, die richtige Antwort zu entdecken.'
|
||||
: 'You\'ve done some great thinking! Now you\'re ready to discover the actual answer.'}</p>
|
||||
</div>
|
||||
<div class="next-question-prompt">
|
||||
<button id="reveal-answer-btn" class="reply-btn" style="font-size: 1.1rem; padding: 15px 30px;">
|
||||
<span>🎯</span>
|
||||
${this.currentLanguage === 'de' ? 'Antwort zeigen!' : 'Reveal Answer!'}
|
||||
</button>
|
||||
<div id="answer-content" class="answer-content hidden" style="margin-top: 20px;">
|
||||
<!-- Answer will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.conversationContainer.appendChild(completionDiv);
|
||||
|
||||
// Animate in
|
||||
setTimeout(() => {
|
||||
completionDiv.classList.add('visible');
|
||||
}, 100);
|
||||
|
||||
// Add click handler for reveal button
|
||||
setTimeout(() => {
|
||||
document.getElementById('reveal-answer-btn').addEventListener('click', () => {
|
||||
this.revealAnswer();
|
||||
});
|
||||
}, 200);
|
||||
|
||||
// Show action buttons
|
||||
this.actionButtons.classList.remove('hidden');
|
||||
}
|
||||
|
||||
async revealAnswer() {
|
||||
const revealBtn = document.getElementById('reveal-answer-btn');
|
||||
const answerContent = document.getElementById('answer-content');
|
||||
const currentQuestion = this.questionInput.value.trim();
|
||||
|
||||
// Show loading state
|
||||
revealBtn.innerHTML = `<span>⏳</span> ${this.currentLanguage === 'de' ? 'Lädt...' : 'Loading...'}`;
|
||||
revealBtn.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/reveal-answer', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
question: currentQuestion,
|
||||
language: this.currentLanguage,
|
||||
userAnswers: this.userAnswers || []
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Hide the reveal button
|
||||
revealBtn.style.display = 'none';
|
||||
|
||||
// Show the answer with animation
|
||||
answerContent.innerHTML = `
|
||||
<div class="final-answer">
|
||||
<h4>${this.currentLanguage === 'de' ? '🎓 Die Antwort:' : '🎓 The Answer:'}</h4>
|
||||
<div class="answer-text">${data.answer}</div>
|
||||
${data.explanation ? `
|
||||
<div class="answer-explanation">
|
||||
<h5>${this.currentLanguage === 'de' ? '💡 Warum ist das so?' : '💡 Why is this so?'}</h5>
|
||||
<p>${data.explanation}</p>
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="learning-encouragement">
|
||||
<p>${this.currentLanguage === 'de'
|
||||
? '🌟 Großartig! Du hast durch deine eigenen Gedanken gelernt!'
|
||||
: '🌟 Great! You learned by thinking it through yourself!'}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
answerContent.classList.remove('hidden');
|
||||
answerContent.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
|
||||
} else {
|
||||
throw new Error(data.error || 'Failed to get answer');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error revealing answer:', error);
|
||||
|
||||
// Fallback answer display
|
||||
revealBtn.innerHTML = `<span>🎯</span> ${this.currentLanguage === 'de' ? 'Antwort zeigen!' : 'Reveal Answer!'}`;
|
||||
revealBtn.disabled = false;
|
||||
|
||||
answerContent.innerHTML = `
|
||||
<div class="final-answer error">
|
||||
<h4>${this.currentLanguage === 'de' ? '🤔 Hmm...' : '🤔 Hmm...'}</h4>
|
||||
<p>${this.currentLanguage === 'de'
|
||||
? 'Ich kann die Antwort gerade nicht laden. Aber du hast schon toll nachgedacht! Frag gerne einen Erwachsenen oder schaue in einem Buch nach.'
|
||||
: 'I can\'t load the answer right now. But you\'ve done great thinking! Feel free to ask an adult or look it up in a book.'}</p>
|
||||
</div>
|
||||
`;
|
||||
answerContent.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Display the answer
|
||||
answerContent.innerHTML = `
|
||||
<div class="answer-box">
|
||||
<div class="answer-header">
|
||||
<span class="answer-icon">💡</span>
|
||||
<h4>${this.currentLanguage === 'de' ? 'Die Antwort:' : 'The Answer:'}</h4>
|
||||
<small class="answer-source">${data.fallback ? '📚 Local' : '✨ AI'} • ${data.answer.source}</small>
|
||||
</div>
|
||||
<div class="answer-text">
|
||||
${data.answer.text}
|
||||
</div>
|
||||
<div class="answer-footer">
|
||||
<p>${this.currentLanguage === 'de'
|
||||
? '🎉 Großartig! Wie nah warst du mit deinen Überlegungen?'
|
||||
: '🎉 Awesome! How close were your thoughts to the answer?'}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
answerContent.classList.remove('hidden');
|
||||
revealBtn.style.display = 'none';
|
||||
|
||||
// Animate answer in
|
||||
answerContent.style.opacity = '0';
|
||||
answerContent.style.transform = 'translateY(20px)';
|
||||
setTimeout(() => {
|
||||
answerContent.style.transition = 'all 0.6s ease-out';
|
||||
answerContent.style.opacity = '1';
|
||||
answerContent.style.transform = 'translateY(0)';
|
||||
}, 100);
|
||||
|
||||
} else {
|
||||
throw new Error(data.error || 'Failed to get answer');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error getting answer:', error);
|
||||
revealBtn.innerHTML = `<span class="btn-icon">❌</span> ${this.currentLanguage === 'de' ? 'Fehler' : 'Error'}`;
|
||||
setTimeout(() => {
|
||||
revealBtn.innerHTML = `<span class="btn-icon">🎯</span> ${this.currentLanguage === 'de' ? 'Nochmal versuchen' : 'Try Again'}`;
|
||||
revealBtn.disabled = false;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
generateThinkingGuidance(question) {
|
||||
// This is where the AI magic happens - generating guided questions instead of answers
|
||||
const questionLower = question.toLowerCase();
|
||||
const t = translations[this.currentLanguage];
|
||||
|
||||
// Categories of questions and their thinking frameworks
|
||||
const frameworks = t.thinkingFrameworks;
|
||||
|
||||
// Determine which framework to use
|
||||
let selectedFramework = frameworks.general;
|
||||
|
||||
// Check for science keywords (works for both languages)
|
||||
const scienceKeywords = ['why', 'warum', 'how', 'wie', 'what happens', 'was passiert', 'science', 'wissenschaft', 'nature', 'natur', 'sky', 'himmel', 'water', 'wasser', 'animals', 'tiere', 'plants', 'pflanzen', 'earth', 'erde', 'space', 'weltall'];
|
||||
const mathKeywords = ['calculate', 'rechnen', 'math', 'mathe', 'numbers', 'zahlen', 'count', 'zählen', 'add', 'addieren', 'subtract', 'subtrahieren', 'multiply', 'multiplizieren', 'divide', 'dividieren', 'solve', 'lösen'];
|
||||
const techKeywords = ['computer', 'internet', 'phone', 'telefon', 'robot', 'roboter', 'machine', 'maschine', 'technology', 'technologie', 'digital'];
|
||||
|
||||
if (scienceKeywords.some(keyword => questionLower.includes(keyword))) {
|
||||
selectedFramework = frameworks.science;
|
||||
} else if (mathKeywords.some(keyword => questionLower.includes(keyword))) {
|
||||
selectedFramework = frameworks.math;
|
||||
} else if (techKeywords.some(keyword => questionLower.includes(keyword))) {
|
||||
selectedFramework = frameworks.technology;
|
||||
}
|
||||
|
||||
return {
|
||||
question: question,
|
||||
framework: selectedFramework,
|
||||
encouragement: this.getEncouragement(),
|
||||
actionSuggestions: this.getActionSuggestions(questionLower)
|
||||
};
|
||||
}
|
||||
|
||||
getEncouragement() {
|
||||
const t = translations[this.currentLanguage];
|
||||
const encouragements = t.encouragements;
|
||||
return encouragements[Math.floor(Math.random() * encouragements.length)];
|
||||
}
|
||||
|
||||
getActionSuggestions(questionLower) {
|
||||
const t = translations[this.currentLanguage];
|
||||
const suggestions = {
|
||||
research: [],
|
||||
experiment: [],
|
||||
discuss: []
|
||||
};
|
||||
|
||||
// Add specific suggestions based on question content (works for both languages)
|
||||
if (questionLower.includes('plant') || questionLower.includes('grow') || questionLower.includes('pflanze') || questionLower.includes('wachsen')) {
|
||||
if (this.currentLanguage === 'de') {
|
||||
suggestions.research.push("Informiere dich über den Lebenszyklus von Pflanzen");
|
||||
suggestions.experiment.push("Versuche, Samen unter verschiedenen Bedingungen wachsen zu lassen");
|
||||
suggestions.discuss.push("Frag einen Gärtner nach Pflanzenpflege");
|
||||
} else {
|
||||
suggestions.research.push("Look up the life cycle of plants");
|
||||
suggestions.experiment.push("Try growing seeds in different conditions");
|
||||
suggestions.discuss.push("Ask a gardener about plant care");
|
||||
}
|
||||
}
|
||||
|
||||
if (questionLower.includes('sky') || questionLower.includes('blue') || questionLower.includes('himmel') || questionLower.includes('blau')) {
|
||||
if (this.currentLanguage === 'de') {
|
||||
suggestions.research.push("Lerne über Licht und wie es sich bewegt");
|
||||
suggestions.experiment.push("Benutze ein Prisma, um Licht in Farben aufzuteilen");
|
||||
suggestions.discuss.push("Sprich mit einem Wissenschaftslehrer über Licht");
|
||||
} else {
|
||||
suggestions.research.push("Learn about light and how it travels");
|
||||
suggestions.experiment.push("Use a prism to split light into colors");
|
||||
suggestions.discuss.push("Talk to a science teacher about light");
|
||||
}
|
||||
}
|
||||
|
||||
// Default suggestions if none match
|
||||
if (suggestions.research.length === 0) {
|
||||
if (this.currentLanguage === 'de') {
|
||||
suggestions.research = [
|
||||
"Suche nach kinderfreundlichen Artikeln über dein Thema",
|
||||
"Finde Bücher in der Bibliothek über dieses Thema",
|
||||
"Schaue Lernvideos (mit einem Erwachsenen)"
|
||||
];
|
||||
} else {
|
||||
suggestions.research = [
|
||||
"Search for kid-friendly articles about your topic",
|
||||
"Find books in the library about this subject",
|
||||
"Watch educational videos (with a grown-up)"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (suggestions.experiment.length === 0) {
|
||||
if (this.currentLanguage === 'de') {
|
||||
suggestions.experiment = [
|
||||
"Denke an sichere Wege, deine Ideen zu testen",
|
||||
"Mache Beobachtungen und schreibe sie auf",
|
||||
"Versuche einfache Experimente mit Haushaltsgegenständen"
|
||||
];
|
||||
} else {
|
||||
suggestions.experiment = [
|
||||
"Think of safe ways to test your ideas",
|
||||
"Make observations and take notes",
|
||||
"Try simple experiments with household items"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (suggestions.discuss.length === 0) {
|
||||
if (this.currentLanguage === 'de') {
|
||||
suggestions.discuss = [
|
||||
"Frag deinen Lehrer, was er denkt",
|
||||
"Sprich mit Familienmitgliedern über ihre Erfahrungen",
|
||||
"Teile deine Frage mit Freunden"
|
||||
];
|
||||
} else {
|
||||
suggestions.discuss = [
|
||||
"Ask your teacher what they think",
|
||||
"Talk to family members about their experiences",
|
||||
"Share your question with friends"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
displayThinkingProcess(guidance) {
|
||||
const t = translations[this.currentLanguage];
|
||||
|
||||
// Clear previous content
|
||||
this.thinkingSteps.innerHTML = '';
|
||||
|
||||
// Add encouragement
|
||||
const encouragementDiv = document.createElement('div');
|
||||
encouragementDiv.className = 'thinking-step highlight';
|
||||
encouragementDiv.innerHTML = `
|
||||
<h4>🎉 ${guidance.encouragement}</h4>
|
||||
<p>${this.currentLanguage === 'de' ? 'Lass uns das Schritt für Schritt erforschen und dir helfen, ein fantastischer Problemlöser zu werden!' : 'Let\'s explore this step by step and help you become a fantastic problem solver!'}</p>
|
||||
`;
|
||||
this.thinkingSteps.appendChild(encouragementDiv);
|
||||
|
||||
// Add thinking steps with animation delay
|
||||
guidance.framework.steps.forEach((step, index) => {
|
||||
setTimeout(() => {
|
||||
const stepDiv = document.createElement('div');
|
||||
stepDiv.className = 'thinking-step';
|
||||
stepDiv.innerHTML = `
|
||||
<h4>${step.title}</h4>
|
||||
<p>${step.content}</p>
|
||||
`;
|
||||
this.thinkingSteps.appendChild(stepDiv);
|
||||
|
||||
// Show action buttons after all steps are displayed
|
||||
if (index === guidance.framework.steps.length - 1) {
|
||||
setTimeout(() => {
|
||||
this.actionButtons.classList.remove('hidden');
|
||||
this.actionButtons.style.animation = 'fadeInUp 0.6s ease-out';
|
||||
|
||||
// Store suggestions for action buttons
|
||||
this.currentSuggestions = guidance.actionSuggestions;
|
||||
}, 300);
|
||||
}
|
||||
}, index * 500);
|
||||
});
|
||||
|
||||
// Show thinking section
|
||||
this.thinkingSection.classList.remove('hidden');
|
||||
this.thinkingSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
showActionGuidance(actionType) {
|
||||
const t = translations[this.currentLanguage];
|
||||
const actionTitles = t.actionTitles;
|
||||
|
||||
const suggestions = this.currentSuggestions[actionType];
|
||||
|
||||
let content = `<div class="action-guidance">
|
||||
<h4>${actionTitles[actionType]}</h4>
|
||||
<ul>`;
|
||||
|
||||
suggestions.forEach(suggestion => {
|
||||
content += `<li>${suggestion}</li>`;
|
||||
});
|
||||
|
||||
content += `</ul>
|
||||
<p><strong>${this.currentLanguage === 'de' ? 'Denk daran:' : 'Remember:'}</strong> ${t.actionReminder}</p>
|
||||
</div>`;
|
||||
|
||||
// Create and show modal or add to thinking steps
|
||||
const guidanceDiv = document.createElement('div');
|
||||
guidanceDiv.className = 'thinking-step highlight';
|
||||
guidanceDiv.innerHTML = content;
|
||||
guidanceDiv.style.animation = 'slideInLeft 0.5s ease-out';
|
||||
|
||||
this.thinkingSteps.appendChild(guidanceDiv);
|
||||
guidanceDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
if (this.loadingOverlay) {
|
||||
this.loadingOverlay.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
if (this.loadingOverlay) {
|
||||
this.loadingOverlay.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
showMessage(message, type = 'info') {
|
||||
// Create a temporary message element
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = `message-popup ${type}`;
|
||||
messageDiv.textContent = message;
|
||||
messageDiv.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: ${type === 'error' ? '#fed7d7' : type === 'warning' ? '#fef5e7' : '#e6fffa'};
|
||||
color: ${type === 'error' ? '#c53030' : type === 'warning' ? '#d69e2e' : '#00695c'};
|
||||
padding: 15px 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||
z-index: 1001;
|
||||
animation: slideInRight 0.3s ease-out;
|
||||
max-width: 300px;
|
||||
`;
|
||||
|
||||
document.body.appendChild(messageDiv);
|
||||
|
||||
// Remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
messageDiv.style.animation = 'slideOutRight 0.3s ease-out';
|
||||
setTimeout(() => messageDiv.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the application when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('DOM loaded, initializing KidsAI Explorer...');
|
||||
|
||||
// Hide loading screen immediately on page load
|
||||
const loadingOverlay = document.getElementById('loading');
|
||||
if (loadingOverlay) {
|
||||
loadingOverlay.classList.add('hidden');
|
||||
console.log('Loading overlay hidden on page load');
|
||||
}
|
||||
|
||||
// Emergency fallback - hide loading after 1 second regardless
|
||||
setTimeout(() => {
|
||||
if (loadingOverlay && !loadingOverlay.classList.contains('hidden')) {
|
||||
loadingOverlay.classList.add('hidden');
|
||||
console.log('Emergency: Loading overlay hidden by timeout');
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Check if translations are loaded
|
||||
if (typeof translations === 'undefined') {
|
||||
console.error('Translations not loaded! Creating fallback...');
|
||||
window.translations = {
|
||||
en: {
|
||||
title: "KidsAI Explorer",
|
||||
tagline: "Think, Learn, Discover Together!",
|
||||
encouragements: ["Great question! You're thinking like a real scientist! 🔬"],
|
||||
thinkingFrameworks: {
|
||||
general: {
|
||||
steps: [
|
||||
{
|
||||
title: "🎯 Let's break this down",
|
||||
content: "What's the main thing you want to understand?"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
actionTitles: {
|
||||
research: "🔍 Research Ideas",
|
||||
experiment: "🧪 Experiment Ideas",
|
||||
discuss: "💬 Discussion Ideas"
|
||||
},
|
||||
actionReminder: "Remember: The goal is to discover the answer yourself!"
|
||||
},
|
||||
de: {
|
||||
title: "KidsAI Explorer",
|
||||
tagline: "Denken, Lernen, Entdecken - Zusammen!",
|
||||
encouragements: ["Tolle Frage! Du denkst wie ein echter Wissenschaftler! 🔬"],
|
||||
thinkingFrameworks: {
|
||||
general: {
|
||||
steps: [
|
||||
{
|
||||
title: "🎯 Lass uns das aufteilen",
|
||||
content: "Was ist das Wichtigste, was du verstehen möchtest?"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
actionTitles: {
|
||||
research: "🔍 Forschungsideen",
|
||||
experiment: "🧪 Experiment-Ideen",
|
||||
discuss: "💬 Diskussionsideen"
|
||||
},
|
||||
actionReminder: "Denk daran: Das Ziel ist es, die Antwort selbst zu entdecken!"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
new KidsAIExplorer();
|
||||
console.log('KidsAI Explorer initialized successfully');
|
||||
} catch (error) {
|
||||
console.error('Error initializing KidsAIExplorer:', error);
|
||||
// Ensure loading is hidden even if there's an error
|
||||
if (loadingOverlay) {
|
||||
loadingOverlay.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add some fun interactive effects
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Add sparkle effect on click
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('ask-btn') || e.target.classList.contains('action-btn')) {
|
||||
createSparkleEffect(e.target);
|
||||
}
|
||||
});
|
||||
|
||||
// Add hover sound effect simulation (visual feedback)
|
||||
const interactiveElements = document.querySelectorAll('.suggestion-card, .ask-btn, .action-btn');
|
||||
interactiveElements.forEach(element => {
|
||||
element.addEventListener('mouseenter', () => {
|
||||
element.style.transform = element.style.transform || 'translateY(-2px)';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createSparkleEffect(element) {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const sparkle = document.createElement('div');
|
||||
sparkle.innerHTML = '✨';
|
||||
sparkle.style.cssText = `
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
font-size: 1rem;
|
||||
animation: sparkle 1s ease-out forwards;
|
||||
z-index: 1000;
|
||||
`;
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
sparkle.style.left = (rect.left + Math.random() * rect.width) + 'px';
|
||||
sparkle.style.top = (rect.top + Math.random() * rect.height) + 'px';
|
||||
|
||||
document.body.appendChild(sparkle);
|
||||
|
||||
setTimeout(() => sparkle.remove(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Add sparkle animation CSS
|
||||
const sparkleCSS = `
|
||||
@keyframes sparkle {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(0);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: translateY(-20px) scale(1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateY(-40px) scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(100px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOutRight {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateX(100px);
|
||||
}
|
||||
}
|
||||
|
||||
.action-guidance ul {
|
||||
margin: 15px 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.action-guidance li {
|
||||
margin: 8px 0;
|
||||
color: #4a5568;
|
||||
font-weight: 500;
|
||||
}
|
||||
`;
|
||||
|
||||
// Inject the sparkle CSS
|
||||
const styleSheet = document.createElement('style');
|
||||
styleSheet.textContent = sparkleCSS;
|
||||
document.head.appendChild(styleSheet);
|
||||
8
html/kidsai/server.log
Normal file
8
html/kidsai/server.log
Normal file
@@ -0,0 +1,8 @@
|
||||
nohup: ignoring input
|
||||
[dotenv@17.0.0] injecting env (4) from .env – 🔐 encrypt with dotenvx: https://dotenvx.com
|
||||
🚀 KidsAI Explorer server running on port 3002
|
||||
📖 Visit http://localhost:3002 to start exploring!
|
||||
🤖 AI Services: OpenAI=true, HuggingFace=true
|
||||
🤖 Calling OpenAI with GPT-3.5-turbo...
|
||||
✅ OpenAI response received: 1. Have you ever noticed how birds' wings are shaped differently than their bodies?
|
||||
2. Can you think...
|
||||
@@ -39,7 +39,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_BASE = '/api';
|
||||
const API_BASE = 'http://localhost:3002/api';
|
||||
|
||||
async function testHealth() {
|
||||
const resultDiv = document.getElementById('health-result');
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test Conversation Feature</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; padding: 20px; background: #f0f0f0; }
|
||||
.test-feedback { background: #4CAF50; color: white; padding: 20px; border-radius: 10px; margin: 20px 0; }
|
||||
.ai-question-reply { border-top: 1px solid rgba(255, 255, 255, 0.2); padding-top: 15px; margin-top: 15px; }
|
||||
.reply-input-section { display: flex; gap: 10px; margin-bottom: 15px; }
|
||||
.ai-reply-input { flex: 1; padding: 10px; border-radius: 5px; border: 1px solid #ccc; }
|
||||
.reply-to-ai-btn { padding: 10px 20px; background: #2196F3; color: white; border: none; border-radius: 5px; cursor: pointer; }
|
||||
.debug { background: #333; color: #0f0; padding: 10px; margin: 10px 0; border-radius: 5px; font-family: monospace; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test Conversation Feature</h1>
|
||||
|
||||
<div class="debug" id="debug">Debug info will appear here...</div>
|
||||
|
||||
<button onclick="testConversation()">Test AI Question Response</button>
|
||||
|
||||
<div id="test-area">
|
||||
<!-- Test feedback will be inserted here -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function testConversation() {
|
||||
const feedback = {
|
||||
response: "That's interesting! Can you tell me more about why you think that?",
|
||||
type: "encouraging"
|
||||
};
|
||||
|
||||
const debugDiv = document.getElementById('debug');
|
||||
debugDiv.innerHTML = `Testing feedback: "${feedback.response}"<br>Contains question mark: ${feedback.response.includes('?')}`;
|
||||
|
||||
const testArea = document.getElementById('test-area');
|
||||
const isAIQuestion = feedback.response.includes('?');
|
||||
|
||||
testArea.innerHTML = `
|
||||
<div class="test-feedback">
|
||||
<p>${feedback.response}</p>
|
||||
${isAIQuestion ? `
|
||||
<div class="ai-question-reply">
|
||||
<div class="reply-input-section">
|
||||
<textarea class="ai-reply-input" placeholder="Your answer..." rows="2"></textarea>
|
||||
<button class="reply-to-ai-btn">Reply</button>
|
||||
</div>
|
||||
</div>
|
||||
` : '<p>No question detected - no reply interface shown</p>'}
|
||||
</div>
|
||||
`;
|
||||
|
||||
debugDiv.innerHTML += `<br>HTML generated with isAIQuestion: ${isAIQuestion}`;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
70
html/kidsai/test-simple.html
Normal file
70
html/kidsai/test-simple.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>KidsAI Simple Test</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; padding: 20px; }
|
||||
.test-button { padding: 10px 20px; margin: 10px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; }
|
||||
.suggestion-card { padding: 10px; margin: 10px; background: #f0f0f0; border-radius: 5px; cursor: pointer; }
|
||||
#question-input { padding: 10px; width: 300px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>KidsAI Simple Test</h1>
|
||||
|
||||
<div>
|
||||
<input type="text" id="question-input" placeholder="Type your question here..." value="test question">
|
||||
<button id="ask-button" class="test-button">Let's Explore!</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Suggestion Cards:</h3>
|
||||
<div class="suggestion-card" data-question-en="How do birds fly?">How do birds fly?</div>
|
||||
<div class="suggestion-card" data-question-en="Why is water wet?">Why is water wet?</div>
|
||||
</div>
|
||||
|
||||
<div id="debug-output"></div>
|
||||
|
||||
<script>
|
||||
console.log('🧪 Simple test page loaded');
|
||||
|
||||
function addDebug(message) {
|
||||
const output = document.getElementById('debug-output');
|
||||
output.innerHTML += '<div>' + new Date().toLocaleTimeString() + ': ' + message + '</div>';
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
addDebug('DOM Content Loaded');
|
||||
|
||||
const questionInput = document.getElementById('question-input');
|
||||
const askButton = document.getElementById('ask-button');
|
||||
const suggestionCards = document.querySelectorAll('.suggestion-card');
|
||||
|
||||
addDebug('Found elements: input=' + !!questionInput + ', button=' + !!askButton + ', cards=' + suggestionCards.length);
|
||||
|
||||
if (askButton) {
|
||||
askButton.addEventListener('click', () => {
|
||||
addDebug('BUTTON CLICKED! Input value: "' + questionInput.value + '"');
|
||||
});
|
||||
}
|
||||
|
||||
suggestionCards.forEach((card, index) => {
|
||||
card.addEventListener('click', () => {
|
||||
const question = card.getAttribute('data-question-en');
|
||||
addDebug('CARD ' + index + ' CLICKED! Question: "' + question + '"');
|
||||
questionInput.value = question;
|
||||
});
|
||||
});
|
||||
|
||||
questionInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
addDebug('ENTER PRESSED! Input value: "' + questionInput.value + '"');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user