Code-Explainer / index.html
Hma47's picture
Upload 4 files
a0cfa52 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CodeTutor Pro - Intelligent Code Explanation Platform</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
:root {
--primary: #7c3aed;
--secondary: #8b5cf6;
--accent: #c4b5fd;
--background: #f8fafc;
--surface: #ffffff;
--text: #1e293b;
--text-secondary: #64748b;
--border: #e2e8f0;
--success: #10b981;
--warning: #f59e0b;
--error: #ef4444;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
color: var(--text);
line-height: 1.6;
min-height: 100vh;
padding: 15px;
direction: ltr;
text-align: left;
}
/* RTL Support - Comprehensive */
body.rtl {
direction: rtl;
text-align: right;
}
body.rtl .form-grid {
direction: rtl;
}
body.rtl .header {
direction: rtl;
}
body.rtl .features-grid {
direction: rtl;
}
body.rtl .settings-bar {
direction: rtl;
}
body.rtl .language-info {
flex-direction: row-reverse;
}
body.rtl .form-group {
text-align: right;
}
body.rtl .landing-form {
text-align: right;
}
body.rtl .landing-form h2 {
text-align: center;
}
body.rtl .api-key-section {
right: 15px;
left: auto;
}
body.rtl .char-counter {
text-align: left;
}
body.rtl .form-section h3 {
flex-direction: row-reverse;
}
body.rtl .output-section h2 {
flex-direction: row-reverse;
}
body.rtl .generate-btn {
direction: rtl;
}
body.rtl .spinner {
margin-right: 0;
margin-left: 8px;
}
body.rtl .feature-card .icon {
direction: ltr; /* Keep emojis in LTR */
}
body.rtl .edit-notice {
direction: rtl;
}
body.rtl .footer {
direction: rtl;
}
/* Ensure code areas remain LTR for readability */
body.rtl #codeInput,
body.rtl .output-area,
body.rtl pre,
body.rtl code {
direction: ltr;
text-align: left;
}
/* API key inputs should remain LTR */
body.rtl #apiKey,
body.rtl #landingApiKey {
direction: ltr;
text-align: left;
}
/* RTL List Formatting - Fix numbered lists positioning */
body.rtl ol {
padding-right: 20px;
padding-left: 0;
margin-right: 0;
margin-left: 0;
}
body.rtl ul {
padding-right: 20px;
padding-left: 0;
margin-right: 0;
margin-left: 0;
}
body.rtl li {
text-align: right;
direction: rtl;
margin-right: 0;
margin-left: 0;
padding-right: 0;
padding-left: 0;
}
/* Fix numbered list markers positioning in RTL */
body.rtl ol li {
list-style-position: inside;
text-align: right;
}
body.rtl ul li {
list-style-position: inside;
text-align: right;
}
/* Ensure proper spacing for RTL lists */
body.rtl .output-area ol,
body.rtl .output-area ul {
margin: 10px 0;
padding-right: 25px;
padding-left: 0;
}
body.rtl .output-area li {
margin-bottom: 5px;
text-align: right;
direction: rtl;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: var(--surface);
border-radius: 16px;
box-shadow: var(--shadow-lg);
overflow: hidden;
position: relative;
}
/* Landing Page Styles */
.landing-page {
display: block;
padding: 40px;
text-align: center;
min-height: 80vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.landing-page.hidden {
display: none;
}
.landing-header {
margin-bottom: 40px;
}
.landing-header h1 {
font-size: 3rem;
font-weight: 800;
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 16px;
}
.landing-header .subtitle {
font-size: 1.3rem;
color: var(--text-secondary);
font-weight: 500;
margin-bottom: 12px;
}
.landing-header .description {
font-size: 1.1rem;
color: var(--text-secondary);
max-width: 600px;
margin: 0 auto;
}
.landing-form {
background: var(--surface);
border: 2px solid var(--border);
border-radius: 16px;
padding: 40px;
max-width: 500px;
width: 100%;
box-shadow: var(--shadow-lg);
}
.landing-form h2 {
color: var(--primary);
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 30px;
text-align: center;
}
.form-group {
margin-bottom: 25px;
text-align: left;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: var(--text);
font-size: 1rem;
}
.form-group input,
.form-group select {
width: 100%;
padding: 14px 16px;
border: 2px solid var(--border);
border-radius: 8px;
font-size: 1rem;
font-family: inherit;
background: var(--surface);
transition: all 0.2s ease;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
}
.start-btn {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
padding: 16px 32px;
border: none;
border-radius: 12px;
font-size: 1.2rem;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
width: 100%;
margin-top: 10px;
}
.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;
}
/* Main App Styles */
.main-app {
display: none;
}
.main-app.active {
display: block;
}
/* Language and Settings Bar */
.settings-bar {
background: rgba(124, 58, 237, 0.05);
border-bottom: 1px solid var(--border);
padding: 15px 30px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.language-info {
display: flex;
align-items: center;
gap: 15px;
font-size: 0.9rem;
color: var(--text-secondary);
}
.change-language-btn,
.clear-cache-btn {
background: var(--primary);
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
font-size: 0.85rem;
cursor: pointer;
transition: all 0.2s ease;
}
.change-language-btn:hover,
.clear-cache-btn:hover {
background: var(--secondary);
transform: translateY(-1px);
}
.clear-cache-btn {
background: var(--warning);
}
.clear-cache-btn:hover {
background: #d97706;
}
/* API Key Section - Static Position Top Left */
.api-key-section {
background: rgba(124, 58, 237, 0.05);
border: 1px solid rgba(124, 58, 237, 0.1);
border-radius: 8px;
padding: 12px;
margin: 15px;
max-width: 280px;
}
.api-key-section label {
font-size: 12px;
font-weight: 600;
color: var(--primary);
margin-bottom: 6px;
display: block;
}
.api-key-section input {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--border);
border-radius: 6px;
font-size: 14px;
background: var(--surface);
transition: all 0.2s ease;
font-family: 'Courier New', monospace;
}
.api-key-section input:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
}
/* Main Content */
.main-content {
padding: 20px 30px 30px;
}
/* Header */
.header {
text-align: center;
margin-bottom: 25px;
padding-bottom: 20px;
border-bottom: 2px solid var(--border);
}
.header h1 {
font-size: 2.5rem;
font-weight: 800;
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 8px;
}
.header .subtitle {
font-size: 1.1rem;
color: var(--text-secondary);
font-weight: 500;
}
.header .description {
font-size: 1rem;
color: var(--text-secondary);
margin-top: 12px;
max-width: 700px;
margin-left: auto;
margin-right: auto;
}
/* Code Explanation Features Overview */
.features-overview {
background: rgba(124, 58, 237, 0.05);
border: 1px solid rgba(124, 58, 237, 0.1);
border-radius: 12px;
padding: 20px;
margin-bottom: 25px;
}
.features-overview h3 {
color: var(--primary);
font-size: 1.2rem;
font-weight: 700;
margin-bottom: 15px;
text-align: center;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.feature-card {
background: rgba(124, 58, 237, 0.02);
border: 1px solid rgba(124, 58, 237, 0.1);
border-radius: 8px;
padding: 15px;
text-align: center;
transition: all 0.3s ease;
}
.feature-card:hover {
background: rgba(124, 58, 237, 0.05);
border-color: var(--primary);
transform: translateY(-2px);
}
.feature-card .icon {
font-size: 1.5rem;
margin-bottom: 8px;
}
.feature-card h4 {
color: var(--primary);
font-size: 0.9rem;
font-weight: 700;
margin-bottom: 6px;
}
.feature-card p {
font-size: 0.8rem;
color: var(--text-secondary);
line-height: 1.4;
}
/* Progress Bar */
.progress-container {
background: var(--border);
height: 6px;
border-radius: 3px;
margin: 15px 0;
overflow: hidden;
}
.progress-bar {
width: 0%;
height: 100%;
background: linear-gradient(90deg, var(--primary) 0%, var(--secondary) 100%);
transition: width 0.3s ease;
border-radius: 3px;
}
/* Form Section */
.form-section {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 25px;
margin-bottom: 25px;
transition: all 0.3s ease;
box-shadow: var(--shadow);
}
.form-section:hover {
border-color: var(--primary);
box-shadow: 0 8px 25px -8px rgba(124, 58, 237, 0.3);
}
.form-section h3 {
font-size: 1.2rem;
font-weight: 700;
color: var(--primary);
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 8px;
}
/* Form Elements */
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: var(--text);
font-size: 0.95rem;
}
input[type="text"],
textarea,
select {
width: 100%;
padding: 12px 16px;
border: 2px solid var(--border);
border-radius: 8px;
font-size: 1rem;
font-family: inherit;
background: var(--surface);
transition: all 0.2s ease;
resize: vertical;
}
input[type="text"]:focus,
textarea:focus,
select:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
}
textarea {
min-height: 200px;
line-height: 1.6;
font-family: 'Fira Code', 'Courier New', monospace;
}
.char-counter {
font-size: 0.8rem;
color: var(--text-secondary);
text-align: right;
margin-top: 4px;
}
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
margin-top: 15px;
}
.form-field {
display: flex;
flex-direction: column;
}
/* Generate Button */
.generate-btn {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
padding: 18px 40px;
border: none;
border-radius: 12px;
font-size: 1.2rem;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
display: block;
margin: 30px auto;
min-width: 320px;
position: relative;
overflow: hidden;
}
.generate-btn:hover {
transform: translateY(-2px);
box-shadow: 0 12px 24px -8px rgba(124, 58, 237, 0.4);
}
.generate-btn:active {
transform: translateY(0);
}
.generate-btn:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
/* Loading Spinner */
.spinner {
display: none;
width: 20px;
height: 20px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s ease-in-out infinite;
margin-right: 8px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Output Section */
.output-section {
margin-top: 30px;
}
.output-section h2 {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary);
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 8px;
text-align: center;
justify-content: center;
}
.output-area {
background: var(--surface);
border: 2px solid var(--border);
border-radius: 12px;
padding: 25px;
min-height: 300px;
transition: all 0.3s ease;
position: relative;
overflow-x: auto;
font-family: 'Georgia', serif;
line-height: 1.8;
white-space: pre-wrap;
word-wrap: break-word;
}
.output-area:empty::before {
content: "Your detailed code explanation will appear here...";
color: var(--text-secondary);
font-style: italic;
font-family: 'Inter', sans-serif;
}
.output-area:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
}
.output-area h2, .output-area h3 {
color: var(--primary);
margin: 25px 0 15px 0;
font-family: 'Inter', sans-serif;
}
.output-area h2 {
border-bottom: 2px solid var(--primary);
padding-bottom: 8px;
font-size: 1.4rem;
}
.output-area h3 {
font-size: 1.2rem;
}
/* Section Headers with Decorative Underlines - Matching Example Format */
.output-area .section-header {
color: #e67e22; /* Orange color like in the example */
font-size: 1.3rem;
font-weight: 700;
margin: 30px 0 20px 0;
padding-bottom: 10px;
border-bottom: 3px solid #e67e22;
text-align: right; /* RTL alignment */
direction: rtl;
font-family: 'Inter', sans-serif;
}
/* For LTR languages */
body:not(.rtl) .output-area .section-header {
text-align: left;
direction: ltr;
}
/* Section content paragraphs */
.output-area .section-content {
margin: 15px 0 25px 0;
line-height: 1.8;
font-size: 1rem;
text-align: right;
direction: rtl;
}
/* For LTR languages */
body:not(.rtl) .output-area .section-content {
text-align: left;
direction: ltr;
}
/* Better paragraph spacing for the new format */
.output-area .section-content p {
margin: 12px 0;
line-height: 1.8;
}
.output-area p {
margin: 12px 0;
}
.output-area strong {
color: var(--primary);
}
.output-area ul, .output-area ol {
margin: 12px 0 12px 20px;
}
.output-area li {
margin: 6px 0;
}
.output-area code {
background: rgba(124, 58, 237, 0.1);
padding: 2px 6px;
border-radius: 4px;
font-family: 'Fira Code', 'Courier New', monospace;
color: var(--primary);
font-size: 0.9em;
}
.output-area pre {
background: rgba(124, 58, 237, 0.05);
border: 1px solid rgba(124, 58, 237, 0.2);
border-radius: 8px;
padding: 16px;
margin: 16px 0;
overflow-x: auto;
font-family: 'Fira Code', 'Courier New', monospace;
font-size: 0.9em;
line-height: 1.5;
}
.output-area pre code {
background: none;
padding: 0;
border-radius: 0;
color: var(--text);
font-size: inherit;
}
/* Edit Notice */
.edit-notice {
background: rgba(124, 58, 237, 0.1);
border: 1px solid rgba(124, 58, 237, 0.2);
color: var(--primary);
padding: 8px 12px;
border-radius: 6px;
margin-bottom: 10px;
font-size: 0.85rem;
text-align: center;
font-weight: 500;
}
/* Error Messages */
.error-message {
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.2);
color: var(--error);
padding: 12px 16px;
border-radius: 8px;
margin: 10px 0;
font-size: 0.9rem;
display: none;
}
/* Status Messages */
.status-message {
background: rgba(124, 58, 237, 0.1);
border: 1px solid rgba(124, 58, 237, 0.2);
color: var(--primary);
padding: 12px 16px;
border-radius: 8px;
margin: 10px 0;
font-size: 0.9rem;
text-align: center;
font-weight: 600;
}
/* Footer */
.footer {
text-align: center;
padding: 20px;
background: rgba(124, 58, 237, 0.05);
border-top: 1px solid var(--border);
color: var(--text-secondary);
font-size: 0.9rem;
}
/* Responsive Design */
@media (max-width: 768px) {
body {
padding: 10px;
}
.landing-page {
padding: 20px;
}
.landing-header h1 {
font-size: 2.2rem;
}
.landing-form {
padding: 30px 20px;
}
.settings-bar {
padding: 12px 20px;
flex-direction: column;
align-items: stretch;
}
.language-info {
justify-content: center;
margin-bottom: 10px;
}
.api-key-section {
margin: 10px;
max-width: none;
}
.main-content {
padding: 15px 20px 20px;
}
.header h1 {
font-size: 2rem;
}
.form-section {
padding: 20px 15px;
}
.features-grid {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.form-grid {
grid-template-columns: 1fr;
}
.generate-btn {
width: 100%;
margin: 25px 0;
padding: 16px 32px;
font-size: 1.1rem;
}
.output-area {
padding: 20px;
}
}
@media (max-width: 480px) {
.landing-header h1 {
font-size: 1.8rem;
}
.header h1 {
font-size: 1.8rem;
}
.features-grid {
grid-template-columns: 1fr;
}
.feature-card {
padding: 12px;
}
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
:root {
--background: #0f172a;
--surface: #1e293b;
--text: #f1f5f9;
--text-secondary: #94a3b8;
--border: #334155;
}
body {
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
}
}
/* Translation loading indicator */
.translation-loading {
opacity: 0.6;
pointer-events: none;
}
.translation-loading::after {
content: "🔄";
position: absolute;
top: 10px;
right: 10px;
animation: spin 1s linear infinite;
}
</style>
</head>
<body>
<!-- Landing Page -->
<div class="landing-page" id="landingPage">
<div class="landing-header">
<h1 data-translate="app_title">CodeTutor Pro</h1>
<p class="subtitle" data-translate="app_subtitle">Intelligent Code Explanation Platform</p>
<p class="description" data-translate="app_description">Transform complex code into clear, beginner-friendly explanations with AI-powered analysis</p>
</div>
<div class="landing-form">
<h2 data-translate="setup_title">Get Started</h2>
<div class="form-group">
<label for="languageSelect" data-translate="language_label">Select Language:</label>
<select id="languageSelect" required>
<option value="">Choose your language...</option>
<!-- Languages will be populated by JavaScript -->
</select>
</div>
<div class="form-group">
<label for="landingApiKey" data-translate="api_key_label">OpenAI API Key:</label>
<input type="password" id="landingApiKey" data-translate-placeholder="api_key_placeholder" placeholder="Enter your OpenAI API key" autocomplete="off" required>
</div>
<button class="start-btn" id="startBtn" data-translate="start_button">Start Using CodeTutor Pro</button>
</div>
</div>
<!-- Main Application -->
<div class="main-app" id="mainApp">
<!-- Settings Bar -->
<div class="settings-bar">
<div class="language-info">
<span data-translate="current_language">Language:</span>
<strong id="currentLanguageName">English</strong>
<button class="change-language-btn" id="changeLanguageBtn" data-translate="change_language">Change Language</button>
</div>
<div>
<button class="clear-cache-btn" id="clearCacheBtn" data-translate="clear_cache">Clear Translation Cache</button>
</div>
</div>
<div class="container">
<!-- API Key Section - Static Position Top Left -->
<div class="api-key-section">
<label for="apiKey" data-translate="api_key_short">🔑 API Key</label>
<input type="password" id="apiKey" data-translate-placeholder="api_key_placeholder" placeholder="Enter your OpenAI API key" autocomplete="off">
</div>
<div class="main-content">
<!-- Header -->
<div class="header">
<h1 data-translate="app_title">CodeTutor Pro</h1>
<p class="subtitle" data-translate="app_subtitle">Intelligent Code Explanation Platform</p>
<p class="description" data-translate="app_description">Transform complex code into clear, beginner-friendly explanations with AI-powered analysis</p>
</div>
<!-- Code Explanation Features Overview -->
<div class="features-overview">
<h3 data-translate="features_title">💡 Advanced Code Explanation Features</h3>
<div class="features-grid">
<div class="feature-card">
<div class="icon">🎯</div>
<h4 data-translate="feature_beginner_title">Beginner-Friendly</h4>
<p data-translate="feature_beginner_desc">Plain language explanations without technical jargon</p>
</div>
<div class="feature-card">
<div class="icon">🔍</div>
<h4 data-translate="feature_analysis_title">Line-by-Line Analysis</h4>
<p data-translate="feature_analysis_desc">Detailed breakdown of each code component</p>
</div>
<div class="feature-card">
<div class="icon">📚</div>
<h4 data-translate="feature_concept_title">Concept Explanation</h4>
<p data-translate="feature_concept_desc">Clear examples and programming concepts</p>
</div>
<div class="feature-card">
<div class="icon">✏️</div>
<h4 data-translate="feature_editable_title">Editable Output</h4>
<p data-translate="feature_editable_desc">Customize and refine explanations as needed</p>
</div>
<div class="feature-card">
<div class="icon">🌐</div>
<h4 data-translate="feature_multilang_title">Multi-Language Support</h4>
<p data-translate="feature_multilang_desc">Works with all major programming languages</p>
</div>
</div>
</div>
<!-- Progress Bar -->
<div class="progress-container">
<div class="progress-bar" id="progressBar"></div>
</div>
<!-- Form Section -->
<div class="form-section">
<h3 data-translate="code_input_title">💻 Code Input & Analysis</h3>
<label for="codeInput" data-translate="code_input_label">Enter Your Code:</label>
<textarea
id="codeInput"
data-translate-placeholder="code_input_placeholder"
placeholder="Paste your code here... (supports all programming languages: Python, JavaScript, Java, C++, HTML, CSS, SQL, etc.)"
maxlength="1000000"
required
></textarea>
<div class="char-counter">
<span id="charCount">0</span>/1000000 <span data-translate="characters">characters</span>
</div>
<div class="form-grid">
<div class="form-field">
<label for="programmingLanguage" data-translate="programming_language_label">Programming Language (Optional):</label>
<select id="programmingLanguage">
<option value="" data-translate="auto_detect">Auto-detect language</option>
<option value="Python">Python</option>
<option value="JavaScript">JavaScript</option>
<option value="Java">Java</option>
<option value="C++">C++</option>
<option value="C#">C#</option>
<option value="HTML">HTML</option>
<option value="CSS">CSS</option>
<option value="SQL">SQL</option>
<option value="PHP">PHP</option>
<option value="Ruby">Ruby</option>
<option value="Go">Go</option>
<option value="Rust">Rust</option>
<option value="Swift">Swift</option>
<option value="Kotlin">Kotlin</option>
<option value="TypeScript">TypeScript</option>
<option value="R">R</option>
<option value="MATLAB">MATLAB</option>
<option value="Scala">Scala</option>
<option value="Perl">Perl</option>
<option value="Shell/Bash">Shell/Bash</option>
</select>
</div>
<div class="form-field">
<label for="explanationLevel" data-translate="explanation_level_label">Explanation Level:</label>
<select id="explanationLevel" required>
<option value="" data-translate="select_explanation_level">Select explanation level</option>
<option value="Complete Beginner" data-translate="level_complete_beginner">Complete Beginner (No programming experience)</option>
<option value="Beginner" data-translate="level_beginner">Beginner (Basic programming knowledge)</option>
<option value="Intermediate" data-translate="level_intermediate">Intermediate (Some programming experience)</option>
<option value="Advanced" data-translate="level_advanced">Advanced (Experienced programmer)</option>
</select>
</div>
</div>
</div>
<!-- Error Message -->
<div class="error-message" id="errorMessage"></div>
<!-- Status Message -->
<div class="status-message" id="statusMessage" style="display: none;"></div>
<!-- Generate Button -->
<button class="generate-btn" id="generateBtn">
<span class="spinner" id="spinner"></span>
<span id="buttonText" data-translate="generate_button">🧠 Generate Explanation</span>
</button>
<!-- Output Section -->
<div class="output-section">
<h2 data-translate="output_title">📖 Code Explanation</h2>
<div class="edit-notice" data-translate="edit_notice">
✏️ This explanation is editable - click anywhere to customize and refine the content
</div>
<div class="output-area" id="explanationOutput" contenteditable="true"></div>
</div>
</div>
<!-- Footer -->
<div class="footer" data-translate="footer">
Created by Shift Mind AI Labs
</div>
</div>
</div>
<script>
// Global variables
let currentLanguage = 'en';
let translationCache = {};
let isTranslating = false;
// 92 Languages with their codes and RTL information
const languages = [
{ code: 'en', name: 'English', nativeName: 'English', rtl: false },
{ code: 'es', name: 'Spanish', nativeName: 'Español', rtl: false },
{ code: 'fr', name: 'French', nativeName: 'Français', rtl: false },
{ code: 'de', name: 'German', nativeName: 'Deutsch', rtl: false },
{ code: 'it', name: 'Italian', nativeName: 'Italiano', rtl: false },
{ code: 'pt', name: 'Portuguese', nativeName: 'Português', rtl: false },
{ code: 'ru', name: 'Russian', nativeName: 'Русский', rtl: false },
{ code: 'ja', name: 'Japanese', nativeName: '日本語', rtl: false },
{ code: 'ko', name: 'Korean', nativeName: '한국어', rtl: false },
{ code: 'zh', name: 'Chinese (Simplified)', nativeName: '中文 (简体)', rtl: false },
{ code: 'zh-TW', name: 'Chinese (Traditional)', nativeName: '中文 (繁體)', rtl: false },
{ code: 'ar', name: 'Arabic', nativeName: 'العربية', rtl: true },
{ code: 'he', name: 'Hebrew', nativeName: 'עברית', rtl: true },
{ code: 'hi', name: 'Hindi', nativeName: 'हिन्दी', rtl: false },
{ code: 'th', name: 'Thai', nativeName: 'ไทย', rtl: false },
{ code: 'vi', name: 'Vietnamese', nativeName: 'Tiếng Việt', rtl: false },
{ code: 'tr', name: 'Turkish', nativeName: 'Türkçe', rtl: false },
{ code: 'pl', name: 'Polish', nativeName: 'Polski', rtl: false },
{ code: 'nl', name: 'Dutch', nativeName: 'Nederlands', rtl: false },
{ code: 'sv', name: 'Swedish', nativeName: 'Svenska', rtl: false },
{ code: 'da', name: 'Danish', nativeName: 'Dansk', rtl: false },
{ code: 'no', name: 'Norwegian', nativeName: 'Norsk', rtl: false },
{ code: 'fi', name: 'Finnish', nativeName: 'Suomi', rtl: false },
{ code: 'cs', name: 'Czech', nativeName: 'Čeština', rtl: false },
{ code: 'sk', name: 'Slovak', nativeName: 'Slovenčina', rtl: false },
{ code: 'hu', name: 'Hungarian', nativeName: 'Magyar', rtl: false },
{ code: 'ro', name: 'Romanian', nativeName: 'Română', rtl: false },
{ code: 'bg', name: 'Bulgarian', nativeName: 'Български', rtl: false },
{ code: 'hr', name: 'Croatian', nativeName: 'Hrvatski', rtl: false },
{ code: 'sr', name: 'Serbian', nativeName: 'Српски', rtl: false },
{ code: 'sl', name: 'Slovenian', nativeName: 'Slovenščina', rtl: false },
{ code: 'et', name: 'Estonian', nativeName: 'Eesti', rtl: false },
{ code: 'lv', name: 'Latvian', nativeName: 'Latviešu', rtl: false },
{ code: 'lt', name: 'Lithuanian', nativeName: 'Lietuvių', rtl: false },
{ code: 'uk', name: 'Ukrainian', nativeName: 'Українська', rtl: false },
{ code: 'be', name: 'Belarusian', nativeName: 'Беларуская', rtl: false },
{ code: 'mk', name: 'Macedonian', nativeName: 'Македонски', rtl: false },
{ code: 'sq', name: 'Albanian', nativeName: 'Shqip', rtl: false },
{ code: 'el', name: 'Greek', nativeName: 'Ελληνικά', rtl: false },
{ code: 'mt', name: 'Maltese', nativeName: 'Malti', rtl: false },
{ code: 'ga', name: 'Irish', nativeName: 'Gaeilge', rtl: false },
{ code: 'cy', name: 'Welsh', nativeName: 'Cymraeg', rtl: false },
{ code: 'is', name: 'Icelandic', nativeName: 'Íslenska', rtl: false },
{ code: 'fo', name: 'Faroese', nativeName: 'Føroyskt', rtl: false },
{ code: 'eu', name: 'Basque', nativeName: 'Euskera', rtl: false },
{ code: 'ca', name: 'Catalan', nativeName: 'Català', rtl: false },
{ code: 'gl', name: 'Galician', nativeName: 'Galego', rtl: false },
{ code: 'oc', name: 'Occitan', nativeName: 'Occitan', rtl: false },
{ code: 'br', name: 'Breton', nativeName: 'Brezhoneg', rtl: false },
{ code: 'co', name: 'Corsican', nativeName: 'Corsu', rtl: false },
{ code: 'sc', name: 'Sardinian', nativeName: 'Sardu', rtl: false },
{ code: 'rm', name: 'Romansh', nativeName: 'Rumantsch', rtl: false },
{ code: 'lb', name: 'Luxembourgish', nativeName: 'Lëtzebuergesch', rtl: false },
{ code: 'fy', name: 'Frisian', nativeName: 'Frysk', rtl: false },
{ code: 'af', name: 'Afrikaans', nativeName: 'Afrikaans', rtl: false },
{ code: 'zu', name: 'Zulu', nativeName: 'isiZulu', rtl: false },
{ code: 'xh', name: 'Xhosa', nativeName: 'isiXhosa', rtl: false },
{ code: 'st', name: 'Sesotho', nativeName: 'Sesotho', rtl: false },
{ code: 'tn', name: 'Setswana', nativeName: 'Setswana', rtl: false },
{ code: 'ss', name: 'Swati', nativeName: 'siSwati', rtl: false },
{ code: 've', name: 'Venda', nativeName: 'Tshivenḓa', rtl: false },
{ code: 'ts', name: 'Tsonga', nativeName: 'Xitsonga', rtl: false },
{ code: 'nr', name: 'Ndebele', nativeName: 'isiNdebele', rtl: false },
{ code: 'sw', name: 'Swahili', nativeName: 'Kiswahili', rtl: false },
{ code: 'am', name: 'Amharic', nativeName: 'አማርኛ', rtl: false },
{ code: 'ti', name: 'Tigrinya', nativeName: 'ትግርኛ', rtl: false },
{ code: 'om', name: 'Oromo', nativeName: 'Afaan Oromoo', rtl: false },
{ code: 'so', name: 'Somali', nativeName: 'Soomaali', rtl: false },
{ code: 'ha', name: 'Hausa', nativeName: 'Hausa', rtl: false },
{ code: 'yo', name: 'Yoruba', nativeName: 'Yorùbá', rtl: false },
{ code: 'ig', name: 'Igbo', nativeName: 'Igbo', rtl: false },
{ code: 'ff', name: 'Fulah', nativeName: 'Fulfulde', rtl: false },
{ code: 'wo', name: 'Wolof', nativeName: 'Wolof', rtl: false },
{ code: 'bm', name: 'Bambara', nativeName: 'Bamanankan', rtl: false },
{ code: 'rw', name: 'Kinyarwanda', nativeName: 'Ikinyarwanda', rtl: false },
{ code: 'rn', name: 'Kirundi', nativeName: 'Ikirundi', rtl: false },
{ code: 'ny', name: 'Chichewa', nativeName: 'Chichewa', rtl: false },
{ code: 'sn', name: 'Shona', nativeName: 'chiShona', rtl: false },
{ code: 'mg', name: 'Malagasy', nativeName: 'Malagasy', rtl: false },
{ code: 'fa', name: 'Persian', nativeName: 'فارسی', rtl: true },
{ code: 'ur', name: 'Urdu', nativeName: 'اردو', rtl: true },
{ code: 'ps', name: 'Pashto', nativeName: 'پښتو', rtl: true },
{ code: 'ku', name: 'Kurdish', nativeName: 'Kurdî', rtl: true },
{ code: 'ckb', name: 'Sorani Kurdish', nativeName: 'کوردی', rtl: true },
{ code: 'sd', name: 'Sindhi', nativeName: 'سنڌي', rtl: true },
{ code: 'ug', name: 'Uyghur', nativeName: 'ئۇيغۇرچە', rtl: true },
{ code: 'bn', name: 'Bengali', nativeName: 'বাংলা', rtl: false },
{ code: 'gu', name: 'Gujarati', nativeName: 'ગુજરાતી', rtl: false },
{ code: 'pa', name: 'Punjabi', nativeName: 'ਪੰਜਾਬੀ', rtl: false },
{ code: 'ta', name: 'Tamil', nativeName: 'தமிழ்', rtl: false },
{ code: 'te', name: 'Telugu', nativeName: 'తెలుగు', rtl: false },
{ code: 'kn', name: 'Kannada', nativeName: 'ಕನ್ನಡ', rtl: false },
{ code: 'ml', name: 'Malayalam', nativeName: 'മലയാളം', rtl: false },
{ code: 'si', name: 'Sinhala', nativeName: 'සිංහල', rtl: false },
{ code: 'my', name: 'Myanmar', nativeName: 'မြန်မာ', rtl: false },
{ code: 'km', name: 'Khmer', nativeName: 'ខ្មែរ', rtl: false },
{ code: 'lo', name: 'Lao', nativeName: 'ລາວ', rtl: false },
{ code: 'ka', name: 'Georgian', nativeName: 'ქართული', rtl: false },
{ code: 'hy', name: 'Armenian', nativeName: 'Հայերեն', rtl: false },
{ code: 'az', name: 'Azerbaijani', nativeName: 'Azərbaycan', rtl: false },
{ code: 'kk', name: 'Kazakh', nativeName: 'Қазақ', rtl: false },
{ code: 'ky', name: 'Kyrgyz', nativeName: 'Кыргыз', rtl: false },
{ code: 'uz', name: 'Uzbek', nativeName: 'Oʻzbek', rtl: false },
{ code: 'tk', name: 'Turkmen', nativeName: 'Türkmen', rtl: false },
{ code: 'tg', name: 'Tajik', nativeName: 'Тоҷикӣ', rtl: false },
{ code: 'mn', name: 'Mongolian', nativeName: 'Монгол', rtl: false }
];
// Default translations for English
const defaultTranslations = {
app_title: "CodeTutor Pro",
app_subtitle: "Intelligent Code Explanation Platform",
app_description: "Transform complex code into clear, beginner-friendly explanations with AI-powered analysis",
setup_title: "Get Started",
language_label: "Select Language:",
api_key_label: "OpenAI API Key:",
api_key_placeholder: "Enter your OpenAI API key",
start_button: "Start Using CodeTutor Pro",
current_language: "Language:",
change_language: "Change Language",
clear_cache: "Clear Translation Cache",
api_key_short: "🔑 API Key",
features_title: "💡 Advanced Code Explanation Features",
feature_beginner_title: "Beginner-Friendly",
feature_beginner_desc: "Plain language explanations without technical jargon",
feature_analysis_title: "Line-by-Line Analysis",
feature_analysis_desc: "Detailed breakdown of each code component",
feature_concept_title: "Concept Explanation",
feature_concept_desc: "Clear examples and programming concepts",
feature_editable_title: "Editable Output",
feature_editable_desc: "Customize and refine explanations as needed",
feature_multilang_title: "Multi-Language Support",
feature_multilang_desc: "Works with all major programming languages",
code_input_title: "💻 Code Input & Analysis",
code_input_label: "Enter Your Code:",
code_input_placeholder: "Paste your code here... (supports all programming languages: Python, JavaScript, Java, C++, HTML, CSS, SQL, etc.)",
characters: "characters",
programming_language_label: "Programming Language (Optional):",
auto_detect: "Auto-detect language",
explanation_level_label: "Explanation Level:",
select_explanation_level: "Select explanation level",
level_complete_beginner: "Complete Beginner (No programming experience)",
level_beginner: "Beginner (Basic programming knowledge)",
level_intermediate: "Intermediate (Some programming experience)",
level_advanced: "Advanced (Experienced programmer)",
generate_button: "🧠 Generate Explanation",
output_title: "📖 Code Explanation",
edit_notice: "✏️ This explanation is editable - click anywhere to customize and refine the content",
footer: "Created by Shift Mind AI Labs"
};
// Load cached translations and settings with improved cache handling
function loadSettings() {
const savedLanguage = localStorage.getItem('codetutor_language');
const savedCache = localStorage.getItem('codetutor_translation_cache');
const savedApiKey = localStorage.getItem('codetutor_api_key');
if (savedLanguage) {
currentLanguage = savedLanguage;
}
if (savedCache) {
try {
const parsedCache = JSON.parse(savedCache);
// Handle both old and new cache formats
translationCache = {};
Object.keys(parsedCache).forEach(key => {
const value = parsedCache[key];
if (typeof value === 'string') {
// Old format - convert to new format
translationCache[key] = {
text: value,
timestamp: Date.now() - (7 * 24 * 60 * 60 * 1000) // Mark as 7 days old
};
} else if (typeof value === 'object' && value.text) {
// New format
translationCache[key] = value;
}
});
} catch (e) {
console.error('Error loading translation cache:', e);
translationCache = {};
}
}
if (savedApiKey) {
document.getElementById('landingApiKey').value = savedApiKey;
document.getElementById('apiKey').value = savedApiKey;
}
}
// Save settings with cache size management
function saveSettings() {
localStorage.setItem('codetutor_language', currentLanguage);
// Limit cache size to prevent localStorage overflow
const cacheKeys = Object.keys(translationCache);
if (cacheKeys.length > 1000) {
// Keep only the 800 most recent entries
const sortedEntries = cacheKeys
.map(key => ({ key, timestamp: translationCache[key].timestamp || 0 }))
.sort((a, b) => b.timestamp - a.timestamp)
.slice(0, 800);
const newCache = {};
sortedEntries.forEach(entry => {
newCache[entry.key] = translationCache[entry.key];
});
translationCache = newCache;
}
localStorage.setItem('codetutor_translation_cache', JSON.stringify(translationCache));
}
// Populate language dropdown
function populateLanguageDropdown() {
const select = document.getElementById('languageSelect');
select.innerHTML = '<option value="">Choose your language...</option>';
languages.forEach(lang => {
const option = document.createElement('option');
option.value = lang.code;
option.textContent = `${lang.nativeName} (${lang.name})`;
if (lang.code === currentLanguage) {
option.selected = true;
}
select.appendChild(option);
});
}
// Set RTL/LTR direction
function setTextDirection(langCode) {
const language = languages.find(lang => lang.code === langCode);
const body = document.body;
if (language && language.rtl) {
body.classList.add('rtl');
body.setAttribute('dir', 'rtl');
} else {
body.classList.remove('rtl');
body.setAttribute('dir', 'ltr');
}
}
// Translation function using OpenAI API with improved caching and batch processing
async function translateText(text, targetLanguage) {
if (targetLanguage === 'en' || !text || text.trim() === '') {
return text;
}
const cacheKey = `${text.trim()}_${targetLanguage}`;
if (translationCache[cacheKey]) {
const cached = translationCache[cacheKey];
return typeof cached === 'string' ? cached : cached.text;
}
const apiKey = document.getElementById('landingApiKey').value || document.getElementById('apiKey').value;
if (!apiKey) {
console.warn('No API key available for translation');
return text;
}
try {
const language = languages.find(lang => lang.code === targetLanguage);
const languageName = language ? language.name : targetLanguage;
const prompt = `Translate the following text to ${languageName}. Maintain any HTML tags, emojis, and formatting. Only return the translated text, nothing else:\n\n${text}`;
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify({
model: "gpt-4o-mini",
messages: [{ role: "user", content: prompt }],
max_tokens: 800,
temperature: 0.2
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || 'Translation API request failed');
}
const data = await response.json();
const translatedText = data.choices[0].message.content.trim();
// Cache the translation with timestamp
translationCache[cacheKey] = {
text: translatedText,
timestamp: Date.now()
};
saveSettings();
return translatedText;
} catch (error) {
console.error('Translation error:', error);
// Try to find a similar cached translation as fallback
const similarKey = Object.keys(translationCache).find(key =>
key.includes(text.substring(0, 20)) && key.endsWith(`_${targetLanguage}`)
);
if (similarKey && translationCache[similarKey]) {
return translationCache[similarKey].text || translationCache[similarKey];
}
return text; // Return original text if translation fails
}
}
// Batch translation for better performance
async function translateBatch(textArray, targetLanguage) {
if (targetLanguage === 'en') {
return textArray;
}
const results = [];
const uncachedTexts = [];
const uncachedIndices = [];
// Check cache first
textArray.forEach((text, index) => {
const cacheKey = `${text.trim()}_${targetLanguage}`;
if (translationCache[cacheKey]) {
const cached = translationCache[cacheKey];
results[index] = typeof cached === 'string' ? cached : cached.text;
} else {
uncachedTexts.push(text);
uncachedIndices.push(index);
}
});
// Translate uncached texts
if (uncachedTexts.length > 0) {
const apiKey = document.getElementById('landingApiKey').value || document.getElementById('apiKey').value;
if (!apiKey) {
// Fill remaining with original texts
uncachedIndices.forEach((index, i) => {
results[index] = uncachedTexts[i];
});
return results;
}
try {
const language = languages.find(lang => lang.code === targetLanguage);
const languageName = language ? language.name : targetLanguage;
const batchText = uncachedTexts.map((text, i) => `${i + 1}. ${text}`).join('\n');
const prompt = `Translate the following numbered list to ${languageName}. Maintain the numbering, HTML tags, emojis, and formatting. Return only the translated numbered list:\n\n${batchText}`;
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify({
model: "gpt-4o-mini",
messages: [{ role: "user", content: prompt }],
max_tokens: 2000,
temperature: 0.2
})
});
if (response.ok) {
const data = await response.json();
const translatedBatch = data.choices[0].message.content.trim();
const translatedLines = translatedBatch.split('\n').filter(line => line.trim());
translatedLines.forEach((line, i) => {
if (i < uncachedTexts.length) {
const translatedText = line.replace(/^\d+\.\s*/, '').trim();
const originalIndex = uncachedIndices[i];
results[originalIndex] = translatedText;
// Cache the translation
const cacheKey = `${uncachedTexts[i].trim()}_${targetLanguage}`;
translationCache[cacheKey] = {
text: translatedText,
timestamp: Date.now()
};
}
});
saveSettings();
} else {
// Fallback to individual translations
for (let i = 0; i < uncachedTexts.length; i++) {
const translatedText = await translateText(uncachedTexts[i], targetLanguage);
results[uncachedIndices[i]] = translatedText;
}
}
} catch (error) {
console.error('Batch translation error:', error);
// Fallback to original texts
uncachedIndices.forEach((index, i) => {
results[index] = uncachedTexts[i];
});
}
}
return results;
}
// Clean old cache entries (older than 30 days)
function cleanOldCache() {
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
let cleaned = false;
Object.keys(translationCache).forEach(key => {
const cached = translationCache[key];
if (typeof cached === 'object' && cached.timestamp && cached.timestamp < thirtyDaysAgo) {
delete translationCache[key];
cleaned = true;
}
});
if (cleaned) {
saveSettings();
}
}
// Apply translations to the page with improved batch processing
async function applyTranslations(langCode) {
if (isTranslating) return;
isTranslating = true;
const elements = document.querySelectorAll('[data-translate]');
const placeholderElements = document.querySelectorAll('[data-translate-placeholder]');
// Add loading indicator
document.body.classList.add('translation-loading');
// Show translation progress
const progressElement = document.getElementById('progressBar');
if (progressElement) {
progressElement.style.width = '10%';
}
try {
// Clean old cache entries
cleanOldCache();
if (langCode === 'en') {
// Apply English (default) translations
elements.forEach(element => {
const key = element.getAttribute('data-translate');
const originalText = defaultTranslations[key] || element.textContent;
element.textContent = originalText;
});
placeholderElements.forEach(element => {
const key = element.getAttribute('data-translate-placeholder');
if (key === 'api_key_placeholder') {
element.setAttribute('placeholder', 'Enter your OpenAI API key');
} else if (key === 'code_input_placeholder') {
element.setAttribute('placeholder', 'Paste your code here... (supports all programming languages: Python, JavaScript, Java, C++, HTML, CSS, SQL, etc.)');
}
});
} else {
// Collect all texts to translate
const textsToTranslate = [];
const elementMap = [];
elements.forEach(element => {
const key = element.getAttribute('data-translate');
const originalText = defaultTranslations[key] || element.textContent;
textsToTranslate.push(originalText);
elementMap.push({ element, type: 'text' });
});
placeholderElements.forEach(element => {
const originalPlaceholder = element.getAttribute('placeholder');
if (originalPlaceholder) {
textsToTranslate.push(originalPlaceholder);
elementMap.push({ element, type: 'placeholder' });
}
});
if (progressElement) {
progressElement.style.width = '30%';
}
// Batch translate all texts
const translatedTexts = await translateBatch(textsToTranslate, langCode);
if (progressElement) {
progressElement.style.width = '70%';
}
// Apply translations
translatedTexts.forEach((translatedText, index) => {
const mapping = elementMap[index];
if (mapping.type === 'text') {
mapping.element.textContent = translatedText;
} else if (mapping.type === 'placeholder') {
mapping.element.setAttribute('placeholder', translatedText);
}
});
}
if (progressElement) {
progressElement.style.width = '90%';
}
// Update current language display
const language = languages.find(lang => lang.code === langCode);
if (language) {
const currentLangElement = document.getElementById('currentLanguageName');
if (currentLangElement) {
currentLangElement.textContent = language.nativeName;
}
}
if (progressElement) {
progressElement.style.width = '100%';
setTimeout(() => {
progressElement.style.width = '0%';
}, 1000);
}
} catch (error) {
console.error('Error applying translations:', error);
// Show error message to user
const errorDiv = document.getElementById('errorMessage');
if (errorDiv) {
errorDiv.textContent = `Translation error: ${error.message}`;
errorDiv.style.display = 'block';
setTimeout(() => {
errorDiv.style.display = 'none';
}, 5000);
}
} finally {
document.body.classList.remove('translation-loading');
isTranslating = false;
}
}
// Initialize the application with improved loading states
function initializeApp() {
loadSettings();
populateLanguageDropdown();
setTextDirection(currentLanguage);
// If language is already selected, show main app
if (currentLanguage !== 'en' || localStorage.getItem('codetutor_language')) {
showMainApp();
// Show loading state while translating
const statusDiv = document.getElementById('statusMessage');
if (statusDiv && currentLanguage !== 'en') {
statusDiv.textContent = 'Loading translations...';
statusDiv.style.display = 'block';
}
applyTranslations(currentLanguage).then(() => {
if (statusDiv) {
statusDiv.style.display = 'none';
}
});
}
}
// Show main application with smooth transition
function showMainApp() {
const landingPage = document.getElementById('landingPage');
const mainApp = document.getElementById('mainApp');
landingPage.classList.add('hidden');
mainApp.classList.add('active');
// Copy API key from landing to main app
const landingApiKey = document.getElementById('landingApiKey').value;
if (landingApiKey) {
document.getElementById('apiKey').value = landingApiKey;
localStorage.setItem('codetutor_api_key', landingApiKey);
}
// Focus on code input for better UX
setTimeout(() => {
const codeInput = document.getElementById('codeInput');
if (codeInput) {
codeInput.focus();
}
}, 500);
}
// Show landing page with reset
function showLandingPage() {
const landingPage = document.getElementById('landingPage');
const mainApp = document.getElementById('mainApp');
landingPage.classList.remove('hidden');
mainApp.classList.remove('active');
// Reset language selection to current language
const languageSelect = document.getElementById('languageSelect');
if (languageSelect) {
languageSelect.value = currentLanguage;
}
}
// Event Listeners
document.getElementById('startBtn').addEventListener('click', async function() {
const selectedLanguage = document.getElementById('languageSelect').value;
const apiKey = document.getElementById('landingApiKey').value.trim();
if (!selectedLanguage) {
alert('Please select a language');
return;
}
if (!apiKey) {
alert('Please enter your OpenAI API key');
return;
}
// Save settings
currentLanguage = selectedLanguage;
localStorage.setItem('codetutor_api_key', apiKey);
saveSettings();
// Set text direction
setTextDirection(currentLanguage);
// Show main app
showMainApp();
// Apply translations
await applyTranslations(currentLanguage);
});
document.getElementById('changeLanguageBtn').addEventListener('click', function() {
showLandingPage();
});
document.getElementById('clearCacheBtn').addEventListener('click', function() {
const cacheSize = Object.keys(translationCache).length;
const cacheMemory = JSON.stringify(translationCache).length;
const cacheMemoryKB = Math.round(cacheMemory / 1024);
const confirmMessage = `Clear translation cache?\n\nCurrent cache contains:\n• ${cacheSize} translations\n• ~${cacheMemoryKB} KB of data\n\nThis will remove all cached translations and may require re-translating content.`;
if (confirm(confirmMessage)) {
translationCache = {};
localStorage.removeItem('codetutor_translation_cache');
// Show success message
const statusDiv = document.getElementById('statusMessage');
if (statusDiv) {
statusDiv.textContent = `Translation cache cleared! Removed ${cacheSize} cached translations.`;
statusDiv.style.display = 'block';
setTimeout(() => {
statusDiv.style.display = 'none';
}, 3000);
} else {
alert(`Translation cache cleared successfully! Removed ${cacheSize} cached translations.`);
}
}
});
// Store API key in localStorage
const apiKeyInput = document.getElementById('apiKey');
const landingApiKeyInput = document.getElementById('landingApiKey');
[apiKeyInput, landingApiKeyInput].forEach(input => {
input.addEventListener('input', function() {
localStorage.setItem('codetutor_api_key', this.value);
// Sync both inputs
if (this.id === 'apiKey') {
landingApiKeyInput.value = this.value;
} else {
apiKeyInput.value = this.value;
}
});
});
// Initialize when page loads
document.addEventListener('DOMContentLoaded', initializeApp);
// Character counter
const codeInputTextarea = document.getElementById('codeInput');
const charCountSpan = document.getElementById('charCount');
codeInputTextarea.addEventListener('input', function() {
const currentLength = this.value.length;
charCountSpan.textContent = currentLength;
if (currentLength > 9000) {
charCountSpan.style.color = 'var(--warning)';
} else if (currentLength > 9500) {
charCountSpan.style.color = 'var(--error)';
} else {
charCountSpan.style.color = 'var(--text-secondary)';
}
});
// Update progress bar
function updateProgress(percent) {
document.getElementById('progressBar').style.width = percent + '%';
}
// Show status message
function showStatus(message) {
const statusDiv = document.getElementById('statusMessage');
statusDiv.textContent = message;
statusDiv.style.display = 'block';
}
// Hide status message
function hideStatus() {
document.getElementById('statusMessage').style.display = 'none';
}
// Error handling
function showError(message) {
const errorDiv = document.getElementById('errorMessage');
errorDiv.textContent = message;
errorDiv.style.display = 'block';
setTimeout(() => {
errorDiv.style.display = 'none';
}, 5000);
}
// Build the comprehensive code explanation prompt
function buildCodeExplanationPrompt() {
const codeInput = document.getElementById('codeInput').value.trim();
const programmingLanguage = document.getElementById('programmingLanguage').value;
const explanationLevel = document.getElementById('explanationLevel').value;
const languageContext = programmingLanguage ? `Programming Language: ${programmingLanguage}` : 'Programming Language: Auto-detect from code';
// Get the target language for the explanation
const language = languages.find(lang => lang.code === currentLanguage);
const languageName = language ? language.name : 'English';
return `You are CodeTutor Pro, an expert programming instructor and code explainer. Your mission is to make complex code understandable for learners at different levels. A user has provided code and wants a comprehensive explanation.
IMPORTANT: Provide the entire response in ${languageName}. All explanations, headings, and content should be in ${languageName}.
CRITICAL INSTRUCTION: DO NOT include any code snippets, code blocks, or actual code in your response. Provide ONLY descriptive explanations in natural language. Instead of showing code, describe what the code does in detail using words only.
USER DETAILS:
${languageContext}
Explanation Level: ${explanationLevel}
Response Language: ${languageName}
CODE TO EXPLAIN:
<<<BEGIN CODE>>>
${codeInput}
<<<END CODE>>>
Provide a comprehensive, well-structured explanation using section headers with underlines (not numbered lists). Format your response with clear section headers followed by detailed paragraphs. Use this structure:
**Section Header Format**: Use descriptive section titles that explain what will be discussed, followed by detailed paragraphs.
Structure your explanation with these types of sections:
**Code Overview Section**
- What this code does in simple terms
- The main purpose and functionality
- What problem it solves or what task it accomplishes
**Detailed Analysis Section**
- Describe each section and component in detail using natural language
- Explain what each part does and why it's needed without showing the actual code
- Describe how different parts work together
**Key Concepts Section**
- Important programming concepts demonstrated in this code
- Explain any algorithms, data structures, or design patterns used
- Define technical terms in simple language
**Execution Flow Section**
- Step-by-step execution flow described in words
- What happens when the code runs
- Input and output explanation
**Learning Insights Section**
- What beginners can learn from this code
- Best practices demonstrated
- Common patterns or techniques shown
**Practical Applications Section**
- Where this type of code might be used
- Real-world scenarios and examples
- How it could be modified or extended
FORMATTING INSTRUCTIONS:
- Use descriptive section headers (not numbered lists like 1., 2., 3.)
- Write each section as flowing paragraphs of text
- Ensure proper text direction for RTL languages
- Integrate English technical terms naturally within the text flow
- Use clear, professional formatting with proper spacing
Tailor your explanation to the ${explanationLevel} level:
- **Complete Beginner**: Use very simple language, explain basic concepts, avoid jargon
- **Beginner**: Use clear language, explain intermediate concepts, minimal jargon with definitions
- **Intermediate**: Use standard programming terminology, focus on logic and structure
- **Advanced**: Use technical language, focus on optimization, patterns, and best practices
Make the explanation engaging, educational, and easy to follow. Use examples and analogies when helpful. Format with clear headings and structure for easy reading.
REMEMBER: Write everything in ${languageName}. DO NOT include any code snippets or code blocks - provide ONLY descriptive text explanations.`;
}
// API call function
async function callAPI(prompt) {
const apiKey = document.getElementById('apiKey').value.trim();
if (!apiKey) {
throw new Error('Please enter your OpenAI API key');
}
const payload = {
model: "gpt-4o-mini",
messages: [{ role: "system", content: prompt }],
max_tokens: 16000,
temperature: 0.5
};
const response = await fetch("https://api.openai.com/v1/chat/completions", {
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 || "API request failed");
}
const data = await response.json();
// Debug: Log the full response to check for truncation
console.log('Full API response:', data);
// Check if the response was truncated
if (data.choices[0].finish_reason === 'length') {
console.warn('Response was truncated due to max_tokens limit');
}
return data.choices[0].message.content;
}
// Validation function
function validateInputs() {
const codeInput = document.getElementById('codeInput').value.trim();
const explanationLevel = document.getElementById('explanationLevel').value;
if (!codeInput) {
document.getElementById('codeInput').focus();
throw new Error('Please enter the code you want explained');
}
if (codeInput.length < 10) {
document.getElementById('codeInput').focus();
throw new Error('Please provide more code to analyze (at least 10 characters)');
}
if (!explanationLevel) {
document.getElementById('explanationLevel').focus();
throw new Error('Please select your explanation level');
}
}
// Handle form submission
document.getElementById('generateBtn').addEventListener('click', async function() {
const button = this;
const buttonText = document.getElementById('buttonText');
const spinner = document.getElementById('spinner');
const explanationOutput = document.getElementById('explanationOutput');
try {
// Validate inputs
validateInputs();
// Update button state
button.disabled = true;
spinner.style.display = 'inline-block';
buttonText.textContent = 'Analyzing Code...';
// Clear previous output
explanationOutput.innerHTML = '';
hideStatus();
// Show progress and status
updateProgress(20);
showStatus("Analyzing code structure and syntax...");
// Build prompt and call API
const prompt = buildCodeExplanationPrompt();
updateProgress(50);
showStatus("Generating comprehensive explanation...");
const result = await callAPI(prompt);
updateProgress(100);
hideStatus();
// Display result with section headers and underlines format
let formattedResult = result;
// Debug: Log the raw result to see what we're getting
console.log('Raw API result:', result);
// Remove any code block markers that might accidentally be included
formattedResult = formattedResult.replace(/```[\s\S]*?```/g, '');
formattedResult = formattedResult.replace(/`([^`\n]+)`/g, '$1');
// Handle section headers with underlines (convert markdown headers to section headers)
formattedResult = formattedResult.replace(/^## (.*?)$/gm, '<div class="section-header">$1</div>');
formattedResult = formattedResult.replace(/^### (.*?)$/gm, '<div class="section-header">$1</div>');
formattedResult = formattedResult.replace(/^#### (.*?)$/gm, '<div class="section-header">$1</div>');
// Handle bold text
formattedResult = formattedResult.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
// Convert line breaks to <br> tags BEFORE processing sections
formattedResult = formattedResult.replace(/\n/g, "<br>");
// Group content after section headers into section-content divs
formattedResult = formattedResult.replace(/(<div class="section-header">.*?<\/div>)<br>(.*?)(?=<div class="section-header">|$)/g,
'$1<br><div class="section-content">$2</div>');
// Clean up extra <br> tags around section headers
formattedResult = formattedResult.replace(/<br><div class="section-header">/g, '<div class="section-header">');
formattedResult = formattedResult.replace(/<\/div><br><div class="section-content">/g, '</div><div class="section-content">');
// Handle any remaining content that's not in a section (like intro text)
if (!formattedResult.includes('<div class="section-header">')) {
// If no section headers, wrap everything in section-content
formattedResult = `<div class="section-content">${formattedResult}</div>`;
} else {
// Handle content before the first section header
formattedResult = formattedResult.replace(/^(.*?)(<div class="section-header">)/s,
'<div class="section-content">$1</div>$2');
}
explanationOutput.innerHTML = formattedResult;
} catch (error) {
console.error('Error:', error);
showError(error.message);
explanationOutput.innerHTML = '';
hideStatus();
updateProgress(0);
} finally {
// Reset button state
button.disabled = false;
spinner.style.display = 'none';
buttonText.textContent = '🧠 Generate Explanation';
}
});
// Handle input changes
document.getElementById('codeInput').addEventListener('input', function() {
updateProgress(0);
hideStatus();
});
document.getElementById('programmingLanguage').addEventListener('change', function() {
updateProgress(0);
hideStatus();
});
document.getElementById('explanationLevel').addEventListener('change', function() {
updateProgress(0);
hideStatus();
});
// Keyboard shortcut for generation
document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
if (document.getElementById('mainApp').classList.contains('active')) {
document.getElementById('generateBtn').click();
}
}
});
// Handle contenteditable placeholder
const outputArea = document.getElementById('explanationOutput');
outputArea.addEventListener('focus', function() {
if (this.innerHTML === '') {
this.innerHTML = '';
}
});
outputArea.addEventListener('blur', function() {
if (this.innerHTML.trim() === '') {
this.innerHTML = '';
}
});
</script>
</body>
</html>