soxogvv commited on
Commit
9e591b6
·
verified ·
1 Parent(s): a7640b5

Create templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +653 -0
templates/index.html ADDED
@@ -0,0 +1,653 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>CodeLlama Pro - AI Code Assistant</title>
7
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet">
8
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
9
+ <style>
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
18
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
19
+ min-height: 100vh;
20
+ color: #333;
21
+ }
22
+
23
+ .container {
24
+ max-width: 1400px;
25
+ margin: 0 auto;
26
+ padding: 20px;
27
+ min-height: 100vh;
28
+ }
29
+
30
+ .header {
31
+ text-align: center;
32
+ margin-bottom: 30px;
33
+ color: white;
34
+ }
35
+
36
+ .header h1 {
37
+ font-size: 3rem;
38
+ margin-bottom: 10px;
39
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
40
+ }
41
+
42
+ .header p {
43
+ font-size: 1.2rem;
44
+ opacity: 0.9;
45
+ }
46
+
47
+ .main-content {
48
+ display: grid;
49
+ grid-template-columns: 1fr 1fr;
50
+ gap: 30px;
51
+ height: calc(100vh - 200px);
52
+ }
53
+
54
+ .panel {
55
+ background: rgba(255, 255, 255, 0.95);
56
+ backdrop-filter: blur(10px);
57
+ border-radius: 20px;
58
+ box-shadow: 0 20px 40px rgba(0,0,0,0.1);
59
+ padding: 30px;
60
+ display: flex;
61
+ flex-direction: column;
62
+ }
63
+
64
+ .panel-header {
65
+ display: flex;
66
+ align-items: center;
67
+ margin-bottom: 20px;
68
+ padding-bottom: 15px;
69
+ border-bottom: 2px solid #eee;
70
+ }
71
+
72
+ .panel-header i {
73
+ font-size: 1.5rem;
74
+ margin-right: 15px;
75
+ color: #667eea;
76
+ }
77
+
78
+ .panel-header h2 {
79
+ font-size: 1.5rem;
80
+ color: #333;
81
+ }
82
+
83
+ .form-group {
84
+ margin-bottom: 20px;
85
+ }
86
+
87
+ .form-group label {
88
+ display: block;
89
+ margin-bottom: 8px;
90
+ font-weight: 600;
91
+ color: #555;
92
+ }
93
+
94
+ .textarea-wrapper {
95
+ position: relative;
96
+ flex: 1;
97
+ }
98
+
99
+ textarea, .code-output {
100
+ width: 100%;
101
+ padding: 15px;
102
+ border: 2px solid #e0e0e0;
103
+ border-radius: 12px;
104
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
105
+ font-size: 14px;
106
+ resize: vertical;
107
+ transition: all 0.3s ease;
108
+ background: #fafafa;
109
+ }
110
+
111
+ textarea:focus {
112
+ outline: none;
113
+ border-color: #667eea;
114
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
115
+ }
116
+
117
+ .code-output {
118
+ background: #2d3748;
119
+ color: #e2e8f0;
120
+ min-height: 200px;
121
+ overflow-y: auto;
122
+ white-space: pre-wrap;
123
+ word-break: break-all;
124
+ }
125
+
126
+ .btn {
127
+ padding: 12px 30px;
128
+ border: none;
129
+ border-radius: 25px;
130
+ font-size: 16px;
131
+ font-weight: 600;
132
+ cursor: pointer;
133
+ transition: all 0.3s ease;
134
+ display: inline-flex;
135
+ align-items: center;
136
+ justify-content: center;
137
+ gap: 8px;
138
+ text-decoration: none;
139
+ }
140
+
141
+ .btn-primary {
142
+ background: linear-gradient(45deg, #667eea, #764ba2);
143
+ color: white;
144
+ }
145
+
146
+ .btn-primary:hover {
147
+ transform: translateY(-2px);
148
+ box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4);
149
+ }
150
+
151
+ .btn-secondary {
152
+ background: #6c757d;
153
+ color: white;
154
+ }
155
+
156
+ .btn-secondary:hover {
157
+ background: #5a6268;
158
+ transform: translateY(-2px);
159
+ }
160
+
161
+ .btn:disabled {
162
+ opacity: 0.6;
163
+ cursor: not-allowed;
164
+ transform: none !important;
165
+ }
166
+
167
+ .action-buttons {
168
+ display: flex;
169
+ gap: 15px;
170
+ margin-top: 20px;
171
+ }
172
+
173
+ .status-bar {
174
+ background: rgba(255, 255, 255, 0.9);
175
+ padding: 15px 30px;
176
+ border-radius: 15px;
177
+ margin-bottom: 20px;
178
+ display: flex;
179
+ align-items: center;
180
+ justify-content: space-between;
181
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
182
+ }
183
+
184
+ .status-indicator {
185
+ display: flex;
186
+ align-items: center;
187
+ gap: 10px;
188
+ }
189
+
190
+ .status-dot {
191
+ width: 12px;
192
+ height: 12px;
193
+ border-radius: 50%;
194
+ background: #dc3545;
195
+ animation: pulse 2s infinite;
196
+ }
197
+
198
+ .status-dot.loading {
199
+ background: #ffc107;
200
+ }
201
+
202
+ .status-dot.ready {
203
+ background: #28a745;
204
+ animation: none;
205
+ }
206
+
207
+ @keyframes pulse {
208
+ 0% { opacity: 1; }
209
+ 50% { opacity: 0.5; }
210
+ 100% { opacity: 1; }
211
+ }
212
+
213
+ .loading-spinner {
214
+ display: inline-block;
215
+ width: 20px;
216
+ height: 20px;
217
+ border: 3px solid #f3f3f3;
218
+ border-top: 3px solid #667eea;
219
+ border-radius: 50%;
220
+ animation: spin 1s linear infinite;
221
+ }
222
+
223
+ @keyframes spin {
224
+ 0% { transform: rotate(0deg); }
225
+ 100% { transform: rotate(360deg); }
226
+ }
227
+
228
+ .explanation-panel {
229
+ background: linear-gradient(145deg, #f8f9ff, #e6f3ff);
230
+ border: 1px solid #d0e7ff;
231
+ border-radius: 12px;
232
+ padding: 20px;
233
+ margin-top: 15px;
234
+ }
235
+
236
+ .explanation-panel h3 {
237
+ color: #2563eb;
238
+ margin-bottom: 10px;
239
+ display: flex;
240
+ align-items: center;
241
+ gap: 8px;
242
+ }
243
+
244
+ .copy-btn {
245
+ position: absolute;
246
+ top: 10px;
247
+ right: 10px;
248
+ background: rgba(0,0,0,0.7);
249
+ color: white;
250
+ border: none;
251
+ padding: 8px 12px;
252
+ border-radius: 6px;
253
+ cursor: pointer;
254
+ font-size: 12px;
255
+ opacity: 0;
256
+ transition: opacity 0.3s ease;
257
+ }
258
+
259
+ .textarea-wrapper:hover .copy-btn,
260
+ .code-output-wrapper:hover .copy-btn {
261
+ opacity: 1;
262
+ }
263
+
264
+ .code-output-wrapper {
265
+ position: relative;
266
+ flex: 1;
267
+ }
268
+
269
+ .toast {
270
+ position: fixed;
271
+ top: 20px;
272
+ right: 20px;
273
+ background: #28a745;
274
+ color: white;
275
+ padding: 15px 25px;
276
+ border-radius: 8px;
277
+ box-shadow: 0 5px 15px rgba(0,0,0,0.2);
278
+ z-index: 1000;
279
+ opacity: 0;
280
+ transform: translateX(100px);
281
+ transition: all 0.3s ease;
282
+ }
283
+
284
+ .toast.show {
285
+ opacity: 1;
286
+ transform: translateX(0);
287
+ }
288
+
289
+ @media (max-width: 768px) {
290
+ .main-content {
291
+ grid-template-columns: 1fr;
292
+ gap: 20px;
293
+ }
294
+
295
+ .header h1 {
296
+ font-size: 2rem;
297
+ }
298
+
299
+ .container {
300
+ padding: 10px;
301
+ }
302
+ }
303
+
304
+ .char-counter {
305
+ position: absolute;
306
+ bottom: 10px;
307
+ right: 15px;
308
+ font-size: 12px;
309
+ color: #666;
310
+ background: rgba(255,255,255,0.9);
311
+ padding: 2px 6px;
312
+ border-radius: 4px;
313
+ }
314
+ </style>
315
+ </head>
316
+ <body>
317
+ <div class="container">
318
+ <div class="header">
319
+ <h1><i class="fas fa-code"></i> CodeLlama Pro</h1>
320
+ <p>Advanced AI-Powered Code Assistant & Editor</p>
321
+ </div>
322
+
323
+ <div class="status-bar">
324
+ <div class="status-indicator">
325
+ <div class="status-dot" id="statusDot"></div>
326
+ <span id="statusText">Initializing model...</span>
327
+ </div>
328
+ <div>
329
+ <button class="btn btn-secondary" id="loadModelBtn" onclick="loadModel()">
330
+ <i class="fas fa-download"></i> Load Model
331
+ </button>
332
+ </div>
333
+ </div>
334
+
335
+ <div class="main-content">
336
+ <div class="panel">
337
+ <div class="panel-header">
338
+ <i class="fas fa-edit"></i>
339
+ <h2>Code Input & Instructions</h2>
340
+ </div>
341
+
342
+ <div class="form-group">
343
+ <label for="existingCode">Existing Code (Optional)</label>
344
+ <div class="textarea-wrapper">
345
+ <textarea id="existingCode" placeholder="Paste your existing code here (optional)..." rows="10"></textarea>
346
+ <button class="copy-btn" onclick="copyToClipboard('existingCode')">
347
+ <i class="fas fa-copy"></i> Copy
348
+ </button>
349
+ <div class="char-counter" id="codeCounter">0 characters</div>
350
+ </div>
351
+ </div>
352
+
353
+ <div class="form-group">
354
+ <label for="instruction">Instructions (Required) *</label>
355
+ <div class="textarea-wrapper">
356
+ <textarea id="instruction" placeholder="Describe what you want me to do with the code..." rows="6" required></textarea>
357
+ <div class="char-counter" id="instructionCounter">0 characters</div>
358
+ </div>
359
+ </div>
360
+
361
+ <div class="action-buttons">
362
+ <button class="btn btn-primary" id="generateBtn" onclick="generateCode()" disabled>
363
+ <i class="fas fa-magic"></i> Generate Code
364
+ </button>
365
+ <button class="btn btn-secondary" id="explainBtn" onclick="explainCode()">
366
+ <i class="fas fa-lightbulb"></i> Explain Code
367
+ </button>
368
+ </div>
369
+ </div>
370
+
371
+ <div class="panel">
372
+ <div class="panel-header">
373
+ <i class="fas fa-code"></i>
374
+ <h2>Generated Code</h2>
375
+ </div>
376
+
377
+ <div class="code-output-wrapper">
378
+ <div class="code-output" id="codeOutput">
379
+ <div style="color: #a0aec0; font-style: italic; text-align: center; margin-top: 80px;">
380
+ <i class="fas fa-rocket" style="font-size: 3rem; margin-bottom: 20px; display: block;"></i>
381
+ Generated code will appear here...
382
+ <br><br>
383
+ Load the model first, then provide instructions to get started!
384
+ </div>
385
+ </div>
386
+ <button class="copy-btn" onclick="copyToClipboard('codeOutput')">
387
+ <i class="fas fa-copy"></i> Copy Code
388
+ </button>
389
+ </div>
390
+
391
+ <div class="explanation-panel" id="explanationPanel" style="display: none;">
392
+ <h3><i class="fas fa-info-circle"></i> Explanation</h3>
393
+ <div id="explanationText"></div>
394
+ </div>
395
+ </div>
396
+ </div>
397
+ </div>
398
+
399
+ <div class="toast" id="toast"></div>
400
+
401
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
402
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
403
+ <script>
404
+ let modelLoaded = false;
405
+ let isGenerating = false;
406
+
407
+ // Initialize
408
+ document.addEventListener('DOMContentLoaded', function() {
409
+ checkModelStatus();
410
+ setupCharCounters();
411
+ setupAutoResize();
412
+
413
+ // Check status every 5 seconds
414
+ setInterval(checkModelStatus, 5000);
415
+ });
416
+
417
+ // Character counters
418
+ function setupCharCounters() {
419
+ const codeTextarea = document.getElementById('existingCode');
420
+ const instructionTextarea = document.getElementById('instruction');
421
+
422
+ codeTextarea.addEventListener('input', function() {
423
+ document.getElementById('codeCounter').textContent = this.value.length + ' characters';
424
+ });
425
+
426
+ instructionTextarea.addEventListener('input', function() {
427
+ document.getElementById('instructionCounter').textContent = this.value.length + ' characters';
428
+ });
429
+ }
430
+
431
+ // Auto-resize textareas
432
+ function setupAutoResize() {
433
+ const textareas = document.querySelectorAll('textarea');
434
+ textareas.forEach(textarea => {
435
+ textarea.addEventListener('input', function() {
436
+ this.style.height = 'auto';
437
+ this.style.height = (this.scrollHeight) + 'px';
438
+ });
439
+ });
440
+ }
441
+
442
+ // Check model status
443
+ async function checkModelStatus() {
444
+ try {
445
+ const response = await fetch('/api/status');
446
+ const data = await response.json();
447
+
448
+ const statusDot = document.getElementById('statusDot');
449
+ const statusText = document.getElementById('statusText');
450
+ const generateBtn = document.getElementById('generateBtn');
451
+ const loadModelBtn = document.getElementById('loadModelBtn');
452
+
453
+ if (data.is_loaded) {
454
+ statusDot.className = 'status-dot ready';
455
+ statusText.textContent = 'Model Ready';
456
+ generateBtn.disabled = false;
457
+ loadModelBtn.style.display = 'none';
458
+ modelLoaded = true;
459
+ } else if (data.is_loading) {
460
+ statusDot.className = 'status-dot loading';
461
+ statusText.innerHTML = '<span class="loading-spinner"></span> Loading Model...';
462
+ generateBtn.disabled = true;
463
+ loadModelBtn.disabled = true;
464
+ } else {
465
+ statusDot.className = 'status-dot';
466
+ statusText.textContent = 'Model Not Loaded';
467
+ generateBtn.disabled = true;
468
+ loadModelBtn.disabled = false;
469
+ loadModelBtn.style.display = 'inline-flex';
470
+ }
471
+ } catch (error) {
472
+ console.error('Error checking status:', error);
473
+ }
474
+ }
475
+
476
+ // Load model
477
+ async function loadModel() {
478
+ try {
479
+ const response = await fetch('/api/load_model', { method: 'POST' });
480
+ const data = await response.json();
481
+
482
+ if (data.status === 'loading') {
483
+ showToast('Model loading started...', 'info');
484
+ } else if (data.status === 'loaded') {
485
+ showToast('Model already loaded!', 'success');
486
+ }
487
+
488
+ checkModelStatus();
489
+ } catch (error) {
490
+ showToast('Error loading model: ' + error.message, 'error');
491
+ }
492
+ }
493
+
494
+ // Generate code
495
+ async function generateCode() {
496
+ if (!modelLoaded || isGenerating) return;
497
+
498
+ const existingCode = document.getElementById('existingCode').value;
499
+ const instruction = document.getElementById('instruction').value.trim();
500
+
501
+ if (!instruction) {
502
+ showToast('Please provide instructions!', 'error');
503
+ return;
504
+ }
505
+
506
+ isGenerating = true;
507
+ const generateBtn = document.getElementById('generateBtn');
508
+ const originalText = generateBtn.innerHTML;
509
+
510
+ generateBtn.innerHTML = '<span class="loading-spinner"></span> Generating...';
511
+ generateBtn.disabled = true;
512
+
513
+ try {
514
+ const response = await fetch('/api/generate', {
515
+ method: 'POST',
516
+ headers: { 'Content-Type': 'application/json' },
517
+ body: JSON.stringify({
518
+ existing_code: existingCode,
519
+ instruction: instruction
520
+ })
521
+ });
522
+
523
+ const data = await response.json();
524
+
525
+ if (data.error) {
526
+ showToast('Error: ' + data.error, 'error');
527
+ return;
528
+ }
529
+
530
+ // Display code
531
+ const codeOutput = document.getElementById('codeOutput');
532
+ codeOutput.textContent = data.code || data.full_response;
533
+
534
+ // Apply syntax highlighting
535
+ Prism.highlightElement(codeOutput);
536
+
537
+ // Display explanation
538
+ if (data.explanation) {
539
+ const explanationPanel = document.getElementById('explanationPanel');
540
+ const explanationText = document.getElementById('explanationText');
541
+ explanationText.textContent = data.explanation;
542
+ explanationPanel.style.display = 'block';
543
+ }
544
+
545
+ showToast('Code generated successfully!', 'success');
546
+
547
+ } catch (error) {
548
+ showToast('Error generating code: ' + error.message, 'error');
549
+ } finally {
550
+ isGenerating = false;
551
+ generateBtn.innerHTML = originalText;
552
+ generateBtn.disabled = false;
553
+ }
554
+ }
555
+
556
+ // Explain code
557
+ async function explainCode() {
558
+ if (!modelLoaded) {
559
+ showToast('Please load the model first!', 'error');
560
+ return;
561
+ }
562
+
563
+ const existingCode = document.getElementById('existingCode').value.trim();
564
+ const codeOutput = document.getElementById('codeOutput').textContent.trim();
565
+
566
+ const codeToExplain = existingCode || codeOutput;
567
+
568
+ if (!codeToExplain || codeToExplain.includes('Generated code will appear here')) {
569
+ showToast('Please provide code to explain!', 'error');
570
+ return;
571
+ }
572
+
573
+ const explainBtn = document.getElementById('explainBtn');
574
+ const originalText = explainBtn.innerHTML;
575
+ explainBtn.innerHTML = '<span class="loading-spinner"></span> Explaining...';
576
+ explainBtn.disabled = true;
577
+
578
+ try {
579
+ const response = await fetch('/api/explain', {
580
+ method: 'POST',
581
+ headers: { 'Content-Type': 'application/json' },
582
+ body: JSON.stringify({ code: codeToExplain })
583
+ });
584
+
585
+ const data = await response.json();
586
+
587
+ if (data.error) {
588
+ showToast('Error: ' + data.error, 'error');
589
+ return;
590
+ }
591
+
592
+ // Display explanation
593
+ const explanationPanel = document.getElementById('explanationPanel');
594
+ const explanationText = document.getElementById('explanationText');
595
+ explanationText.textContent = data.explanation;
596
+ explanationPanel.style.display = 'block';
597
+
598
+ showToast('Code explanation generated!', 'success');
599
+
600
+ } catch (error) {
601
+ showToast('Error explaining code: ' + error.message, 'error');
602
+ } finally {
603
+ explainBtn.innerHTML = originalText;
604
+ explainBtn.disabled = false;
605
+ }
606
+ }
607
+
608
+ // Copy to clipboard
609
+ async function copyToClipboard(elementId) {
610
+ const element = document.getElementById(elementId);
611
+ const text = element.value || element.textContent;
612
+
613
+ try {
614
+ await navigator.clipboard.writeText(text);
615
+ showToast('Copied to clipboard!', 'success');
616
+ } catch (error) {
617
+ // Fallback for older browsers
618
+ const textArea = document.createElement('textarea');
619
+ textArea.value = text;
620
+ document.body.appendChild(textArea);
621
+ textArea.select();
622
+ document.execCommand('copy');
623
+ document.body.removeChild(textArea);
624
+ showToast('Copied to clipboard!', 'success');
625
+ }
626
+ }
627
+
628
+ // Show toast notification
629
+ function showToast(message, type = 'success') {
630
+ const toast = document.getElementById('toast');
631
+ toast.textContent = message;
632
+ toast.className = `toast show ${type}`;
633
+
634
+ setTimeout(() => {
635
+ toast.className = 'toast';
636
+ }, 3000);
637
+ }
638
+
639
+ // Keyboard shortcuts
640
+ document.addEventListener('keydown', function(e) {
641
+ if (e.ctrlKey || e.metaKey) {
642
+ if (e.key === 'Enter') {
643
+ e.preventDefault();
644
+ generateCode();
645
+ } else if (e.key === 'e') {
646
+ e.preventDefault();
647
+ explainCode();
648
+ }
649
+ }
650
+ });
651
+ </script>
652
+ </body>
653
+ </html>