Hma47's picture
Upload 4 files
23e8711 verified
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Educational Activity Intelligence Analyzer</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
/* CSS Reset and Base Styles */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* CSS Custom Properties */
:root {
/* Color System - Intelligence/Analysis theme */
--color-primary: #7c3aed;
--color-primary-hover: #6d28d9;
--color-secondary: #3b82f6;
--color-accent: #8b5cf6;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-error: #ef4444;
/* Neutral Colors - Light Mode */
--color-background: #faf7ff;
--color-surface: #ffffff;
--color-surface-elevated: #ffffff;
--color-border: #e9d5ff;
--color-border-focus: #7c3aed;
--color-text-primary: #1f2937;
--color-text-secondary: #4b5563;
--color-text-muted: #6b7280;
/* Typography */
--font-family-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
/* Optimized Spacing System */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 0.75rem;
--spacing-lg: 1rem;
--spacing-xl: 1.25rem;
--spacing-2xl: 1.5rem;
--spacing-3xl: 2rem;
/* Border Radius */
--radius-sm: 0.375rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
--radius-xl: 1rem;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
/* Transitions */
--transition-fast: 150ms ease-in-out;
--transition-normal: 250ms ease-in-out;
--transition-slow: 350ms ease-in-out;
/* Layout */
--container-max-width: 1200px;
--content-max-width: 1000px;
}
/* RTL Support */
[dir="rtl"] {
text-align: right;
}
[dir="rtl"] .app-container {
direction: rtl;
}
[dir="rtl"] .api-key-compact {
left: auto;
right: var(--spacing-lg);
}
[dir="rtl"] .language-switcher {
right: auto;
left: var(--spacing-lg);
}
/* Dark Mode Support */
@media (prefers-color-scheme: dark) {
:root {
--color-background: #0f0a1a;
--color-surface: #1e1b31;
--color-surface-elevated: #2d2a45;
--color-border: #2d2a45;
--color-text-primary: #f9fafb;
--color-text-secondary: #d1d5db;
--color-text-muted: #9ca3af;
}
}
/* Base Styles */
html {
font-size: 16px;
line-height: 1.5;
scroll-behavior: smooth;
}
body {
font-family: var(--font-family-primary);
background-color: var(--color-background);
color: var(--color-text-primary);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100vh;
transition: all 0.3s ease;
}
/* Language Selection Landing */
.language-landing {
display: block;
max-width: 600px;
margin: 50px auto;
background: var(--color-surface);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-lg);
padding: 40px;
text-align: center;
}
.language-landing h1 {
font-size: 2.5rem;
font-weight: 800;
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 15px;
}
.language-landing p {
font-size: 1.1rem;
color: var(--color-text-secondary);
margin-bottom: 30px;
}
.language-selector {
margin-bottom: 25px;
}
.language-selector label {
display: block;
margin-bottom: 10px;
font-weight: 600;
color: var(--color-text-primary);
font-size: 1.1rem;
}
.language-selector select {
width: 100%;
padding: 15px 20px;
border: 2px solid var(--color-border);
border-radius: var(--radius-lg);
font-size: 1.1rem;
font-family: inherit;
background: var(--color-surface);
transition: all 0.2s ease;
margin-bottom: 20px;
}
.language-selector select:focus {
outline: none;
border-color: var(--color-border-focus);
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
}
.api-key-landing {
margin-bottom: 25px;
}
.api-key-landing label {
display: block;
margin-bottom: 10px;
font-weight: 600;
color: var(--color-text-primary);
font-size: 1.1rem;
}
.api-key-landing input {
width: 100%;
padding: 15px 20px;
border: 2px solid var(--color-border);
border-radius: var(--radius-lg);
font-size: 1.1rem;
background: var(--color-surface);
transition: all 0.2s ease;
font-family: 'Courier New', monospace;
}
.api-key-landing input:focus {
outline: none;
border-color: var(--color-border-focus);
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
}
.start-btn {
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
color: white;
padding: 18px 40px;
border: none;
border-radius: var(--radius-lg);
font-size: 1.2rem;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
width: 100%;
}
.start-btn:hover {
transform: translateY(-2px);
box-shadow: 0 12px 24px -8px rgba(124, 58, 237, 0.4);
}
.start-btn:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
/* Cache Management Section */
.cache-management {
background: rgba(124, 58, 237, 0.05);
border: 1px solid rgba(124, 58, 237, 0.1);
border-radius: var(--radius-lg);
padding: 20px;
margin-bottom: 25px;
text-align: center;
}
.cache-management h3 {
color: var(--color-primary);
margin-bottom: 15px;
font-size: 1.1rem;
font-weight: 600;
}
.cache-status-display {
background: rgba(16, 185, 129, 0.1);
border: 1px solid rgba(16, 185, 129, 0.2);
color: var(--color-success);
padding: 12px 16px;
border-radius: var(--radius-md);
margin-bottom: 15px;
font-size: 0.9rem;
font-weight: 500;
}
.cache-status-display.no-cache {
background: rgba(100, 116, 139, 0.1);
border-color: rgba(100, 116, 139, 0.2);
color: var(--color-text-secondary);
}
.clear-cache-btn {
background: linear-gradient(135deg, var(--color-warning) 0%, #f59e0b 100%);
color: white;
padding: 12px 24px;
border: none;
border-radius: var(--radius-md);
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.clear-cache-btn:hover {
transform: translateY(-1px);
box-shadow: 0 6px 12px -4px rgba(245, 158, 11, 0.4);
}
.clear-cache-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
/* Cache Status Indicator */
.cache-status {
background: rgba(16, 185, 129, 0.1);
border: 1px solid rgba(16, 185, 129, 0.2);
color: var(--color-success);
padding: 8px 12px;
border-radius: var(--radius-sm);
margin-bottom: 15px;
font-size: 0.85rem;
text-align: center;
font-weight: 500;
display: none;
}
.cache-status.cached {
display: block;
}
.cache-status.translating {
background: rgba(124, 58, 237, 0.1);
border-color: rgba(124, 58, 237, 0.2);
color: var(--color-primary);
}
/* Translation Loading Overlay */
.translation-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(124, 58, 237, 0.9);
display: none;
justify-content: center;
align-items: center;
z-index: 10000;
color: white;
text-align: center;
}
.translation-content {
background: rgba(255, 255, 255, 0.1);
padding: 40px;
border-radius: var(--radius-xl);
backdrop-filter: blur(10px);
}
.translation-spinner {
width: 50px;
height: 50px;
border: 4px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s ease-in-out infinite;
margin: 0 auto 20px;
}
/* Main Application (Hidden Initially) */
.main-app {
display: none;
}
/* Layout Components */
.app-container {
min-height: 100vh;
max-width: var(--container-max-width);
margin: 0 auto;
padding: var(--spacing-lg);
position: relative;
}
/* Language Switcher in Main App */
.language-switcher {
position: absolute;
top: var(--spacing-lg);
right: var(--spacing-lg);
display: flex;
gap: 10px;
align-items: center;
z-index: 10;
}
.language-switch-btn {
background: rgba(124, 58, 237, 0.05);
border: 1px solid rgba(124, 58, 237, 0.1);
border-radius: var(--radius-md);
padding: 8px 12px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s ease;
color: var(--color-primary);
font-weight: 500;
}
.language-switch-btn:hover {
background: rgba(124, 58, 237, 0.1);
}
.mini-clear-cache {
background: rgba(245, 158, 11, 0.1);
border: 1px solid rgba(245, 158, 11, 0.2);
color: var(--color-warning);
padding: 6px 10px;
border-radius: var(--radius-sm);
font-size: 11px;
cursor: pointer;
transition: all 0.2s ease;
font-weight: 500;
}
.mini-clear-cache:hover {
background: rgba(245, 158, 11, 0.2);
}
/* Compact API Key in Top-Left */
.api-key-compact {
position: absolute;
top: var(--spacing-lg);
left: var(--spacing-lg);
z-index: 10;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: var(--spacing-md) var(--spacing-lg);
box-shadow: var(--shadow-md);
transition: all var(--transition-normal);
max-width: 280px;
}
.api-key-compact:hover {
box-shadow: var(--shadow-lg);
}
.api-key-compact-label {
display: block;
font-size: 0.75rem;
font-weight: 600;
color: var(--color-text-secondary);
margin-bottom: var(--spacing-xs);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.api-key-compact-input {
width: 100%;
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
font-size: 0.875rem;
background: var(--color-background);
color: var(--color-text-primary);
transition: all var(--transition-fast);
font-family: 'Courier New', monospace;
}
.api-key-compact-input:focus {
outline: none;
border-color: var(--color-border-focus);
box-shadow: 0 0 0 2px rgb(124 58 237 / 0.1);
}
.api-key-compact-input::placeholder {
color: var(--color-text-muted);
font-size: 0.75rem;
}
/* Main Content */
.app-main {
max-width: var(--content-max-width);
margin: 0 auto;
padding-top: var(--spacing-2xl);
}
/* Header */
.app-header {
text-align: center;
margin-bottom: var(--spacing-3xl);
}
.app-title {
font-size: 2.5rem;
font-weight: 700;
background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: var(--spacing-lg);
line-height: 1.2;
}
.app-subtitle {
font-size: 1.125rem;
color: var(--color-text-secondary);
max-width: 700px;
margin: 0 auto;
line-height: 1.4;
}
/* Input Section */
.input-section {
background: var(--color-surface);
border-radius: var(--radius-xl);
padding: var(--spacing-3xl);
box-shadow: var(--shadow-md);
margin-bottom: var(--spacing-3xl);
transition: all var(--transition-normal);
}
.input-section:hover {
box-shadow: var(--shadow-lg);
}
.section-title {
font-size: 1.5rem;
font-weight: 600;
color: var(--color-text-primary);
margin-bottom: var(--spacing-xl);
text-align: center;
}
.form-group {
margin-bottom: var(--spacing-xl);
}
.form-label {
display: block;
font-size: 1rem;
font-weight: 600;
color: var(--color-text-primary);
margin-bottom: var(--spacing-md);
}
.form-textarea {
width: 100%;
min-height: 120px;
padding: var(--spacing-lg);
border: 2px solid var(--color-border);
border-radius: var(--radius-lg);
font-size: 1rem;
font-family: var(--font-family-primary);
background: var(--color-background);
color: var(--color-text-primary);
transition: all var(--transition-fast);
resize: vertical;
}
.form-textarea:focus {
outline: none;
border-color: var(--color-border-focus);
box-shadow: 0 0 0 3px rgb(124 58 237 / 0.1);
}
.form-textarea::placeholder {
color: var(--color-text-muted);
}
.analyze-button {
display: inline-flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-lg) var(--spacing-3xl);
font-size: 1.125rem;
font-weight: 600;
color: white;
background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
border: none;
border-radius: var(--radius-lg);
cursor: pointer;
transition: all var(--transition-normal);
box-shadow: var(--shadow-md);
position: relative;
overflow: hidden;
margin: 0 auto;
display: block;
}
.analyze-button:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: var(--shadow-xl);
}
.analyze-button:active {
transform: translateY(0);
}
.analyze-button:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
.button-text {
transition: opacity var(--transition-fast);
}
.loading-spinner {
display: none;
width: 20px;
height: 20px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top: 2px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Results Section */
.results-container {
display: grid;
gap: var(--spacing-3xl);
margin-top: var(--spacing-3xl);
}
.agent-card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-xl);
padding: var(--spacing-3xl);
box-shadow: var(--shadow-md);
transition: all var(--transition-normal);
position: relative;
overflow: hidden;
}
.agent-card:hover {
box-shadow: var(--shadow-lg);
border-color: var(--color-accent);
}
.agent-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--color-primary), var(--color-secondary));
}
.agent-header {
display: flex;
align-items: center;
gap: var(--spacing-lg);
margin-bottom: var(--spacing-xl);
}
.agent-icon {
width: 48px;
height: 48px;
background: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 700;
font-size: 1.25rem;
}
.agent-title {
font-size: 1.5rem;
font-weight: 600;
color: var(--color-text-primary);
margin: 0;
}
.agent-subtitle {
font-size: 0.875rem;
color: var(--color-text-secondary);
margin: 0;
}
.agent-content {
background: var(--color-background);
border-radius: var(--radius-lg);
padding: var(--spacing-xl);
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.875rem;
line-height: 1.6;
color: var(--color-text-primary);
white-space: pre-wrap;
word-wrap: break-word;
border: 1px solid var(--color-border);
min-height: 100px;
max-height: 600px;
overflow-y: auto;
}
.agent-content:empty::before {
content: 'Analysis will appear here...';
color: var(--color-text-muted);
font-style: italic;
font-family: var(--font-family-primary);
}
/* Loading States */
.agent-card.loading .agent-content {
display: flex;
align-items: center;
justify-content: center;
min-height: 120px;
}
.agent-card.loading .agent-content::before {
content: '';
width: 32px;
height: 32px;
border: 3px solid var(--color-border);
border-top: 3px solid var(--color-primary);
border-radius: 50%;
animation: spin 1s linear infinite;
}
/* Error Handling */
.error-message {
background: #fef2f2;
border: 1px solid #fecaca;
color: var(--color-error);
padding: var(--spacing-lg);
border-radius: var(--radius-lg);
margin-bottom: var(--spacing-lg);
display: flex;
align-items: center;
gap: var(--spacing-md);
}
.error-icon {
font-size: 1.25rem;
}
.error-dismiss {
margin-left: auto;
background: none;
border: none;
color: var(--color-error);
cursor: pointer;
font-size: 1.25rem;
padding: var(--spacing-xs);
border-radius: var(--radius-sm);
}
.error-dismiss:hover {
background: rgba(239, 68, 68, 0.1);
}
/* Hidden sections */
.hidden {
display: none !important;
}
/* Footer */
.app-footer {
text-align: center;
padding: var(--spacing-xl);
color: var(--color-text-secondary);
font-size: 0.9rem;
margin-top: var(--spacing-3xl);
}
/* Responsive Design */
@media (max-width: 768px) {
.language-landing {
margin: 20px auto;
padding: 30px 20px;
}
.app-container {
padding: var(--spacing-md);
}
.api-key-compact {
position: static;
margin-bottom: var(--spacing-xl);
max-width: none;
}
.language-switcher {
position: relative;
top: auto;
right: auto;
margin-bottom: var(--spacing-xl);
justify-content: center;
}
.app-main {
padding-top: 0;
}
.app-title {
font-size: 2rem;
}
.app-subtitle {
font-size: 1rem;
}
.input-section,
.agent-card {
padding: var(--spacing-xl);
}
.agent-header {
flex-direction: column;
text-align: center;
gap: var(--spacing-md);
}
.agent-content {
font-size: 0.8rem;
max-height: 400px;
}
}
@media (max-width: 480px) {
.language-landing h1 {
font-size: 2rem;
}
.app-title {
font-size: 1.8rem;
}
}
/* Accessibility */
.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;
}
/* Focus styles for keyboard navigation */
.analyze-button:focus,
.api-key-compact-input:focus,
.form-textarea:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Custom scrollbar for agent content */
.agent-content::-webkit-scrollbar {
width: 8px;
}
.agent-content::-webkit-scrollbar-track {
background: var(--color-border);
border-radius: var(--radius-sm);
}
.agent-content::-webkit-scrollbar-thumb {
background: var(--color-accent);
border-radius: var(--radius-sm);
}
.agent-content::-webkit-scrollbar-thumb:hover {
background: var(--color-primary);
}
</style>
</head>
<body>
<!-- Translation Loading Overlay -->
<div class="translation-overlay" id="translationOverlay">
<div class="translation-content">
<div class="translation-spinner"></div>
<h2 id="translationTitle">Translating Interface...</h2>
<p id="translationMessage">Please wait while we translate the interface to your selected language.</p>
</div>
</div>
<!-- Language Selection Landing Page -->
<div class="language-landing" id="languageLanding">
<h1 data-translate="app_title">Educational Activity Intelligence Analyzer</h1>
<p data-translate="welcome_message">Welcome! Please select your preferred language and enter your API key to get started with advanced multi-agent analysis of teaching activities.</p>
<!-- Cache Status Indicator -->
<div class="cache-status" id="cacheStatus">
💾 Translations cached - instant loading!
</div>
<!-- Cache Management Section -->
<div class="cache-management" id="cacheManagement">
<h3 data-translate="cache_management_title">🗂️ Translation Cache Management</h3>
<div class="cache-status-display" id="cacheStatusDisplay">
<span data-translate="cache_status_checking">Checking cache status...</span>
</div>
<button class="clear-cache-btn" id="clearCacheBtn" data-translate="clear_cache_button">
🗑️ Clear All Cached Translations
</button>
</div>
<div class="language-selector">
<label for="languageSelect" data-translate="select_language">🌐 Select Language</label>
<select id="languageSelect">
<option value="en">🇺🇸 English</option>
<option value="es">🇪🇸 Español (Spanish)</option>
<option value="fr">🇫🇷 Français (French)</option>
<option value="de">🇩🇪 Deutsch (German)</option>
<option value="zh">🇨🇳 中文 (Chinese)</option>
<option value="ja">🇯🇵 日本語 (Japanese)</option>
<option value="ko">🇰🇷 한국어 (Korean)</option>
<option value="pt">🇵🇹 Português (Portuguese)</option>
<option value="it">🇮🇹 Italiano (Italian)</option>
<option value="ar">🇸🇦 العربية (Arabic)</option>
<option value="ru">🇷🇺 Русский (Russian)</option>
<option value="hi">🇮🇳 हिन्दी (Hindi)</option>
<option value="bn">🇧🇩 বাংলা (Bengali)</option>
<option value="ur">🇵🇰 اردو (Urdu)</option>
<option value="tr">🇹🇷 Türkçe (Turkish)</option>
<option value="pl">🇵🇱 Polski (Polish)</option>
<option value="nl">🇳🇱 Nederlands (Dutch)</option>
<option value="sv">🇸🇪 Svenska (Swedish)</option>
<option value="da">🇩🇰 Dansk (Danish)</option>
<option value="no">🇳🇴 Norsk (Norwegian)</option>
<option value="fi">🇫🇮 Suomi (Finnish)</option>
<option value="is">🇮🇸 Íslenska (Icelandic)</option>
<option value="cs">🇨🇿 Čeština (Czech)</option>
<option value="sk">🇸🇰 Slovenčina (Slovak)</option>
<option value="hu">🇭🇺 Magyar (Hungarian)</option>
<option value="ro">🇷🇴 Română (Romanian)</option>
<option value="bg">🇧🇬 Български (Bulgarian)</option>
<option value="hr">🇭🇷 Hrvatski (Croatian)</option>
<option value="sr">🇷🇸 Српски (Serbian)</option>
<option value="sl">🇸🇮 Slovenščina (Slovenian)</option>
<option value="mk">🇲🇰 Македонски (Macedonian)</option>
<option value="sq">🇦🇱 Shqip (Albanian)</option>
<option value="lv">🇱🇻 Latviešu (Latvian)</option>
<option value="lt">🇱🇹 Lietuvių (Lithuanian)</option>
<option value="et">🇪🇪 Eesti (Estonian)</option>
<option value="mt">🇲🇹 Malti (Maltese)</option>
<option value="ga">🇮🇪 Gaeilge (Irish)</option>
<option value="cy">🏴󠁧󠁢󠁷󠁬󠁳󠁿 Cymraeg (Welsh)</option>
<option value="eu">🏴󠁥󠁳󠁰󠁶󠁿 Euskera (Basque)</option>
<option value="ca">🏴󠁥󠁳󠁣󠁴󠁿 Català (Catalan)</option>
<option value="gl">🏴󠁥󠁳󠁧󠁡󠁿 Galego (Galician)</option>
<option value="el">🇬🇷 Ελληνικά (Greek)</option>
<option value="he">🇮🇱 עברית (Hebrew)</option>
<option value="fa">🇮🇷 فارسی (Persian)</option>
<option value="ps">🇦🇫 پښتو (Pashto)</option>
<option value="ku">🏴󠁩󠁱󠁫󠁲󠁿 کوردی (Kurdish)</option>
<option value="az">🇦🇿 Azərbaycan (Azerbaijani)</option>
<option value="kk">🇰🇿 Қазақша (Kazakh)</option>
<option value="ky">🇰🇬 Кыргызча (Kyrgyz)</option>
<option value="uz">🇺🇿 O'zbek (Uzbek)</option>
<option value="tk">🇹🇲 Türkmen (Turkmen)</option>
<option value="tg">🇹🇯 Тоҷикӣ (Tajik)</option>
<option value="mn">🇲🇳 Монгол (Mongolian)</option>
<option value="ka">🇬🇪 ქართული (Georgian)</option>
<option value="hy">🇦🇲 Հայերեն (Armenian)</option>
<option value="th">🇹🇭 ไทย (Thai)</option>
<option value="vi">🇻🇳 Tiếng Việt (Vietnamese)</option>
<option value="lo">🇱🇦 ລາວ (Lao)</option>
<option value="km">🇰🇭 ខ្មែរ (Khmer)</option>
<option value="my">🇲🇲 မြန်မာ (Myanmar)</option>
<option value="si">🇱🇰 සිංහල (Sinhala)</option>
<option value="ta">🇱🇰 தமிழ் (Tamil)</option>
<option value="te">🇮🇳 తెలుగు (Telugu)</option>
<option value="kn">🇮🇳 ಕನ್ನಡ (Kannada)</option>
<option value="ml">🇮🇳 മലയാളം (Malayalam)</option>
<option value="gu">🇮🇳 ગુજરાતી (Gujarati)</option>
<option value="pa">🇮🇳 ਪੰਜਾਬੀ (Punjabi)</option>
<option value="or">🇮🇳 ଓଡ଼ିଆ (Odia)</option>
<option value="as">🇮🇳 অসমীয়া (Assamese)</option>
<option value="ne">🇳🇵 नेपाली (Nepali)</option>
<option value="dz">🇧🇹 རྫོང་ཁ (Dzongkha)</option>
<option value="bo">🏔️ བོད་ཡིག (Tibetan)</option>
<option value="id">🇮🇩 Bahasa Indonesia</option>
<option value="ms">🇲🇾 Bahasa Melayu (Malay)</option>
<option value="tl">🇵🇭 Filipino (Tagalog)</option>
<option value="ceb">🇵🇭 Cebuano</option>
<option value="haw">🏝️ ʻŌlelo Hawaiʻi (Hawaiian)</option>
<option value="mi">🇳🇿 Te Reo Māori (Maori)</option>
<option value="sm">🇼🇸 Gagana Samoa (Samoan)</option>
<option value="to">🇹🇴 Lea Fakatonga (Tongan)</option>
<option value="fj">🇫🇯 Na Vosa Vakaviti (Fijian)</option>
<option value="mg">🇲🇬 Malagasy</option>
<option value="sw">🇰🇪 Kiswahili (Swahili)</option>
<option value="zu">🇿🇦 isiZulu (Zulu)</option>
<option value="xh">🇿🇦 isiXhosa (Xhosa)</option>
<option value="af">🇿🇦 Afrikaans</option>
<option value="st">🇱🇸 Sesotho (Southern Sotho)</option>
<option value="tn">🇧🇼 Setswana (Tswana)</option>
<option value="ss">🇸🇿 siSwati (Swati)</option>
<option value="ve">🇿🇦 Tshivenḓa (Venda)</option>
<option value="ts">🇿🇦 Xitsonga (Tsonga)</option>
<option value="nr">🇿🇦 isiNdebele (Southern Ndebele)</option>
<option value="am">🇪🇹 አማርኛ (Amharic)</option>
<option value="ti">🇪🇷 ትግርኛ (Tigrinya)</option>
<option value="om">🇪🇹 Afaan Oromoo (Oromo)</option>
<option value="so">🇸🇴 Soomaali (Somali)</option>
<option value="ha">🇳🇬 Hausa</option>
<option value="yo">🇳🇬 Yorùbá (Yoruba)</option>
<option value="ig">🇳🇬 Igbo</option>
<option value="ff">🇸🇳 Fulfulde (Fulani)</option>
<option value="wo">🇸🇳 Wolof</option>
<option value="bm">🇲🇱 Bamanankan (Bambara)</option>
<option value="rn">🇧🇮 Kirundi (Rundi)</option>
<option value="rw">🇷🇼 Kinyarwanda (Rwanda)</option>
<option value="lg">🇺🇬 Luganda</option>
<option value="ny">🇲🇼 Chichewa (Nyanja)</option>
<option value="sn">🇿🇼 chiShona (Shona)</option>
<option value="nd">🇿🇼 isiNdebele (Northern Ndebele)</option>
</select>
</div>
<div class="api-key-landing">
<label for="apiKeyLanding" data-translate="api_key_label">🔑 OpenAI API Key</label>
<input type="password" id="apiKeyLanding" placeholder="Enter your OpenAI API key" data-translate-placeholder="api_key_placeholder">
</div>
<button class="start-btn" id="startBtn" data-translate="start_button">🚀 Start Educational Analysis</button>
</div>
<!-- Main Application -->
<div class="main-app" id="mainApp">
<div class="app-container">
<!-- Language Switcher -->
<div class="language-switcher" id="languageSwitcher">
<div class="language-switch-btn" onclick="showLanguageLanding()">
<span data-translate="change_language">🌐 Change Language</span>
</div>
<div class="mini-clear-cache" onclick="clearAllTranslationCache()" title="Clear translation cache">
<span data-translate="clear_cache_mini">🗑️ Clear Cache</span>
</div>
</div>
<!-- Compact API Key in Top-Left -->
<div class="api-key-compact">
<label for="apiKey" class="api-key-compact-label" data-translate="api_key_short">API Key</label>
<input
type="password"
id="apiKey"
class="api-key-compact-input"
data-translate-placeholder="api_key_placeholder"
autocomplete="off"
>
</div>
<main class="app-main">
<header class="app-header">
<h1 class="app-title" data-translate="app_title">Educational Activity Intelligence Analyzer</h1>
<p class="app-subtitle" data-translate="app_subtitle">
Advanced multi-agent analysis of teaching activities using PICRAT model, Bloom's Taxonomy, and practical scenario development.
Get comprehensive insights from three specialized AI agents to enhance your educational technology integration.
</p>
</header>
<!-- Input Section -->
<section class="input-section">
<h2 class="section-title" data-translate="section_title">Describe Your Teaching Activity</h2>
<form id="analysisForm">
<div class="form-group">
<label for="activityDescription" class="form-label" data-translate="activity_label">Activity Description</label>
<textarea
id="activityDescription"
class="form-textarea"
data-translate-placeholder="activity_placeholder"
required
></textarea>
</div>
<button type="submit" class="analyze-button">
<span class="button-text" data-translate="analyze_button">🧠 Analyze Activity</span>
<div class="loading-spinner" id="loadingSpinner"></div>
</button>
</form>
</section>
<!-- Results Section -->
<section class="results-container">
<!-- Agent 1: Deep Structured Analysis -->
<div class="agent-card" id="agent1Card">
<div class="agent-header">
<div class="agent-icon">1</div>
<div>
<h3 class="agent-title" data-translate="agent1_title">Deep Structured Analysis</h3>
<p class="agent-subtitle" data-translate="agent1_subtitle">PICRAT Model • Bloom's Taxonomy • Critical Thinking Assessment</p>
</div>
</div>
<div class="agent-content" id="analysisOutput1"></div>
</div>
<!-- Agent 2: Practical Scenarios -->
<div class="agent-card" id="agent2Card">
<div class="agent-header">
<div class="agent-icon">2</div>
<div>
<h3 class="agent-title" data-translate="agent2_title">Practical Scenarios</h3>
<p class="agent-subtitle" data-translate="agent2_subtitle">Implementation Strategies • Enhancement Opportunities • Real-world Applications</p>
</div>
</div>
<div class="agent-content" id="analysisOutput2"></div>
</div>
<!-- Agent 3: Feedback & Overview -->
<div class="agent-card" id="agent3Card">
<div class="agent-header">
<div class="agent-icon">3</div>
<div>
<h3 class="agent-title" data-translate="agent3_title">Meta-Analysis & Recommendations</h3>
<p class="agent-subtitle" data-translate="agent3_subtitle">Synthesis • Best Practices Alignment • Strategic Recommendations</p>
</div>
</div>
<div class="agent-content" id="analysisOutput3"></div>
</div>
</section>
</main>
<footer class="app-footer">
<p>Created By Shift Mind AI Labs</p>
</footer>
</div>
</div>
<script>
// RTL languages list
const rtlLanguages = ['ar', 'he', 'fa', 'ur', 'ps', 'ku'];
// Current language and API key
let currentLanguage = 'en';
let currentApiKey = '';
// Language names mapping
const languageNames = {
en: 'English', es: 'Spanish', fr: 'French', de: 'German', zh: 'Chinese',
ja: 'Japanese', ko: 'Korean', pt: 'Portuguese', it: 'Italian', ar: 'Arabic',
ru: 'Russian', hi: 'Hindi', bn: 'Bengali', ur: 'Urdu', tr: 'Turkish',
pl: 'Polish', nl: 'Dutch', sv: 'Swedish', da: 'Danish', no: 'Norwegian',
fi: 'Finnish', is: 'Icelandic', cs: 'Czech', sk: 'Slovak', hu: 'Hungarian',
ro: 'Romanian', bg: 'Bulgarian', hr: 'Croatian', sr: 'Serbian', sl: 'Slovenian',
mk: 'Macedonian', sq: 'Albanian', lv: 'Latvian', lt: 'Lithuanian', et: 'Estonian',
mt: 'Maltese', ga: 'Irish', cy: 'Welsh', eu: 'Basque', ca: 'Catalan',
gl: 'Galician', el: 'Greek', he: 'Hebrew', fa: 'Persian', ps: 'Pashto',
ku: 'Kurdish', az: 'Azerbaijani', kk: 'Kazakh', ky: 'Kyrgyz', uz: 'Uzbek',
tk: 'Turkmen', tg: 'Tajik', mn: 'Mongolian', ka: 'Georgian', hy: 'Armenian',
th: 'Thai', vi: 'Vietnamese', lo: 'Lao', km: 'Khmer', my: 'Myanmar',
si: 'Sinhala', ta: 'Tamil', te: 'Telugu', kn: 'Kannada', ml: 'Malayalam',
gu: 'Gujarati', pa: 'Punjabi', or: 'Odia', as: 'Assamese', ne: 'Nepali',
dz: 'Dzongkha', bo: 'Tibetan', id: 'Indonesian', ms: 'Malay', tl: 'Filipino',
ceb: 'Cebuano', haw: 'Hawaiian', mi: 'Maori', sm: 'Samoan', to: 'Tongan',
fj: 'Fijian', mg: 'Malagasy', sw: 'Swahili', zu: 'Zulu', xh: 'Xhosa',
af: 'Afrikaans', st: 'Southern Sotho', tn: 'Tswana', ss: 'Swati', ve: 'Venda',
ts: 'Tsonga', nr: 'Southern Ndebele', am: 'Amharic', ti: 'Tigrinya', om: 'Oromo',
so: 'Somali', ha: 'Hausa', yo: 'Yoruba', ig: 'Igbo', ff: 'Fulani',
wo: 'Wolof', bm: 'Bambara', rn: 'Rundi', rw: 'Rwanda', lg: 'Luganda',
ny: 'Chichewa', sn: 'Shona', nd: 'Northern Ndebele'
};
// Translation cache management
const CACHE_PREFIX = 'educational_analyzer_translations_';
const CACHE_VERSION = '1.0';
// Check if translations are cached for a language
function isLanguageCached(language) {
const cacheKey = CACHE_PREFIX + language;
const cached = localStorage.getItem(cacheKey);
return cached !== null;
}
// Save translations to cache
function saveTranslationsToCache(language, translations) {
const cacheKey = CACHE_PREFIX + language;
const cacheData = {
version: CACHE_VERSION,
timestamp: Date.now(),
translations: translations
};
localStorage.setItem(cacheKey, JSON.stringify(cacheData));
console.log(`Translations cached for ${language}`);
}
// Load translations from cache
function loadTranslationsFromCache(language) {
const cacheKey = CACHE_PREFIX + language;
const cached = localStorage.getItem(cacheKey);
if (cached) {
try {
const cacheData = JSON.parse(cached);
if (cacheData.version === CACHE_VERSION) {
console.log(`Translations loaded from cache for ${language}`);
return cacheData.translations;
}
} catch (error) {
console.error('Error parsing cached translations:', error);
}
}
return null;
}
// Get all cached languages
function getCachedLanguages() {
const cachedLanguages = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith(CACHE_PREFIX)) {
const language = key.replace(CACHE_PREFIX, '');
cachedLanguages.push(language);
}
}
return cachedLanguages;
}
// Clear all translation cache
function clearAllTranslationCache() {
const cachedLanguages = getCachedLanguages();
if (cachedLanguages.length === 0) {
alert('No cached translations to clear.');
return;
}
const languageList = cachedLanguages.map(lang => languageNames[lang] || lang).join(', ');
const confirmMessage = `Are you sure you want to clear all cached translations?\n\nCached languages: ${languageList}\n\nThis will require re-downloading translations when switching languages.`;
if (confirm(confirmMessage)) {
// Clear all translation caches
cachedLanguages.forEach(language => {
const cacheKey = CACHE_PREFIX + language;
localStorage.removeItem(cacheKey);
});
// Update cache status
updateCacheStatus(currentLanguage);
updateCacheStatusDisplay();
alert(`Cache cleared successfully!\n\n${cachedLanguages.length} language(s) removed from cache.`);
// Ask if user wants to reload current language translations
if (currentLanguage !== 'en' && cachedLanguages.includes(currentLanguage)) {
if (confirm('Would you like to reload the current language translations?')) {
applyLanguage(currentLanguage);
}
}
}
}
// Update cache status indicator
function updateCacheStatus(language) {
const cacheStatus = document.getElementById('cacheStatus');
const isCached = isLanguageCached(language);
if (language === 'en') {
cacheStatus.classList.remove('cached', 'translating');
return;
}
if (isCached) {
cacheStatus.textContent = '💾 Translations cached - instant loading!';
cacheStatus.classList.add('cached');
cacheStatus.classList.remove('translating');
} else {
cacheStatus.textContent = '🔄 First time translation - will be cached for future use';
cacheStatus.classList.add('translating');
cacheStatus.classList.remove('cached');
}
}
// Update cache status display in management section
function updateCacheStatusDisplay() {
const cacheStatusDisplay = document.getElementById('cacheStatusDisplay');
const clearCacheBtn = document.getElementById('clearCacheBtn');
const cachedLanguages = getCachedLanguages();
if (cachedLanguages.length === 0) {
cacheStatusDisplay.textContent = '📭 No cached translations';
cacheStatusDisplay.className = 'cache-status-display no-cache';
clearCacheBtn.disabled = true;
} else {
const languageList = cachedLanguages.map(lang => languageNames[lang] || lang).join(', ');
cacheStatusDisplay.textContent = `💾 ${cachedLanguages.length} language(s) cached: ${languageList}`;
cacheStatusDisplay.className = 'cache-status-display';
clearCacheBtn.disabled = false;
}
}
// Initialize the application
function initializeApp() {
// Load saved language and API key
const savedLanguage = localStorage.getItem('educational_analyzer_language') || 'en';
const savedApiKey = localStorage.getItem('educational_analyzer_api_key') || '';
currentLanguage = savedLanguage;
currentApiKey = savedApiKey;
// Set language selector
document.getElementById('languageSelect').value = currentLanguage;
document.getElementById('apiKeyLanding').value = currentApiKey;
// Apply direction for current language
applyDirection(currentLanguage);
// Update cache status
updateCacheStatus(currentLanguage);
updateCacheStatusDisplay();
// Show appropriate screen
if (currentApiKey && currentLanguage) {
showMainApp();
} else {
showLanguageLanding();
}
}
// Apply language direction
function applyDirection(language) {
currentLanguage = language;
// Set document language and direction
document.documentElement.lang = language;
document.documentElement.dir = rtlLanguages.includes(language) ? 'rtl' : 'ltr';
// Save language preference
localStorage.setItem('educational_analyzer_language', language);
// Update cache status
updateCacheStatus(language);
}
// API call function for translation
async function translateText(text, targetLanguage) {
if (!currentApiKey) {
throw new Error('API key is required for translation');
}
const languageName = languageNames[targetLanguage] || 'English';
const prompt = `Translate the following text to ${languageName}. Provide ONLY the exact translation without any explanations, additional information, or formatting:
"${text}"`;
const payload = {
model: "gpt-4o-mini",
messages: [{ role: "user", content: prompt }],
max_tokens: 500,
temperature: 0.1
};
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${currentApiKey}`
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || "Translation API request failed");
}
const data = await response.json();
return data.choices[0].message.content.trim();
}
// Apply cached translations to UI
function applyCachedTranslations(translations) {
// Apply text translations
Object.keys(translations.texts).forEach(originalText => {
const translation = translations.texts[originalText];
const elements = document.querySelectorAll(`[data-translate]`);
elements.forEach(element => {
const originalElementText = element.getAttribute('data-original-text') || element.textContent;
if (originalElementText === originalText) {
element.textContent = translation;
}
});
});
// Apply placeholder translations
Object.keys(translations.placeholders).forEach(originalPlaceholder => {
const translation = translations.placeholders[originalPlaceholder];
const elements = document.querySelectorAll(`[data-translate-placeholder]`);
elements.forEach(element => {
const originalElementPlaceholder = element.getAttribute('data-original-placeholder') || element.placeholder;
if (originalElementPlaceholder === originalPlaceholder) {
element.placeholder = translation;
}
});
});
}
// Translate all UI elements
async function translateInterface(targetLanguage) {
if (targetLanguage === 'en') {
// No translation needed for English
return;
}
// Check if translations are cached
const cachedTranslations = loadTranslationsFromCache(targetLanguage);
if (cachedTranslations) {
// Use cached translations
console.log('Using cached translations for', targetLanguage);
applyCachedTranslations(cachedTranslations);
return;
}
// Need to translate via API
showTranslationOverlay();
try {
// Get all elements with data-translate attribute
const elements = document.querySelectorAll('[data-translate]');
const placeholderElements = document.querySelectorAll('[data-translate-placeholder]');
// Collect all texts to translate
const textsToTranslate = [];
const placeholdersToTranslate = [];
const elementMap = new Map();
elements.forEach(element => {
const originalText = element.getAttribute('data-original-text') || element.textContent;
if (!element.getAttribute('data-original-text')) {
element.setAttribute('data-original-text', originalText);
}
textsToTranslate.push(originalText);
elementMap.set(originalText, element);
});
placeholderElements.forEach(element => {
const originalPlaceholder = element.getAttribute('data-original-placeholder') || element.placeholder;
if (!element.getAttribute('data-original-placeholder')) {
element.setAttribute('data-original-placeholder', originalPlaceholder);
}
placeholdersToTranslate.push(originalPlaceholder);
elementMap.set(originalPlaceholder, element);
});
// Prepare cache structure
const translationsCache = {
texts: {},
placeholders: {}
};
// Translate texts in batches
const batchSize = 10;
const allTexts = [...textsToTranslate, ...placeholdersToTranslate];
for (let i = 0; i < allTexts.length; i += batchSize) {
const batch = allTexts.slice(i, i + batchSize);
// Update progress
updateTranslationProgress(i, allTexts.length);
// Translate batch
const translations = await Promise.all(
batch.map(text => translateText(text, targetLanguage))
);
// Apply translations and cache them
batch.forEach((originalText, index) => {
const element = elementMap.get(originalText);
const translation = translations[index];
if (element.hasAttribute('data-translate')) {
element.textContent = translation;
translationsCache.texts[originalText] = translation;
} else if (element.hasAttribute('data-translate-placeholder')) {
element.placeholder = translation;
translationsCache.placeholders[originalText] = translation;
}
});
}
// Save translations to cache
saveTranslationsToCache(targetLanguage, translationsCache);
// Update cache status
updateCacheStatus(targetLanguage);
updateCacheStatusDisplay();
} catch (error) {
console.error('Translation error:', error);
showError('Translation failed: ' + error.message);
} finally {
hideTranslationOverlay();
}
}
// Show translation overlay
function showTranslationOverlay() {
document.getElementById('translationOverlay').style.display = 'flex';
}
// Hide translation overlay
function hideTranslationOverlay() {
document.getElementById('translationOverlay').style.display = 'none';
}
// Update translation progress
function updateTranslationProgress(current, total) {
const percentage = Math.round((current / total) * 100);
document.getElementById('translationMessage').textContent =
`Translating interface... ${percentage}% complete (will be cached for future use)`;
}
// Apply language with API translation or cache
async function applyLanguage(language) {
applyDirection(language);
if (language !== 'en') {
await translateInterface(language);
}
}
// Show language landing page
function showLanguageLanding() {
document.getElementById('languageLanding').style.display = 'block';
document.getElementById('mainApp').style.display = 'none';
}
// Show main application
function showMainApp() {
document.getElementById('languageLanding').style.display = 'none';
document.getElementById('mainApp').style.display = 'block';
// Set API key in main app
document.getElementById('apiKey').value = currentApiKey;
}
// Start button click handler
document.getElementById('startBtn').addEventListener('click', async function() {
const selectedLanguage = document.getElementById('languageSelect').value;
const apiKey = document.getElementById('apiKeyLanding').value.trim();
if (!apiKey) {
alert('Please enter your OpenAI API key');
return;
}
currentLanguage = selectedLanguage;
currentApiKey = apiKey;
// Save API key
localStorage.setItem('educational_analyzer_api_key', apiKey);
// Apply language with translation (cached or API)
await applyLanguage(selectedLanguage);
// Show main app
showMainApp();
});
// Language selector change handler
document.getElementById('languageSelect').addEventListener('change', async function() {
const selectedLanguage = this.value;
updateCacheStatus(selectedLanguage);
if (currentApiKey) {
await applyLanguage(selectedLanguage);
} else {
applyDirection(selectedLanguage);
}
});
// Clear cache button handler
document.getElementById('clearCacheBtn').addEventListener('click', clearAllTranslationCache);
// API key sync between landing and main app
document.getElementById('apiKeyLanding').addEventListener('input', function() {
currentApiKey = this.value;
localStorage.setItem('educational_analyzer_api_key', this.value);
document.getElementById('apiKey').value = this.value;
});
document.getElementById('apiKey').addEventListener('input', function() {
currentApiKey = this.value;
localStorage.setItem('educational_analyzer_api_key', this.value);
document.getElementById('apiKeyLanding').value = this.value;
});
// App Configuration
const AppConfig = {
API_BASE_URL: 'https://api.openai.com/v1/chat/completions',
MODEL: 'gpt-4o-mini',
MAX_TOKENS: 2000,
TEMPERATURE: 0.7
};
// App State Management
const AppState = {
apiKey: '',
isAnalyzing: false,
currentActivity: '',
setApiKey(key) {
this.apiKey = key;
this.saveToLocalStorage();
},
setAnalyzing(analyzing) {
this.isAnalyzing = analyzing;
UIController.updateAnalyzingState(analyzing);
},
saveToLocalStorage() {
try {
localStorage.setItem('educational_analyzer_api_key', this.apiKey);
} catch (e) {
console.warn('Could not save to localStorage:', e);
}
},
loadFromLocalStorage() {
try {
this.apiKey = localStorage.getItem('educational_analyzer_api_key') || '';
} catch (e) {
console.warn('Could not load from localStorage:', e);
}
}
};
// Agent Prompts - Dynamic functions to use current selected language
const agentPrompts = {
agent1: function(activity) {
const languageName = languageNames[currentLanguage] || 'English';
return `
You are Agent 1, an educational technology expert. Your task is to conduct a DEEP, STRUCTURED ANALYSIS of the teaching activity described below using the PICRAT model (PIC + RAT) and Bloom's Taxonomy, with a strong emphasis on how effectively the activity fosters critical thinking.
IMPORTANT: Write your entire response in ${languageName} language.
Activity Description:
${activity}
OUTPUT FORMAT (use these exact headings in your final response):
PICRAT Dimensions
a. Passive (P)
Provide a detailed analysis of the passive element, including examples and implications for student engagement.
b. Interactive (I)
Provide a detailed analysis of the interactive element, including examples and implications for student engagement.
c. Creative (C)
Provide a detailed analysis of the creative element, including examples and implications for student engagement.
d. Replacement (R)
Examine the replacement element thoroughly, highlighting how technology is used to substitute traditional methods.
e. Amplification (A)
Examine the amplification element thoroughly, highlighting how technology is used to enhance traditional methods.
f. Transformation (T)
Examine the transformation element thoroughly, highlighting how technology is used to transform learning experiences.
Bloom's Taxonomy
Identify and explain the cognitive level(s) addressed (Remembering, Understanding, Applying, Analyzing, Evaluating, Creating) with specific references to the activity.
Relationship Between PICRAT & Bloom's
Analyze how the chosen PICRAT categories align with or support the identified Bloom's level(s), illustrating their interconnectivity.
Critical Thinking
Assess to what extent this activity fosters critical thinking, providing examples of how students are encouraged to engage in higher-order thinking.
Overall Comments/Recommendations
Summarize key strengths of the activity and suggest actionable improvements to enhance its effectiveness.
`.trim();
},
agent2: function(agent1Result) {
const languageName = languageNames[currentLanguage] || 'English';
return `
You are Agent 2, a scenario developer. Based on the analysis from Agent 1, craft practical scenarios or applications that illustrate how the teaching activity can be improved or enhanced. Use Agent 1's insights verbatim as your foundation.
IMPORTANT: Write your entire response in ${languageName} language.
Agent 1's Analysis:
${agent1Result}
Instructions:
1. Provide realistic classroom or online teaching scenarios.
2. Address technology integration, engagement strategies, and cognitive depth.
3. Include any potential pitfalls or variations for different subjects or grade levels.
Output only your final scenarios.
`.trim();
},
agent3: function(agent1Result, agent2Result) {
const languageName = languageNames[currentLanguage] || 'English';
return `
You are Agent 3, a meta-reviewer. You have access to the outputs from Agent 1 and Agent 2. Provide overarching feedback and a high-level overview that ties both analyses and scenarios together.
IMPORTANT: Write your entire response in ${languageName} language.
Agent 1's Analysis:
${agent1Result}
Agent 2's Scenarios:
${agent2Result}
Instructions:
1. Summarize the key insights from Agents 1 and 2.
2. Discuss overall alignment with best practices in technology integration and higher-order thinking.
3. Suggest any final recommendations or overarching considerations (e.g., accessibility, long-term professional growth).
Output only your final feedback and overview.
`.trim();
}
};
// API Service Module
const APIService = {
async callGPT(prompt, apiKey) {
const payload = {
model: AppConfig.MODEL,
messages: [{ role: 'user', content: prompt }],
max_tokens: AppConfig.MAX_TOKENS,
temperature: AppConfig.TEMPERATURE
};
const response = await fetch(AppConfig.API_BASE_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return data.choices?.[0]?.message?.content || 'No output received.';
}
};
// Simulation Service for Demo Mode
const SimulationService = {
getSimulatedResponse(agentName, placeholders) {
switch (agentName) {
case "agent1":
return `
1. PICRAT Dimensions
a. PIC Classification (Interactive)
b. RAT Classification (Amplification)
2. Bloom's Taxonomy
- Applying, Analyzing
3. Relationship Between PICRAT & Bloom's
- Interactive use of tech supports mid-level Bloom's tasks
4. Critical Thinking
- Moderate level; students engage with real-world data but do not create or evaluate complex outputs
5. Overall Comments/Recommendations
- Strength: Encourages active student participation
- Recommendation: Incorporate a peer-evaluation component to push into higher Bloom's levels
`.trim();
case "agent2":
return `
[SIMULATED Agent 2 Scenarios]
1) Students collaborate in groups to apply the analyzed data in a new project, amplifying the original activity.
2) Introduce a mini-debate or panel discussion, using technology for real-time data visualization and deeper analysis.
`.trim();
case "agent3":
return `
[SIMULATED Agent 3 Feedback & Overview]
Overall alignment with best practices: Encourages interactive use of tech and mid-level Bloom's.
Recommendation: Integrate a reflection stage to foster higher-order thinking and transform the activity into a more creative endeavor.
`.trim();
default:
return "[No simulation available]";
}
}
};
// UI Controller Module
const UIController = {
elements: {},
init() {
this.elements = {
apiKeyInput: document.getElementById('apiKey'),
analysisForm: document.getElementById('analysisForm'),
activityDescription: document.getElementById('activityDescription'),
analyzeButton: document.querySelector('.analyze-button'),
loadingSpinner: document.getElementById('loadingSpinner'),
buttonText: document.querySelector('.button-text'),
agent1Card: document.getElementById('agent1Card'),
agent2Card: document.getElementById('agent2Card'),
agent3Card: document.getElementById('agent3Card'),
analysisOutput1: document.getElementById('analysisOutput1'),
analysisOutput2: document.getElementById('analysisOutput2'),
analysisOutput3: document.getElementById('analysisOutput3')
};
this.setupEventListeners();
},
setupEventListeners() {
this.elements.apiKeyInput.addEventListener('input', (e) => {
AppState.setApiKey(e.target.value.trim());
});
this.elements.analysisForm.addEventListener('submit', (e) => {
e.preventDefault();
App.analyzeActivity();
});
},
updateAnalyzingState(isAnalyzing) {
if (isAnalyzing) {
this.elements.analyzeButton.disabled = true;
this.elements.buttonText.style.opacity = '0';
this.elements.loadingSpinner.style.display = 'block';
} else {
this.elements.analyzeButton.disabled = false;
this.elements.buttonText.style.opacity = '1';
this.elements.loadingSpinner.style.display = 'none';
}
},
setAgentLoading(agentNumber, isLoading) {
const card = this.elements[`agent${agentNumber}Card`];
if (isLoading) {
card.classList.add('loading');
this.elements[`analysisOutput${agentNumber}`].textContent = '';
} else {
card.classList.remove('loading');
}
},
setAgentContent(agentNumber, content) {
this.elements[`analysisOutput${agentNumber}`].textContent = content;
},
clearAllOutputs() {
for (let i = 1; i <= 3; i++) {
this.elements[`analysisOutput${i}`].textContent = '';
this.elements[`agent${i}Card`].classList.remove('loading');
}
},
showError(message) {
const errorHtml = `
<div class="error-message">
<span class="error-icon">⚠️</span>
<span class="error-text">${message}</span>
<button class="error-dismiss" onclick="this.parentElement.remove()">×</button>
</div>
`;
const inputSection = document.querySelector('.input-section');
if (inputSection) {
inputSection.insertAdjacentHTML('afterbegin', errorHtml);
}
}
};
// Error Handler Module
const ErrorHandler = {
handleError(error, agentNumber = null) {
console.error('Application error:', error);
let userMessage = 'An unexpected error occurred. Please try again.';
if (error.message.includes('API key')) {
userMessage = 'Invalid API key. Please check your OpenAI API key and try again.';
} else if (error.message.includes('network') || error.message.includes('fetch')) {
userMessage = 'Network error. Please check your internet connection and try again.';
} else if (error.message.includes('rate limit')) {
userMessage = 'Rate limit exceeded. Please wait a moment before trying again.';
} else if (error.message.includes('quota')) {
userMessage = 'API quota exceeded. Please check your OpenAI account usage.';
}
if (agentNumber) {
UIController.setAgentContent(agentNumber, `Error: ${userMessage}`);
} else {
UIController.showError(userMessage);
}
}
};
// Input Validator Module
const InputValidator = {
validateActivity(activity) {
if (!activity || activity.trim().length < 10) {
throw new Error('Please provide a more detailed description of your teaching activity (at least 10 characters).');
}
return true;
}
};
// Main App Module
const App = {
init() {
AppState.loadFromLocalStorage();
UIController.init();
// Load saved API key
if (AppState.apiKey) {
UIController.elements.apiKeyInput.value = AppState.apiKey;
}
console.log('Educational Activity Intelligence Analyzer initialized');
},
async analyzeActivity() {
if (AppState.isAnalyzing) return;
try {
const activity = UIController.elements.activityDescription.value.trim();
// Validate activity description
InputValidator.validateActivity(activity);
// Update state
AppState.currentActivity = activity;
AppState.setAnalyzing(true);
// Clear previous outputs
UIController.clearAllOutputs();
// Get API key
const apiKey = UIController.elements.apiKeyInput.value.trim();
// Update API key in state
if (apiKey) {
AppState.setApiKey(apiKey);
}
// Run agents sequentially
await this.runAgent1(activity, apiKey);
await this.runAgent2(apiKey);
await this.runAgent3(apiKey);
} catch (error) {
ErrorHandler.handleError(error);
} finally {
AppState.setAnalyzing(false);
}
},
async runAgent1(activity, apiKey) {
try {
UIController.setAgentLoading(1, true);
const prompt = agentPrompts.agent1(activity);
let result;
if (!apiKey) {
// Simulate delay for demo
await new Promise(resolve => setTimeout(resolve, 1500));
result = SimulationService.getSimulatedResponse('agent1', { ACTIVITY: activity });
} else {
result = await APIService.callGPT(prompt, apiKey);
}
AppState.agent1Result = result;
UIController.setAgentContent(1, result);
} catch (error) {
ErrorHandler.handleError(error, 1);
} finally {
UIController.setAgentLoading(1, false);
}
},
async runAgent2(apiKey) {
try {
UIController.setAgentLoading(2, true);
const prompt = agentPrompts.agent2(AppState.agent1Result || '');
let result;
if (!apiKey) {
// Simulate delay for demo
await new Promise(resolve => setTimeout(resolve, 1200));
result = SimulationService.getSimulatedResponse('agent2', { AGENT1_RESULT: AppState.agent1Result });
} else {
result = await APIService.callGPT(prompt, apiKey);
}
AppState.agent2Result = result;
UIController.setAgentContent(2, result);
} catch (error) {
ErrorHandler.handleError(error, 2);
} finally {
UIController.setAgentLoading(2, false);
}
},
async runAgent3(apiKey) {
try {
UIController.setAgentLoading(3, true);
const prompt = agentPrompts.agent3(AppState.agent1Result || '', AppState.agent2Result || '');
let result;
if (!apiKey) {
// Simulate delay for demo
await new Promise(resolve => setTimeout(resolve, 1000));
result = SimulationService.getSimulatedResponse('agent3', {
AGENT1_RESULT: AppState.agent1Result,
AGENT2_RESULT: AppState.agent2Result
});
} else {
result = await APIService.callGPT(prompt, apiKey);
}
UIController.setAgentContent(3, result);
} catch (error) {
ErrorHandler.handleError(error, 3);
} finally {
UIController.setAgentLoading(3, false);
}
}
};
// Initialize app when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
initializeApp();
App.init();
});
</script>
</body>
</html>