AI-Learning-Coach / index.html
Hma47's picture
Upload 4 files
1de8a7b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>AI-Interactive Metacognition Tracker</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- React & ReactDOM -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- @babel/standalone -->
<script crossorigin src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- Framer Motion - using different CDN -->
<script crossorigin src="https://cdn.jsdelivr.net/npm/framer-motion@10.16.4/dist/framer-motion.umd.js"></script>
<!-- Recharts - using different CDN -->
<script crossorigin src="https://cdn.jsdelivr.net/npm/recharts@2.8.0/umd/Recharts.min.js"></script>
<style>
html, body {
background: linear-gradient(135deg, #f8fafc 0%, #e0f2fe 100%);
min-height: 100%;
margin: 0;
}
.glass {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(10px);
border-radius: 1.25rem;
box-shadow: 0 8px 32px rgba(59, 130, 246, 0.15);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.ai-message {
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
color: white;
border-radius: 1rem 1rem 0.25rem 1rem;
padding: 1rem;
margin: 0.5rem 0;
max-width: 85%;
animation: slideInLeft 0.3s ease-out;
line-height: 1.6;
}
.ai-message p {
margin: 0.5rem 0;
}
.ai-message ul, .ai-message ol {
margin: 0.5rem 0;
padding-left: 1.5rem;
}
.ai-message li {
margin: 0.25rem 0;
}
.ai-message h3, .ai-message h4 {
margin: 1rem 0 0.5rem 0;
font-weight: bold;
}
.ai-message strong {
font-weight: bold;
}
.user-message {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
color: white;
border-radius: 1rem 1rem 1rem 0.25rem;
padding: 1rem;
margin: 0.5rem 0;
max-width: 80%;
margin-left: auto;
animation: slideInRight 0.3s ease-out;
}
.typing-indicator {
background: #f3f4f6;
border-radius: 1rem;
padding: 1rem;
margin: 0.5rem 0;
max-width: 80%;
animation: pulse 1.5s infinite;
}
.chat-container {
max-height: 500px;
overflow-y: auto;
padding: 1rem;
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
background: rgba(255, 255, 255, 0.5);
}
.progress-bar {
background: linear-gradient(90deg, #3b82f6 0%, #1d4ed8 100%);
height: 6px;
border-radius: 3px;
transition: width 0.5s ease;
}
@keyframes slideInLeft {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideInRight {
from { opacity: 0; transform: translateX(20px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.fade-in {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* Accessibility improvements */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
button:focus, input:focus, textarea:focus, select:focus {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
@media (max-width: 640px) {
.ai-message, .user-message {
max-width: 95%;
}
.chat-container {
max-height: 400px;
}
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useCallback, useRef } = React;
// Fallback motion component if framer-motion fails to load
const motion = window['framer-motion'] ? window['framer-motion'].motion : {
div: ({ children, className, initial, animate, ...props }) =>
React.createElement('div', { className: `${className} fade-in`, ...props }, children)
};
// Fallback chart components if Recharts fails to load
const Recharts = window['Recharts'] || {};
const {
LineChart = () => React.createElement('div', { className: 'text-center text-gray-500 p-4' }, 'Charts unavailable - external library loading issue'),
Line = () => null,
XAxis = () => null,
YAxis = () => null,
CartesianGrid = () => null,
Tooltip = () => null,
ResponsiveContainer = ({ children }) => React.createElement('div', { style: { width: '100%', height: '160px' } }, children),
RadarChart = () => React.createElement('div', { className: 'text-center text-gray-500 p-4' }, 'Charts unavailable - external library loading issue'),
PolarGrid = () => null,
PolarAngleAxis = () => null,
PolarRadiusAxis = () => null,
Radar = () => null
} = Recharts;
const AI_PROMPTS = {
welcome: `You are an AI learning coach specializing in metacognition. Your role is to guide users through a reflective learning process.
FORMATTING REQUIREMENTS:
- Use proper paragraph breaks with double line breaks
- Use bullet points (•) for lists
- Use numbered lists (1., 2., 3.) for sequential steps
- Use **bold** for emphasis on key points
- Structure responses with clear sections
- Keep paragraphs concise (2-3 sentences max)
Be encouraging, supportive, and ask thoughtful questions. Start by welcoming the user and explaining that you'll guide them through a metacognitive reflection process to improve their learning.`,
taskPlanning: `Help the user articulate their learning task clearly.
FORMATTING REQUIREMENTS:
- Use proper paragraph breaks
- Use bullet points for suggestions
- Use **bold** for key concepts
- Structure your response with clear sections
Ask about:
• Their specific learning goals
• What they want to achieve
• Help them break down complex tasks
Suggest appropriate learning strategies based on the task type. Be encouraging and help them set realistic expectations.`,
preLearning: `Guide the user through pre-learning reflection.
FORMATTING REQUIREMENTS:
- Use clear paragraph structure
- Use bullet points for strategies
- Use **bold** for important concepts
- Include numbered steps when appropriate
Help them:
• Assess their current confidence level
• Identify potential challenges
• Prepare mentally for the learning task
• Suggest specific strategies and resources
• Explore their prior knowledge and experience`,
duringLearning: `Support the user during their learning process.
FORMATTING REQUIREMENTS:
- Use paragraph breaks for readability
- Use bullet points for suggestions
- Use **bold** for key advice
- Structure responses clearly
Ask about:
• Their current progress
• Any difficulties they're encountering
• Whether their chosen strategies are working
Provide adaptive guidance and suggest adjustments if needed. Be encouraging and help them stay motivated.`,
postLearning: `Help the user evaluate their learning experience.
FORMATTING REQUIREMENTS:
- Use clear paragraph structure
- Use bullet points for reflection prompts
- Use **bold** for key insights
- Organize thoughts into sections
Guide them to reflect on:
• What worked well and what didn't
• Whether they achieved their goals
• What they learned about their own learning process
• Patterns and insights about their metacognitive strategies`,
developmentPlan: `Based on the entire conversation history, provide a comprehensive personalized development plan.
FORMATTING REQUIREMENTS:
- Use clear headings with **bold**
- Use bullet points and numbered lists extensively
- Use paragraph breaks for readability
- Structure as a complete action plan
Create a detailed plan that includes:
**METACOGNITIVE STRENGTHS IDENTIFIED:**
• List specific strengths observed
• Evidence from their responses
**AREAS FOR IMPROVEMENT:**
• Specific skills to develop
• Patterns that need attention
**PERSONALIZED LEARNING STRATEGIES:**
• Tailored strategies based on their task type
• Specific techniques that match their learning style
**ACTION PLAN:**
1. Immediate next steps (this week)
2. Short-term goals (next month)
3. Long-term development (next 3 months)
**RECOMMENDED ACTIVITIES:**
• Specific exercises and practices
• Resources and tools to use
• Frequency and timing suggestions
**PROGRESS TRACKING:**
• How to monitor improvement
• Key indicators to watch for
• When to reassess and adjust
Make this a comprehensive, actionable plan they can follow.`
};
function AIInteractiveMetacognitionTracker() {
// Core state
const [stage, setStage] = useState(0); // 0: Setup, 1: Task Planning, 2: Pre-Learning, 3: During Learning, 4: Post-Learning, 5: Development Plan
const [apiKey, setApiKey] = useState("");
const [apiKeyInput, setApiKeyInput] = useState("");
const [apiError, setApiError] = useState("");
// Conversation state
const [conversations, setConversations] = useState({});
const [currentInput, setCurrentInput] = useState("");
const [isAITyping, setIsAITyping] = useState(false);
const [aiLoading, setAILoading] = useState(false);
// User data
const [userProfile, setUserProfile] = useState({});
const [sessionData, setSessionData] = useState({});
const [reflectionHistory, setReflectionHistory] = useState([]);
// Refs
const chatEndRef = useRef(null);
// Load data from localStorage on mount
useEffect(() => {
try {
const savedApiKey = localStorage.getItem('ai-metacognition-api-key');
const savedProfile = localStorage.getItem('ai-metacognition-profile');
const savedHistory = localStorage.getItem('ai-metacognition-history');
if (savedApiKey) {
setApiKey(savedApiKey);
}
if (savedProfile) {
setUserProfile(JSON.parse(savedProfile));
}
if (savedHistory) {
setReflectionHistory(JSON.parse(savedHistory));
}
} catch (error) {
console.error('Error loading saved data:', error);
}
}, []);
// Save data to localStorage
useEffect(() => {
try {
if (apiKey) {
localStorage.setItem('ai-metacognition-api-key', apiKey);
}
} catch (error) {
console.error('Error saving API key:', error);
}
}, [apiKey]);
useEffect(() => {
try {
localStorage.setItem('ai-metacognition-profile', JSON.stringify(userProfile));
} catch (error) {
console.error('Error saving profile:', error);
}
}, [userProfile]);
useEffect(() => {
try {
localStorage.setItem('ai-metacognition-history', JSON.stringify(reflectionHistory));
} catch (error) {
console.error('Error saving history:', error);
}
}, [reflectionHistory]);
// Auto-scroll to bottom of chat
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [conversations, isAITyping]);
// Initialize conversation when stage changes
useEffect(() => {
if (stage > 0 && apiKey && !conversations[stage]) {
initializeStageConversation(stage);
}
}, [stage, apiKey]);
const initializeStageConversation = async (stageNum) => {
const stageNames = ['', 'Task Planning', 'Pre-Learning Reflection', 'During Learning Support', 'Post-Learning Evaluation', 'Development Plan'];
const stageName = stageNames[stageNum];
setIsAITyping(true);
try {
const contextPrompt = getContextPrompt(stageNum);
const aiResponse = await callAI(contextPrompt, stageNum);
setConversations(prev => ({
...prev,
[stageNum]: [
{ type: 'ai', content: aiResponse, timestamp: new Date().toISOString() }
]
}));
} catch (error) {
console.error('Error initializing conversation:', error);
setApiError(`Failed to initialize ${stageName}: ${error.message}`);
} finally {
setIsAITyping(false);
}
};
const getContextPrompt = (stageNum) => {
const basePrompt = AI_PROMPTS[Object.keys(AI_PROMPTS)[stageNum]];
// For the final stage, include comprehensive context from all previous stages
if (stageNum === 5) {
const allConversations = Object.keys(conversations)
.filter(key => parseInt(key) < 5)
.map(key => ({
stage: parseInt(key),
stageName: ['', 'Task Planning', 'Pre-Learning', 'During Learning', 'Post-Learning'][parseInt(key)],
conversation: conversations[key]
}));
const contextSummary = allConversations.map(stage => {
const userMessages = stage.conversation.filter(msg => msg.type === 'user').map(msg => msg.content);
const aiMessages = stage.conversation.filter(msg => msg.type === 'ai').map(msg => msg.content);
return `**${stage.stageName} (Stage ${stage.stage}):**
User Responses: ${userMessages.join(' | ')}
AI Guidance: ${aiMessages.slice(-1)[0] || 'No AI response'}`;
}).join('\n\n');
return `${basePrompt}
**COMPLETE SESSION CONTEXT:**
${contextSummary}
**SESSION DATA:**
${JSON.stringify(sessionData, null, 2)}
Based on this complete interaction history, create a comprehensive, personalized development plan.`;
}
const context = {
userProfile,
sessionData,
previousConversations: conversations,
stage: stageNum
};
return `${basePrompt}\n\nContext: ${JSON.stringify(context, null, 2)}\n\nProvide a welcoming message to start this stage.`;
};
const callAI = async (prompt, stageNum, userMessage = '') => {
if (!apiKey) {
throw new Error('API key not provided');
}
const messages = [
{ role: 'system', content: prompt }
];
// Add conversation history
const stageConversation = conversations[stageNum] || [];
stageConversation.forEach(msg => {
messages.push({
role: msg.type === 'ai' ? 'assistant' : 'user',
content: msg.content
});
});
if (userMessage) {
messages.push({ role: 'user', content: userMessage });
}
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey.trim()}`
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages,
max_tokens: stageNum === 5 ? 1000 : 600, // More tokens for final comprehensive plan
temperature: 0.7
})
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(`OpenAI API error: ${response.status} ${response.statusText}${errorData.error ? ` - ${errorData.error.message}` : ''}`);
}
const data = await response.json();
if (!data.choices || !data.choices[0] || !data.choices[0].message) {
throw new Error('Invalid response format from OpenAI API');
}
return data.choices[0].message.content;
};
const formatAIResponse = (content) => {
// Convert markdown-style formatting to HTML
let formatted = content
// Convert **bold** to <strong>
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
// Convert bullet points
.replace(/^• (.+)$/gm, '<li>$1</li>')
.replace(/^- (.+)$/gm, '<li>$1</li>')
// Convert numbered lists
.replace(/^(\d+)\. (.+)$/gm, '<li>$2</li>')
// Convert double line breaks to paragraphs
.split('\n\n')
.map(paragraph => {
if (paragraph.includes('<li>')) {
return `<ul>${paragraph}</ul>`;
}
return paragraph.trim() ? `<p>${paragraph.trim()}</p>` : '';
})
.join('');
return formatted;
};
const handleSendMessage = async () => {
if (!currentInput.trim() || isAITyping) return;
const userMessage = currentInput.trim();
setCurrentInput("");
// Add user message to conversation
setConversations(prev => ({
...prev,
[stage]: [
...(prev[stage] || []),
{ type: 'user', content: userMessage, timestamp: new Date().toISOString() }
]
}));
setIsAITyping(true);
setAILoading(true);
try {
const contextPrompt = getContextPrompt(stage);
const aiResponse = await callAI(contextPrompt, stage, userMessage);
// Add AI response to conversation
setConversations(prev => ({
...prev,
[stage]: [
...prev[stage],
{ type: 'ai', content: aiResponse, timestamp: new Date().toISOString() }
]
}));
// Update session data based on stage
updateSessionData(stage, userMessage, aiResponse);
} catch (error) {
console.error('Error getting AI response:', error);
setApiError(`AI Error: ${error.message}`);
} finally {
setIsAITyping(false);
setAILoading(false);
}
};
const updateSessionData = (stageNum, userMessage, aiResponse) => {
const timestamp = new Date().toISOString();
setSessionData(prev => ({
...prev,
[`stage${stageNum}`]: {
...prev[`stage${stageNum}`],
lastUpdate: timestamp,
userInput: userMessage,
aiGuidance: aiResponse,
keyInsights: extractKeyInsights(userMessage, stageNum)
}
}));
};
const extractKeyInsights = (userMessage, stageNum) => {
// Extract key insights based on stage
const insights = {};
switch(stageNum) {
case 1: // Task Planning
insights.learningTask = userMessage;
break;
case 2: // Pre-Learning
insights.initialConfidence = userMessage;
break;
case 3: // During Learning
insights.learningProgress = userMessage;
break;
case 4: // Post-Learning
insights.learningOutcome = userMessage;
break;
case 5: // Development Plan
insights.developmentCommitment = userMessage;
break;
}
return insights;
};
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
};
const validateApiKey = async () => {
if (!apiKeyInput.trim()) {
setApiError("Please enter your OpenAI API key");
return;
}
setAILoading(true);
setApiError("");
try {
const response = await fetch('https://api.openai.com/v1/models', {
headers: {
'Authorization': `Bearer ${apiKeyInput.trim()}`
}
});
if (!response.ok) {
throw new Error('Invalid API key');
}
setApiKey(apiKeyInput.trim());
setStage(1);
setApiKeyInput("");
} catch (error) {
setApiError("Invalid API key. Please check and try again.");
} finally {
setAILoading(false);
}
};
const nextStage = () => {
if (stage < 5) {
setStage(stage + 1);
}
};
const previousStage = () => {
if (stage > 1) {
setStage(stage - 1);
}
};
const completeSession = () => {
const sessionSummary = {
id: Date.now(),
timestamp: new Date().toISOString(),
conversations,
sessionData,
userProfile,
completedStages: 5
};
setReflectionHistory(prev => [...prev, sessionSummary]);
// Reset for new session
setStage(1);
setConversations({});
setSessionData({});
setCurrentInput("");
};
const getProgressPercentage = () => {
return (stage / 5) * 100;
};
const stageNames = [
'Setup',
'Task Planning',
'Pre-Learning Reflection',
'During Learning Support',
'Post-Learning Evaluation',
'Development Plan'
];
// Stage 0: API Key Setup
if (stage === 0) {
return (
<div className="min-h-screen flex flex-col items-center justify-center py-8">
<motion.div className="max-w-md w-full px-4">
<div className="glass p-8 text-center">
<div className="mb-6">
<h1 className="text-3xl font-bold text-blue-900 mb-2">🤖 AI Learning Coach</h1>
<p className="text-gray-600">Your personal AI guide for metacognitive reflection</p>
</div>
<div className="mb-6">
<label htmlFor="api-key" className="block font-semibold mb-2 text-left">OpenAI API Key</label>
<input
id="api-key"
type="password"
className="w-full px-4 py-3 border rounded-lg text-center"
placeholder="sk-..."
value={apiKeyInput}
onChange={e => setApiKeyInput(e.target.value)}
onKeyPress={e => e.key === 'Enter' && validateApiKey()}
autoFocus
/>
<p className="text-xs text-gray-500 mt-2">
Get your API key at <a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer" className="text-blue-600 underline">OpenAI</a>
</p>
</div>
{apiError && (
<div className="mb-4 text-red-600 text-sm">{apiError}</div>
)}
<button
onClick={validateApiKey}
disabled={aiLoading || !apiKeyInput.trim()}
className="w-full bg-blue-600 text-white py-3 rounded-lg font-semibold hover:bg-blue-700 transition disabled:opacity-50"
>
{aiLoading ? "Validating..." : "Start AI-Guided Learning"}
</button>
<div className="mt-6 text-xs text-gray-500">
<p>🔒 Your API key is stored locally and used only for this session</p>
<p>🎯 AI will guide you through 5 stages of metacognitive reflection</p>
<p>📝 Responses will be structured with clear formatting</p>
</div>
</div>
</motion.div>
</div>
);
}
// Main Application Interface
return (
<div className="min-h-screen flex flex-col py-4">
<div className="max-w-4xl w-full mx-auto px-4">
{/* Header */}
<div className="glass p-4 mb-4">
<div className="flex items-center justify-between mb-4">
<div>
<h1 className="text-2xl font-bold text-blue-900">🤖 AI Learning Coach</h1>
<p className="text-sm text-gray-600">Stage {stage}: {stageNames[stage]}</p>
</div>
<div className="text-right">
<div className="text-xs text-gray-500 mb-1">Progress</div>
<div className="w-32 bg-gray-200 rounded-full h-2">
<div
className="progress-bar rounded-full h-2"
style={{ width: `${getProgressPercentage()}%` }}
></div>
</div>
</div>
</div>
{/* Stage Navigation */}
<div className="flex gap-2 text-xs flex-wrap">
{stageNames.slice(1).map((name, i) => (
<span
key={i}
className={`px-2 py-1 rounded-full ${
stage === i + 1
? "bg-blue-600 text-white"
: stage > i + 1
? "bg-green-100 text-green-800"
: "bg-gray-100 text-gray-600"
}`}
>
{i + 1}. {name}
</span>
))}
</div>
</div>
{/* Chat Interface */}
<div className="glass p-4 mb-4">
<div className="chat-container">
{conversations[stage]?.map((message, index) => (
<div
key={index}
className={message.type === 'ai' ? 'ai-message' : 'user-message'}
>
<div className="flex items-start gap-2">
<span className="text-xs opacity-75 mt-1">
{message.type === 'ai' ? '🤖' : '👤'}
</span>
<div className="flex-1">
{message.type === 'ai' ? (
<div dangerouslySetInnerHTML={{ __html: formatAIResponse(message.content) }} />
) : (
message.content
)}
</div>
</div>
</div>
))}
{isAITyping && (
<div className="typing-indicator">
<div className="flex items-center gap-2">
<span className="text-xs">🤖</span>
<div>AI is thinking...</div>
<div className="flex gap-1">
<div className="w-2 h-2 bg-blue-500 rounded-full animate-bounce"></div>
<div className="w-2 h-2 bg-blue-500 rounded-full animate-bounce" style={{animationDelay: '0.1s'}}></div>
<div className="w-2 h-2 bg-blue-500 rounded-full animate-bounce" style={{animationDelay: '0.2s'}}></div>
</div>
</div>
</div>
)}
<div ref={chatEndRef} />
</div>
{/* Input Area */}
<div className="mt-4 flex gap-2">
<textarea
value={currentInput}
onChange={e => setCurrentInput(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Type your response here... (Press Enter to send, Shift+Enter for new line)"
className="flex-1 px-3 py-2 border rounded-lg resize-none"
rows={3}
disabled={isAITyping}
/>
<button
onClick={handleSendMessage}
disabled={!currentInput.trim() || isAITyping}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition disabled:opacity-50"
>
Send
</button>
</div>
{apiError && (
<div className="mt-2 text-red-600 text-sm">{apiError}</div>
)}
</div>
{/* Navigation */}
<div className="glass p-4">
<div className="flex justify-between items-center">
<button
onClick={previousStage}
disabled={stage <= 1}
className="px-4 py-2 bg-gray-300 text-gray-700 rounded-lg hover:bg-gray-400 transition disabled:opacity-50"
>
← Previous Stage
</button>
<div className="text-center">
<p className="text-sm text-gray-600">
{conversations[stage]?.length || 0} messages in this stage
</p>
{stage === 5 && (
<p className="text-xs text-blue-600 font-semibold">
📋 Comprehensive Development Plan
</p>
)}
</div>
{stage < 5 ? (
<button
onClick={nextStage}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"
>
Next Stage →
</button>
) : (
<button
onClick={completeSession}
className="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition"
>
Complete Session ✓
</button>
)}
</div>
</div>
{/* Session History */}
{reflectionHistory.length > 0 && (
<div className="glass p-4 mt-4">
<h3 className="font-bold text-blue-900 mb-2">Previous Sessions</h3>
<div className="text-sm text-gray-600">
{reflectionHistory.length} completed reflection session{reflectionHistory.length !== 1 ? 's' : ''}
</div>
</div>
)}
</div>
</div>
);
}
ReactDOM.createRoot(document.getElementById("root")).render(<AIInteractiveMetacognitionTracker />);
</script>
</body>
</html>