feat: Improve frontend conversation handling and loading UX
- Add session-based conversation history management - Improve loading overlay with kid-friendly animated messages - Disable premature completion messages in conversation mode - Add better error handling and fallback mechanisms - Improve conversation flow to work with new fundamentals approach - Add comprehensive logging for debugging conversation state - Fix conversation container management for smoother UX
This commit is contained in:
@@ -15,6 +15,10 @@ class KidsAIExplorer {
|
||||
|
||||
this.currentLanguage = localStorage.getItem('kidsai-language') || 'en';
|
||||
|
||||
// Generate session ID for conversation tracking
|
||||
this.sessionId = this.generateSessionId();
|
||||
console.log('🔑 Session ID:', this.sessionId);
|
||||
|
||||
// Configure API base URL based on environment
|
||||
this.apiBaseUrl = this.getApiBaseUrl();
|
||||
console.log('🔗 API Base URL:', this.apiBaseUrl);
|
||||
@@ -47,6 +51,11 @@ class KidsAIExplorer {
|
||||
console.log('✅ KidsAI constructor completed');
|
||||
}
|
||||
|
||||
// Generate unique session ID for conversation tracking
|
||||
generateSessionId() {
|
||||
return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||
}
|
||||
|
||||
// Detect mobile device
|
||||
isMobileDevice() {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|
||||
@@ -117,6 +126,25 @@ class KidsAIExplorer {
|
||||
this.switchLanguage(btn.dataset.lang);
|
||||
});
|
||||
});
|
||||
|
||||
// Logo click to return to main page
|
||||
const logoButton = document.getElementById('logo-button');
|
||||
if (logoButton) {
|
||||
console.log('🏠 Adding click listener to logo');
|
||||
logoButton.addEventListener('click', () => {
|
||||
console.log('🏠 Logo clicked - returning to main page');
|
||||
this.returnToMainPage();
|
||||
});
|
||||
|
||||
// Add keyboard support for accessibility
|
||||
logoButton.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
console.log('🏠 Logo activated via keyboard - returning to main page');
|
||||
this.returnToMainPage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('✅ Event listeners setup complete');
|
||||
}
|
||||
@@ -195,6 +223,89 @@ class KidsAIExplorer {
|
||||
this.initializeLanguage();
|
||||
}
|
||||
|
||||
returnToMainPage() {
|
||||
console.log('🏠 Returning to main page');
|
||||
|
||||
// Clear the question input
|
||||
if (this.questionInput) {
|
||||
this.questionInput.value = '';
|
||||
this.questionInput.style.height = 'auto'; // Reset height if auto-resized
|
||||
this.questionInput.blur(); // Remove focus to prevent keyboard from showing
|
||||
}
|
||||
|
||||
// Hide thinking section
|
||||
if (this.thinkingSection) {
|
||||
this.thinkingSection.classList.add('hidden');
|
||||
this.thinkingSection.classList.remove('chat-mode');
|
||||
}
|
||||
|
||||
// Show main page sections that were hidden during chat
|
||||
const welcomeSection = document.querySelector('.welcome-section');
|
||||
const questionSection = document.querySelector('.question-section');
|
||||
const suggestionsSection = document.querySelector('.suggestions-section');
|
||||
|
||||
if (welcomeSection) welcomeSection.style.display = '';
|
||||
if (questionSection) questionSection.style.display = '';
|
||||
if (suggestionsSection) suggestionsSection.style.display = '';
|
||||
|
||||
// Clear thinking steps
|
||||
if (this.thinkingSteps) {
|
||||
this.thinkingSteps.innerHTML = '';
|
||||
}
|
||||
|
||||
// Hide action buttons
|
||||
const actionButtons = document.getElementById('action-buttons');
|
||||
if (actionButtons) {
|
||||
actionButtons.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Hide loading overlay and ensure it's properly hidden
|
||||
if (this.loadingOverlay) {
|
||||
this.loadingOverlay.classList.add('hidden');
|
||||
this.loadingOverlay.style.display = 'none';
|
||||
}
|
||||
|
||||
// Clear any existing message popups
|
||||
const messagePopups = document.querySelectorAll('.message-popup');
|
||||
messagePopups.forEach(popup => popup.remove());
|
||||
|
||||
// Reset any conversation state
|
||||
this.currentStep = 0;
|
||||
this.userAnswers = [];
|
||||
this.currentQuestionIndex = 0;
|
||||
|
||||
// Clear any chat input containers that might be stuck
|
||||
const chatInputContainers = document.querySelectorAll('.chat-input-container');
|
||||
chatInputContainers.forEach(container => {
|
||||
if (container && container.parentNode) {
|
||||
container.parentNode.removeChild(container);
|
||||
}
|
||||
});
|
||||
|
||||
// Reset conversation container state
|
||||
const conversationContainer = document.querySelector('.conversation-container');
|
||||
if (conversationContainer) {
|
||||
conversationContainer.classList.remove('keyboard-open');
|
||||
conversationContainer.style.height = '';
|
||||
conversationContainer.style.maxHeight = '';
|
||||
conversationContainer.style.position = '';
|
||||
conversationContainer.style.bottom = '';
|
||||
conversationContainer.style.left = '';
|
||||
conversationContainer.style.width = '';
|
||||
conversationContainer.style.zIndex = '';
|
||||
}
|
||||
|
||||
// Smooth scroll to top instead of focusing input (prevents keyboard from showing)
|
||||
setTimeout(() => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}, 100);
|
||||
|
||||
console.log('✅ Returned to main page state');
|
||||
}
|
||||
|
||||
async handleQuestion() {
|
||||
console.log('🤔 handleQuestion called');
|
||||
|
||||
@@ -219,7 +330,8 @@ class KidsAIExplorer {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
question: question,
|
||||
language: this.currentLanguage
|
||||
language: this.currentLanguage,
|
||||
sessionId: this.sessionId
|
||||
})
|
||||
});
|
||||
|
||||
@@ -256,8 +368,18 @@ class KidsAIExplorer {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show thinking section
|
||||
// Show thinking section and hide other content
|
||||
this.thinkingSection.classList.remove('hidden');
|
||||
this.thinkingSection.classList.add('chat-mode');
|
||||
|
||||
// Hide other sections to focus on chat
|
||||
const welcomeSection = document.querySelector('.welcome-section');
|
||||
const questionSection = document.querySelector('.question-section');
|
||||
const suggestionsSection = document.querySelector('.suggestions-section');
|
||||
|
||||
if (welcomeSection) welcomeSection.style.display = 'none';
|
||||
if (questionSection) questionSection.style.display = 'none';
|
||||
if (suggestionsSection) suggestionsSection.style.display = 'none';
|
||||
|
||||
// Clear previous content completely
|
||||
this.thinkingSteps.innerHTML = '';
|
||||
@@ -267,10 +389,36 @@ class KidsAIExplorer {
|
||||
this.userAnswers = [];
|
||||
this.currentQuestionIndex = 0;
|
||||
|
||||
// Create conversation container
|
||||
// Create conversation container with explicit height enforcement
|
||||
const conversationContainer = document.createElement('div');
|
||||
conversationContainer.className = 'conversation-container';
|
||||
|
||||
// CRITICAL: Force container properties via JavaScript as backup
|
||||
conversationContainer.style.height = '450px';
|
||||
conversationContainer.style.maxHeight = '450px';
|
||||
conversationContainer.style.minHeight = '450px';
|
||||
conversationContainer.style.overflowY = 'scroll'; // Force scrollbar visibility
|
||||
conversationContainer.style.overflowX = 'hidden';
|
||||
conversationContainer.style.display = 'block';
|
||||
conversationContainer.style.boxSizing = 'border-box';
|
||||
conversationContainer.style.position = 'relative';
|
||||
conversationContainer.style.contain = 'layout size style';
|
||||
|
||||
this.thinkingSteps.appendChild(conversationContainer);
|
||||
|
||||
// Store reference and start periodic height enforcement
|
||||
this.conversationContainer = conversationContainer;
|
||||
|
||||
// Enforce height immediately and then periodically
|
||||
this.enforceContainerHeight();
|
||||
|
||||
// Set up periodic enforcement to prevent CSS conflicts
|
||||
if (this.heightEnforcementInterval) {
|
||||
clearInterval(this.heightEnforcementInterval);
|
||||
}
|
||||
this.heightEnforcementInterval = setInterval(() => {
|
||||
this.enforceContainerHeight();
|
||||
}, 1000); // Check every second
|
||||
|
||||
// Show initial encouragement
|
||||
const welcomeStep = document.createElement('div');
|
||||
@@ -303,14 +451,17 @@ class KidsAIExplorer {
|
||||
];
|
||||
|
||||
// Create chat interface
|
||||
this.conversationContainer = conversationContainer;
|
||||
this.questions = questions;
|
||||
|
||||
// Start the conversation with the first question
|
||||
this.askNextQuestion();
|
||||
|
||||
// Scroll to thinking section
|
||||
this.thinkingSection.scrollIntoView({ behavior: 'smooth' });
|
||||
// Gently scroll to thinking section without jumping
|
||||
setTimeout(() => {
|
||||
if (this.thinkingSection) {
|
||||
this.thinkingSection.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
displayLocalGuidance(question) {
|
||||
@@ -342,7 +493,13 @@ class KidsAIExplorer {
|
||||
showLoading() {
|
||||
console.log('⏳ Showing loading...');
|
||||
if (this.loadingOverlay) {
|
||||
console.log('🔍 Loading overlay found, removing hidden class');
|
||||
this.loadingOverlay.classList.remove('hidden');
|
||||
this.loadingOverlay.style.display = 'flex'; // Force display
|
||||
console.log('✅ Loading overlay should now be visible');
|
||||
this.startLoadingMessages();
|
||||
} else {
|
||||
console.error('❌ Loading overlay not found!');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,6 +507,59 @@ class KidsAIExplorer {
|
||||
console.log('✅ Hiding loading...');
|
||||
if (this.loadingOverlay) {
|
||||
this.loadingOverlay.classList.add('hidden');
|
||||
this.loadingOverlay.style.display = 'none'; // Force hide
|
||||
this.stopLoadingMessages();
|
||||
}
|
||||
}
|
||||
|
||||
startLoadingMessages() {
|
||||
const loadingMessages = this.currentLanguage === 'de' ? [
|
||||
"🧠 Ich denke nach...",
|
||||
"💡 Ich sammle Ideen für dich...",
|
||||
"🔍 Ich schaue, wie ich dir am besten helfen kann...",
|
||||
"⚙️ Ich bereite eine tolle Antwort vor...",
|
||||
"🚀 Gleich habe ich etwas Interessantes für dich!",
|
||||
"🌟 Ich überlege, welche Frage dich zum Nachdenken bringt..."
|
||||
] : [
|
||||
"🧠 I'm thinking...",
|
||||
"💡 I'm gathering ideas for you...",
|
||||
"🔍 I'm figuring out the best way to help you...",
|
||||
"⚙️ I'm preparing a great response...",
|
||||
"🚀 I'll have something interesting for you soon!",
|
||||
"🌟 I'm thinking of a question that will make you curious..."
|
||||
];
|
||||
|
||||
const messageElement = this.loadingOverlay.querySelector('p');
|
||||
if (!messageElement) return;
|
||||
|
||||
let messageIndex = 0;
|
||||
messageElement.textContent = loadingMessages[0];
|
||||
|
||||
// Clear any existing interval
|
||||
if (this.loadingMessageInterval) {
|
||||
clearInterval(this.loadingMessageInterval);
|
||||
}
|
||||
|
||||
// Rotate messages every 1.5 seconds
|
||||
this.loadingMessageInterval = setInterval(() => {
|
||||
messageIndex = (messageIndex + 1) % loadingMessages.length;
|
||||
|
||||
// Fade out current message
|
||||
messageElement.classList.add('fade-out');
|
||||
|
||||
// After fade out, change text and fade in
|
||||
setTimeout(() => {
|
||||
messageElement.textContent = loadingMessages[messageIndex];
|
||||
messageElement.classList.remove('fade-out');
|
||||
}, 250);
|
||||
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
stopLoadingMessages() {
|
||||
if (this.loadingMessageInterval) {
|
||||
clearInterval(this.loadingMessageInterval);
|
||||
this.loadingMessageInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +627,8 @@ class KidsAIExplorer {
|
||||
async generateAIResponseToAnswer(answer, stepIndex, responseDiv) {
|
||||
console.log('🚀 generateAIResponseToAnswer called with:', { answer, stepIndex });
|
||||
|
||||
this.showLoading();
|
||||
|
||||
try {
|
||||
// Show loading state
|
||||
responseDiv.innerHTML = `
|
||||
@@ -525,15 +737,84 @@ class KidsAIExplorer {
|
||||
question: currentQuestion,
|
||||
originalTopic: originalQuestion,
|
||||
language: this.currentLanguage,
|
||||
sessionId: this.sessionId,
|
||||
context: contextReason,
|
||||
stepIndex: questionIndex,
|
||||
instructions: needsExplanationDueToRepeatedDontKnowChat
|
||||
? (this.currentLanguage === 'de'
|
||||
? 'Das Kind braucht jetzt eine einfache, klare Erklärung. Es hat bereits mehrmals "weiß nicht" gesagt. Gib eine vollständige Antwort mit praktischen Beispielen. Erkläre das Konzept Schritt für Schritt mit alltäglichen Vergleichen. Keine weiteren Fragen - das Kind braucht Wissen!'
|
||||
: 'The child needs a simple, clear explanation now. They have said "don\'t know" multiple times. Give a complete answer with practical examples. Explain the concept step by step with everyday comparisons. No more questions - the child needs knowledge!')
|
||||
? `ERKLÄRUNGS-AUFTRAG: Ein Kind braucht jetzt eine klare, verständliche Erklärung.
|
||||
|
||||
SITUATION: Das Kind hat mehrmals "weiß nicht" gesagt und braucht fundiertes Wissen, um weitermachen zu können.
|
||||
|
||||
AUFGABE: Gib eine vollständige, aber kindgerechte Erklärung zu "${currentQuestion}" im Kontext von "${originalQuestion}".
|
||||
|
||||
DEINE ERKLÄRUNG MUSS:
|
||||
1. ALTERSGERECHT SEIN: Verwende Sprache und Konzepte für 8-12-Jährige
|
||||
2. VOLLSTÄNDIG SEIN: Beantworte die Frage komplett, nicht nur teilweise
|
||||
3. PRAKTISCHE BEISPIELE ENTHALTEN: Verwende Situationen aus dem Kinderalltag
|
||||
4. EXPERIMENTE VORSCHLAGEN: Lade zu einfachen, sicheren Experimenten ein
|
||||
5. SCHRITT-FÜR-SCHRITT AUFGEBAUT SEIN: Erkläre logisch von einfach zu komplex
|
||||
6. NEUGIER WECKEN: Mache Lust auf weitere Entdeckungen
|
||||
|
||||
STIL:
|
||||
- Freundlich und ermutigend
|
||||
- Wissenschaftlich korrekt aber verständlich
|
||||
- Mit konkreten Alltagsbeispielen
|
||||
- Einladend zum Weiterforschen
|
||||
|
||||
Das Kind braucht jetzt Wissen, nicht weitere Fragen. Erkläre vollständig und kindgerecht!`
|
||||
: `EXPLANATION TASK: A child now needs a clear, understandable explanation.
|
||||
|
||||
SITUATION: The child has said "don't know" multiple times and needs solid knowledge to continue.
|
||||
|
||||
TASK: Give a complete but child-friendly explanation about "${currentQuestion}" in the context of "${originalQuestion}".
|
||||
|
||||
YOUR EXPLANATION MUST:
|
||||
1. BE AGE-APPROPRIATE: Use language and concepts for 8-12 year olds
|
||||
2. BE COMPLETE: Answer the question fully, not just partially
|
||||
3. CONTAIN PRACTICAL EXAMPLES: Use situations from children's daily lives
|
||||
4. SUGGEST EXPERIMENTS: Invite simple, safe experiments
|
||||
5. BE BUILT STEP-BY-STEP: Explain logically from simple to complex
|
||||
6. SPARK CURIOSITY: Create desire for further discoveries
|
||||
|
||||
STYLE:
|
||||
- Friendly and encouraging
|
||||
- Scientifically correct but understandable
|
||||
- With concrete everyday examples
|
||||
- Inviting to further research
|
||||
|
||||
The child needs knowledge now, not more questions. Explain completely and child-appropriately!`)
|
||||
: (this.currentLanguage === 'de'
|
||||
? 'Das Kind ist verwirrt und braucht eine einfache Erklärung. Gib eine klare, konkrete Antwort für Kinder. Erkläre das Konzept mit alltäglichen Beispielen. Verwende einfache Wörter und lade zum Ausprobieren ein. Keine weiteren Fragen stellen - erst erklären!'
|
||||
: 'The child is confused and needs a simple explanation. Give a clear, concrete answer for children. Explain the concept with everyday examples. Use simple words and invite experimentation. Don\'t ask more questions - explain first!')
|
||||
? `VERWIRRUNG AUFLÖSEN: Ein Kind ist verwirrt und braucht Klarheit.
|
||||
|
||||
SITUATION: Das Kind ist bei "${currentQuestion}" im Kontext von "${originalQuestion}" verwirrt.
|
||||
|
||||
AUFGABE: Gib eine klare, beruhigende Erklärung, die das Verständnis wiederherstellt.
|
||||
|
||||
DEINE ERKLÄRUNG SOLLTE:
|
||||
1. BERUHIGEND SEIN: Zeige, dass Verwirrung normal beim Lernen ist
|
||||
2. EINFACH ANFANGEN: Beginne mit dem Grundlegendsten
|
||||
3. ALLTÄGLICHE VERGLEICHE NUTZEN: Verwende Dinge, die das Kind kennt
|
||||
4. SCHRITT-FÜR-SCHRITT AUFBAUEN: Vom Einfachen zum Komplexeren
|
||||
5. PRAKTISCH SEIN: Lade zu einfachen Beobachtungen oder Tests ein
|
||||
6. ERMUTIGEN: Mache deutlich, dass das Kind das verstehen kann
|
||||
|
||||
Das Kind braucht Klarheit und Ermutigung. Erkläre so, dass Verständnis entsteht!`
|
||||
: `RESOLVE CONFUSION: A child is confused and needs clarity.
|
||||
|
||||
SITUATION: The child is confused about "${currentQuestion}" in the context of "${originalQuestion}".
|
||||
|
||||
TASK: Give a clear, reassuring explanation that restores understanding.
|
||||
|
||||
YOUR EXPLANATION SHOULD:
|
||||
1. BE REASSURING: Show that confusion is normal when learning
|
||||
2. START SIMPLE: Begin with the most basic elements
|
||||
3. USE EVERYDAY COMPARISONS: Use things the child knows
|
||||
4. BUILD STEP-BY-STEP: From simple to more complex
|
||||
5. BE PRACTICAL: Invite simple observations or tests
|
||||
6. ENCOURAGE: Make clear that the child can understand this
|
||||
|
||||
The child needs clarity and encouragement. Explain so that understanding emerges!`)
|
||||
})
|
||||
});
|
||||
} else if (isNegative || isPureDontKnow) {
|
||||
@@ -541,8 +822,56 @@ class KidsAIExplorer {
|
||||
console.log('📤 Sending guidance request to /api/ask for Socratic questioning');
|
||||
|
||||
const guidancePrompt = this.currentLanguage === 'de'
|
||||
? `Ein Kind hat "${answer}" geantwortet auf die Frage "${currentQuestion}". Das Kind weiß die Antwort nicht. Führe es durch 2-3 aufbauende Socratic Fragen zur Entdeckung, ohne direkte Antworten zu geben. Beginne mit einfachen Beobachtungen, die das Kind kennt.`
|
||||
: `A child answered "${answer}" to the question "${currentQuestion}". The child doesn't know the answer. Guide them through 2-3 building Socratic questions to discovery without giving direct answers. Start with simple observations the child would know.`;
|
||||
? `SOCRATIC LEARNING GUIDANCE: Ein Kind braucht Hilfe beim Entdeckungslernen.
|
||||
|
||||
AKTUELLE LAGE:
|
||||
- Frage: "${currentQuestion}"
|
||||
- Antwort des Kindes: "${answer}"
|
||||
- Problem: Das Kind weiß die Antwort nicht und braucht behutsame Führung
|
||||
|
||||
DEIN AUFTRAG: Entwickle EINE durchdachte Socratic Frage, die das Kind zur selbständigen Entdeckung führt.
|
||||
|
||||
PÄDAGOGISCHE PRINZIPIEN:
|
||||
1. AUFBAU AUF BEKANNTEM: Beginne mit Erfahrungen, die das Kind bereits gemacht hat
|
||||
2. KONKRETE BEOBACHTUNGEN: Lade zu spezifischen, sichtbaren Entdeckungen ein
|
||||
3. EINFACHE EXPERIMENTE: Schlage Tests mit Haushaltsgegenständen vor
|
||||
4. ALTERSGERECHTE SPRACHE: Verwende Wörter, die 8-12-Jährige problemlos verstehen
|
||||
5. WISSENSCHAFTLICHE GENAUIGKEIT: Führe zu echten wissenschaftlichen Erkenntnissen
|
||||
6. NEUGIER WECKEN: Formuliere so spannend, dass das Kind unbedingt weiterforschen möchte
|
||||
|
||||
QUALITÄT DEINER FRAGE:
|
||||
- Baut direkt auf dem Nichtwissen des Kindes auf
|
||||
- Ist praktisch umsetzbar (zu Hause, in der Schule)
|
||||
- Führt zu einer "Aha!"-Erfahrung
|
||||
- Ist wissenschaftlich korrekt aber kindgerecht
|
||||
- Weckt Forscherdrang und Experimentierfreude
|
||||
|
||||
Entwickle jetzt EINE perfekte Socratic Leitfrage für dieses Kind:`
|
||||
: `SOCRATIC LEARNING GUIDANCE: A child needs help with discovery learning.
|
||||
|
||||
CURRENT SITUATION:
|
||||
- Question: "${currentQuestion}"
|
||||
- Child's answer: "${answer}"
|
||||
- Problem: The child doesn't know the answer and needs gentle guidance
|
||||
|
||||
YOUR TASK: Develop ONE thoughtful Socratic question that leads the child to independent discovery.
|
||||
|
||||
PEDAGOGICAL PRINCIPLES:
|
||||
1. BUILD ON KNOWN: Start with experiences the child has already had
|
||||
2. CONCRETE OBSERVATIONS: Invite specific, visible discoveries
|
||||
3. SIMPLE EXPERIMENTS: Suggest tests with household items
|
||||
4. AGE-APPROPRIATE LANGUAGE: Use words that 8-12 year olds easily understand
|
||||
5. SCIENTIFIC ACCURACY: Lead to genuine scientific insights
|
||||
6. SPARK CURIOSITY: Formulate so excitingly that the child absolutely wants to continue researching
|
||||
|
||||
QUALITY OF YOUR QUESTION:
|
||||
- Builds directly on the child's not knowing
|
||||
- Is practically achievable (at home, at school)
|
||||
- Leads to an "Aha!" experience
|
||||
- Is scientifically correct but child-friendly
|
||||
- Sparks research drive and experimental joy
|
||||
|
||||
Now develop ONE perfect Socratic guiding question for this child:`;
|
||||
|
||||
response = await fetch(`${this.apiBaseUrl}/api/ask`, {
|
||||
method: 'POST',
|
||||
@@ -551,7 +880,8 @@ class KidsAIExplorer {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
question: guidancePrompt,
|
||||
language: this.currentLanguage
|
||||
language: this.currentLanguage,
|
||||
sessionId: this.sessionId
|
||||
})
|
||||
});
|
||||
} else {
|
||||
@@ -567,6 +897,7 @@ class KidsAIExplorer {
|
||||
answer: answer,
|
||||
question: currentQuestion,
|
||||
language: this.currentLanguage,
|
||||
sessionId: this.sessionId,
|
||||
stepIndex: stepIndex
|
||||
})
|
||||
});
|
||||
@@ -631,6 +962,8 @@ class KidsAIExplorer {
|
||||
`;
|
||||
}
|
||||
|
||||
this.hideLoading();
|
||||
|
||||
// Show next question or reveal option after response
|
||||
setTimeout(() => {
|
||||
const nextStepIndex = stepIndex + 1;
|
||||
@@ -659,6 +992,8 @@ class KidsAIExplorer {
|
||||
async generateChatAIResponse(answer, questionIndex) {
|
||||
console.log('🚀 generateChatAIResponse called with:', { answer, questionIndex });
|
||||
|
||||
this.showLoading();
|
||||
|
||||
try {
|
||||
// Get current question context
|
||||
const currentQuestion = this.questions && this.questions[questionIndex]
|
||||
@@ -673,8 +1008,7 @@ class KidsAIExplorer {
|
||||
const isNegative = answerLower === 'no' || answerLower === 'nein' || answerLower === 'nope' || answerLower === 'nei';
|
||||
|
||||
// Check for pure "don't know" responses (without additional thinking)
|
||||
const hasDontKnowPhrase = answerLower.includes("don't know") || answerLower.includes('weiß nicht') || answerLower.includes('weis nicht') ||
|
||||
answerLower.includes('weiss nicht') || answerLower.includes('keine ahnung') || answerLower.includes('keinen plan') ||
|
||||
const hasDontKnowPhrase = answerLower.includes("don't know") || answerLower.includes('weiß nicht') || answerLower.includes('keine ahnung') ||
|
||||
answerLower.includes('ich weiß es nicht') || answerLower.includes('weis es nicht') || answerLower.includes('weiss es nicht') ||
|
||||
answerLower.includes('i dont know') || answerLower.includes("i don't know") || answerLower.includes('hab keine ahnung');
|
||||
|
||||
@@ -742,14 +1076,15 @@ class KidsAIExplorer {
|
||||
// Use AI to determine if child is asking for information/explanation
|
||||
let isAskingForDefinition = false;
|
||||
|
||||
// Quick check: if it contains a question mark and isn't just expressing uncertainty
|
||||
if (answer.includes('?') && !isPureDontKnow && !isExpressingConfusion) {
|
||||
isAskingForDefinition = true;
|
||||
} else {
|
||||
// For non-obvious cases, we'll let the AI determine this in the server response
|
||||
// The server can detect question patterns better than hard-coded rules
|
||||
// DISABLED: Don't automatically switch to explanation mode for questions
|
||||
// The educational approach is to guide children to discover answers themselves
|
||||
// Even when they ask "what is X?", we should ask guiding questions
|
||||
// if (answer.includes('?') && !isPureDontKnow && !isExpressingConfusion) {
|
||||
// isAskingForDefinition = true;
|
||||
// } else {
|
||||
// Always use guided questioning approach
|
||||
isAskingForDefinition = false;
|
||||
}
|
||||
// }
|
||||
|
||||
let response;
|
||||
|
||||
@@ -769,6 +1104,7 @@ class KidsAIExplorer {
|
||||
question: currentQuestion,
|
||||
originalTopic: originalQuestion,
|
||||
language: this.currentLanguage,
|
||||
sessionId: this.sessionId,
|
||||
context: contextReason,
|
||||
stepIndex: questionIndex,
|
||||
instructions: needsExplanationDueToRepeatedDontKnow
|
||||
@@ -794,6 +1130,7 @@ class KidsAIExplorer {
|
||||
question: currentQuestion,
|
||||
originalTopic: originalQuestion,
|
||||
language: this.currentLanguage,
|
||||
sessionId: this.sessionId,
|
||||
context: 'definition_request',
|
||||
stepIndex: questionIndex,
|
||||
instructions: this.currentLanguage === 'de'
|
||||
@@ -806,8 +1143,80 @@ class KidsAIExplorer {
|
||||
console.log('📤 Sending guidance request to /api/ask for Socratic questioning');
|
||||
|
||||
const guidancePrompt = this.currentLanguage === 'de'
|
||||
? `Ein Kind hat "${answer}" geantwortet auf die Frage "${currentQuestion}" über das Thema "${originalQuestion}". Das Kind weiß die Antwort nicht und braucht einfachere Grundlagen. Gib KEINE weiteren abstrakten Fragen! Stattdessen: 1) Beschreibe eine einfache Beobachtung, die das Kind machen kann (z.B. "Hast du schon mal Licht durch ein Glas Wasser gesehen?") 2) Lade zum praktischen Ausprobieren ein 3) Baue auf dem auf, was das Kind bereits kennt. Verwende konkrete, sichtbare Beispiele statt abstrakter Konzepte.`
|
||||
: `A child answered "${answer}" to the question "${currentQuestion}" about the topic "${originalQuestion}". The child doesn't know and needs simpler foundations. Give NO more abstract questions! Instead: 1) Describe a simple observation the child can make (e.g. "Have you ever seen light through a glass of water?") 2) Invite practical experimentation 3) Build on what the child already knows. Use concrete, visible examples instead of abstract concepts.`;
|
||||
? `LERN-COACHING AUFGABE: Ein Kind im Alter von 8-12 Jahren braucht deine Hilfe beim Lernen.
|
||||
|
||||
AKTUELLE SITUATION:
|
||||
- Ursprüngliche Frage: "${originalQuestion}"
|
||||
- Gestellte Frage: "${currentQuestion}"
|
||||
- Antwort des Kindes: "${answer}"
|
||||
- Problem: Das Kind weiß die Antwort nicht und braucht einfachere, konkretere Hilfestellung
|
||||
|
||||
DEIN ZIEL: Entwickle EINE einzige, präzise Frage, die das Kind zu einer einfachen, beobachtbaren Entdeckung führt.
|
||||
|
||||
DEINE HERANGEHENSWEISE:
|
||||
1. AUFBAU AUF BEKANNTEM: Beginne mit etwas, das das Kind garantiert schon erlebt hat
|
||||
2. KONKRETE BEOBACHTUNG: Lade zu einer spezifischen, sichtbaren Beobachtung ein
|
||||
3. HAUSHALTS-EXPERIMENTE: Schlage einfache Tests mit alltäglichen Gegenständen vor
|
||||
4. SCHRITT-FÜR-SCHRITT: Führe das Kind von der Beobachtung zur Erkenntnis
|
||||
5. NEUGIER WECKEN: Formuliere so spannend, dass das Kind sofort ausprobieren möchte
|
||||
|
||||
QUALITÄTSKRITERIEN:
|
||||
- Verwende Sprache, die 8-12-Jährige problemlos verstehen
|
||||
- Baue auf Alltagserfahrungen auf (Zuhause, Schule, Spielplatz)
|
||||
- Schlage praktische Aktivitäten vor, die sofort umsetzbar sind
|
||||
- Sei wissenschaftlich korrekt aber kindgerecht
|
||||
- Wecke echte Neugier und Forscherdrang
|
||||
|
||||
BEISPIELE für gute Fragen:
|
||||
- "Hast du schon mal beobachtet, was passiert, wenn du..."
|
||||
- "Was siehst du, wenn du zuhause..."
|
||||
- "Kannst du heute ausprobieren, ob..."
|
||||
- "Hast du dir schon mal angeschaut, wie..."
|
||||
|
||||
VERMEIDE:
|
||||
- Abstrakte oder komplizierte Konzepte
|
||||
- Zu viele Fragen auf einmal
|
||||
- Schwer verständliche Fachbegriffe
|
||||
- Experimente, die gefährlich oder schwer durchführbar sind
|
||||
|
||||
Entwickle jetzt EINE perfekte Leitfrage für dieses Kind:`
|
||||
: `LEARNING COACHING TASK: A child aged 8-12 years needs your help with learning.
|
||||
|
||||
CURRENT SITUATION:
|
||||
- Original question: "${originalQuestion}"
|
||||
- Question asked: "${currentQuestion}"
|
||||
- Child's answer: "${answer}"
|
||||
- Problem: The child doesn't know the answer and needs simpler, more concrete guidance
|
||||
|
||||
YOUR GOAL: Develop ONE single, precise question that leads the child to a simple, observable discovery.
|
||||
|
||||
YOUR APPROACH:
|
||||
1. BUILD ON KNOWN: Start with something the child has definitely already experienced
|
||||
2. CONCRETE OBSERVATION: Invite a specific, visible observation
|
||||
3. HOUSEHOLD EXPERIMENTS: Suggest simple tests with everyday objects
|
||||
4. STEP-BY-STEP: Lead the child from observation to understanding
|
||||
5. SPARK CURIOSITY: Formulate so excitingly that the child wants to try immediately
|
||||
|
||||
QUALITY CRITERIA:
|
||||
- Use language that 8-12 year olds easily understand
|
||||
- Build on everyday experiences (home, school, playground)
|
||||
- Suggest practical activities that are immediately doable
|
||||
- Be scientifically correct but child-friendly
|
||||
- Spark genuine curiosity and research drive
|
||||
|
||||
EXAMPLES of good questions:
|
||||
- "Have you ever observed what happens when you..."
|
||||
- "What do you see when you at home..."
|
||||
- "Can you try today to see if..."
|
||||
- "Have you ever looked at how..."
|
||||
|
||||
AVOID:
|
||||
- Abstract or complicated concepts
|
||||
- Too many questions at once
|
||||
- Hard-to-understand technical terms
|
||||
- Experiments that are dangerous or hard to perform
|
||||
|
||||
Now develop ONE perfect guiding question for this child:`;
|
||||
|
||||
response = await fetch(`${this.apiBaseUrl}/api/ask`, {
|
||||
method: 'POST',
|
||||
@@ -832,6 +1241,7 @@ class KidsAIExplorer {
|
||||
answer: answer,
|
||||
question: currentQuestion,
|
||||
language: this.currentLanguage,
|
||||
sessionId: this.sessionId,
|
||||
stepIndex: questionIndex
|
||||
})
|
||||
});
|
||||
@@ -947,6 +1357,8 @@ class KidsAIExplorer {
|
||||
setTimeout(() => {
|
||||
this.addContinueChoiceButtons();
|
||||
}, 1500);
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1174,6 +1586,8 @@ class KidsAIExplorer {
|
||||
async generateDeeperExplorationResponse(answer) {
|
||||
console.log('🚀 generateDeeperExplorationResponse called with:', answer);
|
||||
|
||||
this.showLoading();
|
||||
|
||||
try {
|
||||
// Get the last AI question for context
|
||||
const lastAIMessages = Array.from(this.conversationContainer.querySelectorAll('.ai-message.exploration'));
|
||||
@@ -1193,6 +1607,7 @@ class KidsAIExplorer {
|
||||
answer: answer,
|
||||
question: lastExplorationQuestion,
|
||||
language: this.currentLanguage,
|
||||
sessionId: this.sessionId,
|
||||
context: 'deeper_exploration_response',
|
||||
instructions: this.currentLanguage === 'de'
|
||||
? 'Du hilfst einem Kind beim tieferen Verstehen. Gib KEINE direkten Antworten oder Erklärungen. Stelle interessante Folgefragen, die das Kind zum Experimentieren oder Beobachten ermutigen. Verwende Analogien und lade zu eigenen Entdeckungen ein.'
|
||||
@@ -1267,6 +1682,8 @@ class KidsAIExplorer {
|
||||
setTimeout(() => {
|
||||
this.addContinueChoiceButtons();
|
||||
}, 1500);
|
||||
} finally {
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1287,9 +1704,11 @@ class KidsAIExplorer {
|
||||
askNextQuestion() {
|
||||
console.log('❓ askNextQuestion called, currentQuestionIndex:', this.currentQuestionIndex);
|
||||
|
||||
// DISABLED: Old completion logic for question-based system
|
||||
// We now use a conversational fundamentals approach, so don't auto-complete
|
||||
if (!this.questions || this.currentQuestionIndex >= this.questions.length) {
|
||||
console.log('✅ All questions completed');
|
||||
this.showCompletionMessage();
|
||||
console.log('✅ Questions completed, but continuing conversation mode');
|
||||
// Don't show completion - let conversation continue
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1353,10 +1772,18 @@ class KidsAIExplorer {
|
||||
inputContainer.classList.add('visible');
|
||||
this.scrollToBottomSmoothly();
|
||||
|
||||
// Focus on textarea
|
||||
// Focus on textarea and add Enter key handler
|
||||
const textarea = inputContainer.querySelector(`#${inputId}`);
|
||||
if (textarea) {
|
||||
textarea.focus();
|
||||
|
||||
// Add Enter key listener for chat input
|
||||
textarea.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
this.submitChatAnswerFromInput(inputId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
@@ -1432,10 +1859,33 @@ class KidsAIExplorer {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Smooth scroll to bottom of conversation
|
||||
// Enforce conversation container height periodically to prevent CSS conflicts
|
||||
enforceContainerHeight() {
|
||||
if (this.conversationContainer) {
|
||||
// Force height properties via JavaScript to override any CSS conflicts
|
||||
this.conversationContainer.style.height = '450px';
|
||||
this.conversationContainer.style.maxHeight = '450px';
|
||||
this.conversationContainer.style.minHeight = '450px';
|
||||
this.conversationContainer.style.overflowY = 'scroll'; // Force scrollbar visibility
|
||||
this.conversationContainer.style.overflowX = 'hidden';
|
||||
this.conversationContainer.style.display = 'block';
|
||||
this.conversationContainer.style.boxSizing = 'border-box';
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth scroll to bottom of conversation within the container
|
||||
scrollToBottomSmoothly() {
|
||||
if (this.conversationContainer) {
|
||||
this.conversationContainer.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||
// Use requestAnimationFrame for smooth scrolling
|
||||
requestAnimationFrame(() => {
|
||||
// Scroll to the bottom of the conversation container
|
||||
this.conversationContainer.scrollTop = this.conversationContainer.scrollHeight;
|
||||
|
||||
// Additional fallback for timing issues
|
||||
setTimeout(() => {
|
||||
this.conversationContainer.scrollTop = this.conversationContainer.scrollHeight;
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user