Erik commited on
Commit
268159b
·
verified ·
1 Parent(s): 4f86eec

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +644 -3
index.html CHANGED
@@ -1,5 +1,646 @@
1
- Before coding, I need to clarify one thing:
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- **Should the application use the official Hugging Face `transformers` library (PyTorch) with manual CPU offloading, or should it use the `transformers.js` (WebLLM) approach to run entirely in the browser (client-side) to ensure zero load on the free CPU tier?**
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
- *Note: If we use server-side PyTorch on the free tier, we must be very careful about memory usage. If we use `transformers.js`, the heavy lifting happens on the user's device, which is often better for free tiers.*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Client-Side AI Code Modifier</title>
7
+
8
+ <!-- Icons -->
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
+
11
+ <!-- Fonts -->
12
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
13
 
14
+ <style>
15
+ :root {
16
+ --bg-dark: #0f0f11;
17
+ --bg-panel: #1a1a1d;
18
+ --bg-input: #252529;
19
+ --accent: #6366f1;
20
+ --accent-hover: #4f46e5;
21
+ --text-main: #e4e4e7;
22
+ --text-muted: #a1a1aa;
23
+ --border: #27272a;
24
+ --success: #10b981;
25
+ --error: #ef4444;
26
+ --code-font: 'JetBrains Mono', monospace;
27
+ }
28
 
29
+ * {
30
+ box-sizing: border-box;
31
+ margin: 0;
32
+ padding: 0;
33
+ }
34
+
35
+ body {
36
+ font-family: 'Inter', sans-serif;
37
+ background-color: var(--bg-dark);
38
+ color: var(--text-main);
39
+ height: 100vh;
40
+ overflow: hidden;
41
+ display: flex;
42
+ flex-direction: column;
43
+ }
44
+
45
+ /* --- Header --- */
46
+ header {
47
+ background-color: var(--bg-panel);
48
+ border-bottom: 1px solid var(--border);
49
+ padding: 1rem 2rem;
50
+ display: flex;
51
+ justify-content: space-between;
52
+ align-items: center;
53
+ height: 64px;
54
+ flex-shrink: 0;
55
+ }
56
+
57
+ .brand {
58
+ display: flex;
59
+ align-items: center;
60
+ gap: 10px;
61
+ font-weight: 700;
62
+ font-size: 1.2rem;
63
+ color: var(--text-main);
64
+ }
65
+
66
+ .brand i { color: var(--accent); }
67
+
68
+ .logo-link {
69
+ text-decoration: none;
70
+ color: var(--text-muted);
71
+ font-size: 0.8rem;
72
+ display: flex;
73
+ align-items: center;
74
+ gap: 5px;
75
+ transition: color 0.2s;
76
+ }
77
+
78
+ .logo-link:hover {
79
+ color: var(--accent);
80
+ }
81
+
82
+ .status-badge {
83
+ display: flex;
84
+ align-items: center;
85
+ gap: 8px;
86
+ font-size: 0.75rem;
87
+ background: rgba(16, 185, 129, 0.1);
88
+ color: var(--success);
89
+ padding: 4px 12px;
90
+ border-radius: 20px;
91
+ border: 1px solid rgba(16, 185, 129, 0.2);
92
+ }
93
+
94
+ .status-dot {
95
+ width: 8px;
96
+ height: 8px;
97
+ background-color: var(--success);
98
+ border-radius: 50%;
99
+ box-shadow: 0 0 8px var(--success);
100
+ }
101
+
102
+ /* --- Main Layout --- */
103
+ main {
104
+ display: grid;
105
+ grid-template-columns: 1fr 350px;
106
+ flex-grow: 1;
107
+ overflow: hidden;
108
+ }
109
+
110
+ /* --- Editor Section --- */
111
+ .editor-container {
112
+ display: flex;
113
+ flex-direction: column;
114
+ border-right: 1px solid var(--border);
115
+ height: 100%;
116
+ }
117
+
118
+ .toolbar {
119
+ padding: 10px 20px;
120
+ background: var(--bg-panel);
121
+ border-bottom: 1px solid var(--border);
122
+ display: flex;
123
+ justify-content: space-between;
124
+ align-items: center;
125
+ }
126
+
127
+ .toolbar-title {
128
+ font-size: 0.85rem;
129
+ color: var(--text-muted);
130
+ text-transform: uppercase;
131
+ letter-spacing: 1px;
132
+ font-weight: 600;
133
+ }
134
+
135
+ .editor-area {
136
+ flex-grow: 1;
137
+ display: flex;
138
+ flex-direction: column;
139
+ }
140
+
141
+ textarea.code-input {
142
+ flex-grow: 1;
143
+ background-color: var(--bg-dark);
144
+ color: #d4d4d4;
145
+ border: none;
146
+ resize: none;
147
+ padding: 20px;
148
+ font-family: var(--code-font);
149
+ font-size: 14px;
150
+ line-height: 1.6;
151
+ outline: none;
152
+ white-space: pre;
153
+ overflow: auto;
154
+ }
155
+
156
+ textarea.code-input::placeholder {
157
+ color: #444;
158
+ }
159
+
160
+ /* --- Sidebar / AI Panel --- */
161
+ .ai-panel {
162
+ background-color: var(--bg-panel);
163
+ display: flex;
164
+ flex-direction: column;
165
+ height: 100%;
166
+ }
167
+
168
+ .panel-header {
169
+ padding: 20px;
170
+ border-bottom: 1px solid var(--border);
171
+ }
172
+
173
+ .panel-header h2 {
174
+ font-size: 1.1rem;
175
+ margin-bottom: 5px;
176
+ }
177
+
178
+ .panel-header p {
179
+ font-size: 0.8rem;
180
+ color: var(--text-muted);
181
+ }
182
+
183
+ .control-group {
184
+ padding: 20px;
185
+ border-bottom: 1px solid var(--border);
186
+ }
187
+
188
+ .label {
189
+ display: block;
190
+ font-size: 0.8rem;
191
+ color: var(--text-muted);
192
+ margin-bottom: 8px;
193
+ font-weight: 500;
194
+ }
195
+
196
+ textarea.prompt-input {
197
+ width: 100%;
198
+ background-color: var(--bg-input);
199
+ border: 1px solid var(--border);
200
+ color: var(--text-main);
201
+ padding: 10px;
202
+ border-radius: 6px;
203
+ font-family: 'Inter', sans-serif;
204
+ font-size: 0.9rem;
205
+ resize: vertical;
206
+ min-height: 80px;
207
+ outline: none;
208
+ transition: border-color 0.2s;
209
+ }
210
+
211
+ textarea.prompt-input:focus {
212
+ border-color: var(--accent);
213
+ }
214
+
215
+ .slider-container {
216
+ display: flex;
217
+ align-items: center;
218
+ gap: 10px;
219
+ }
220
+
221
+ input[type="range"] {
222
+ flex-grow: 1;
223
+ accent-color: var(--accent);
224
+ cursor: pointer;
225
+ }
226
+
227
+ .slider-value {
228
+ font-size: 0.8rem;
229
+ font-family: var(--code-font);
230
+ color: var(--accent);
231
+ min-width: 40px;
232
+ text-align: right;
233
+ }
234
+
235
+ .action-btn {
236
+ width: 100%;
237
+ padding: 14px;
238
+ margin-top: auto; /* Push to bottom if needed, but here we stack */
239
+ background-color: var(--accent);
240
+ color: white;
241
+ border: none;
242
+ border-radius: 8px;
243
+ font-weight: 600;
244
+ cursor: pointer;
245
+ display: flex;
246
+ align-items: center;
247
+ justify-content: center;
248
+ gap: 10px;
249
+ transition: all 0.2s;
250
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
251
+ }
252
+
253
+ .action-btn:hover {
254
+ background-color: var(--accent-hover);
255
+ transform: translateY(-1px);
256
+ }
257
+
258
+ .action-btn:active {
259
+ transform: translateY(0);
260
+ }
261
+
262
+ .action-btn:disabled {
263
+ background-color: var(--border);
264
+ color: var(--text-muted);
265
+ cursor: not-allowed;
266
+ transform: none;
267
+ }
268
+
269
+ /* --- Console / Output Log --- */
270
+ .console-log {
271
+ height: 150px;
272
+ background-color: #000;
273
+ padding: 15px;
274
+ overflow-y: auto;
275
+ font-family: var(--code-font);
276
+ font-size: 0.8rem;
277
+ border-top: 1px solid var(--border);
278
+ }
279
+
280
+ .log-entry {
281
+ margin-bottom: 6px;
282
+ padding-bottom: 6px;
283
+ border-bottom: 1px solid #222;
284
+ }
285
+
286
+ .log-time {
287
+ color: var(--text-muted);
288
+ margin-right: 8px;
289
+ }
290
+
291
+ .log-info { color: #60a5fa; }
292
+ .log-success { color: var(--success); }
293
+ .log-warn { color: #fbbf24; }
294
+
295
+ /* --- Responsive Design --- */
296
+ @media (max-width: 768px) {
297
+ main {
298
+ grid-template-columns: 1fr;
299
+ grid-template-rows: 1fr 1fr;
300
+ overflow-y: auto;
301
+ }
302
+
303
+ .editor-container {
304
+ border-right: none;
305
+ border-bottom: 1px solid var(--border);
306
+ }
307
+
308
+ .ai-panel {
309
+ height: 500px;
310
+ }
311
+
312
+ body {
313
+ overflow: auto;
314
+ }
315
+
316
+ header {
317
+ padding: 1rem;
318
+ }
319
+ }
320
+
321
+ /* Loading Animation */
322
+ .loader {
323
+ width: 18px;
324
+ height: 18px;
325
+ border: 3px solid #ffffff;
326
+ border-bottom-color: transparent;
327
+ border-radius: 50%;
328
+ display: inline-block;
329
+ box-sizing: border-box;
330
+ animation: rotation 1s linear infinite;
331
+ }
332
+
333
+ @keyframes rotation {
334
+ 0% { transform: rotate(0deg); }
335
+ 100% { transform: rotate(360deg); }
336
+ }
337
+ </style>
338
+ </head>
339
+ <body>
340
+
341
+ <header>
342
+ <div class="brand">
343
+ <i class="fa-solid fa-brain"></i>
344
+ <span>Client-Side AI Studio</span>
345
+ </div>
346
+ <div style="display: flex; align-items: center; gap: 20px;">
347
+ <div class="status-badge">
348
+ <div class="status-dot"></div>
349
+ <span>WebLLM (Transformers.js)</span>
350
+ </div>
351
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="logo-link">
352
+ <i class="fa-solid fa-code"></i> Built with anycoder
353
+ </a>
354
+ </div>
355
+ </header>
356
+
357
+ <main>
358
+ <!-- Left Side: Code Editor -->
359
+ <section class="editor-container">
360
+ <div class="toolbar">
361
+ <span class="toolbar-title"><i class="fa-solid fa-file-code"></i> main.js</span>
362
+ <div style="font-size: 0.75rem; color: var(--text-muted);">
363
+ <i class="fa-solid fa-terminal"></i> Ready
364
+ </div>
365
+ </div>
366
+ <div class="editor-area">
367
+ <textarea id="codeEditor" class="code-input" spellcheck="false" placeholder="// Existing code appears here...&#10;const server = require('express')();&#10;&#10;// TODO: Optimize database connection&#10;function legacyFunction(data) {&#10; // messy logic here&#10; return data;&#10;}">
368
+ // --- EXISTING CODE BASE ---
369
+ // You can modify this code using the AI panel on the right.
370
+
371
+ const express = require('express');
372
+ const app = express();
373
+ const PORT = process.env.PORT || 3000;
374
+
375
+ // Middleware to parse JSON bodies
376
+ app.use(express.json());
377
+
378
+ // Database connection simulation (Needs optimization)
379
+ let database = {
380
+ users: [],
381
+ items: []
382
+ };
383
+
384
+ /**
385
+ * Legacy function to handle user creation
386
+ * @param {Object} userData
387
+ */
388
+ function createUser(userData) {
389
+ if (!userData.email) {
390
+ throw new Error('Email is required');
391
+ }
392
+
393
+ const newUser = {
394
+ id: Date.now(),
395
+ ...userData,
396
+ createdAt: new Date().toISOString()
397
+ };
398
+
399
+ database.users.push(newUser);
400
+ return newUser;
401
+ }
402
+
403
+ // Endpoint: Get all users
404
+ app.get('/api/users', (req, res) => {
405
+ res.json({ success: true, data: database.users });
406
+ });
407
+
408
+ // Endpoint: Create User
409
+ app.post('/api/users', (req, res) => {
410
+ try {
411
+ const user = createUser(req.body);
412
+ res.status(201).json({ success: true, data: user });
413
+ } catch (error) {
414
+ res.status(400).json({ success: false, error: error.message });
415
+ }
416
+ });
417
+
418
+ // Start Server
419
+ app.listen(PORT, () => {
420
+ console.log(`Server running on port ${PORT}`);
421
+ console.log("Ready to accept requests.");
422
+ });
423
+ </textarea>
424
+ </div>
425
+ </section>
426
+
427
+ <!-- Right Side: AI Controls -->
428
+ <aside class="ai-panel">
429
+ <div class="panel-header">
430
+ <h2><i class="fa-solid fa-robot"></i> AI Assistant</h2>
431
+ <p>Running DistilGPT-2 locally in your browser</p>
432
+ </div>
433
+
434
+ <div class="control-group">
435
+ <label class="label">Modification Request</label>
436
+ <textarea id="promptInput" class="prompt-input" placeholder="Describe what you want to change...&#10;Example: 'Add input validation to the email field' or 'Refactor createUser to use async/await'"></textarea>
437
+ </div>
438
+
439
+ <div class="control-group">
440
+ <label class="label">Creativity (Temperature) <span id="tempValue" class="slider-value">0.7</span></label>
441
+ <div class="slider-container">
442
+ <input type="range" id="temperature" min="0" max="1" step="0.1" value="0.7">
443
+ </div>
444
+ <div style="margin-top: 10px;">
445
+ <label class="label">Max New Tokens</label>
446
+ <div class="slider-container">
447
+ <input type="range" id="maxTokens" min="50" max="500" step="50" value="200">
448
+ <span class="slider-value" id="tokensValue">200</span>
449
+ </div>
450
+ </div>
451
+ </div>
452
+
453
+ <button id="generateBtn" class="action-btn">
454
+ <span id="btnText"><i class="fa-solid fa-play"></i> Generate Code</span>
455
+ <div id="btnLoader" class="loader" style="display: none;"></div>
456
+ </button>
457
+
458
+ <div class="console-log" id="consoleLog">
459
+ <div class="log-entry">
460
+ <span class="log-time">[System]</span>
461
+ <span class="log-info">Transformers.js initialized. Model loaded: distilgpt2</span>
462
+ </div>
463
+ <div class="log-entry">
464
+ <span class="log-time">[System]</span>
465
+ <span class="log-info">Client-side environment ready.</span>
466
+ </div>
467
+ </div>
468
+ </aside>
469
+ </main>
470
+
471
+ <!-- Transformers.js Library -->
472
+ <script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@2.13.0/dist/transformers.min.js"></script>
473
+
474
+ <script>
475
+ // --- Configuration & State ---
476
+ const CONFIG = {
477
+ modelId: 'Xenova/distilgpt2', // Lightweight GPT model for the browser
478
+ progressCallback: (data) => {
479
+ logToConsole(`Downloading model: ${(data.progress * 100).toFixed(2)}%`, 'info');
480
+ }
481
+ };
482
+
483
+ let generator = null;
484
+ let isModelLoaded = false;
485
+
486
+ // --- DOM Elements ---
487
+ const codeEditor = document.getElementById('codeEditor');
488
+ const promptInput = document.getElementById('promptInput');
489
+ const generateBtn = document.getElementById('generateBtn');
490
+ const btnText = document.getElementById('btnText');
491
+ const btnLoader = document.getElementById('btnLoader');
492
+ const consoleLog = document.getElementById('consoleLog');
493
+ const tempInput = document.getElementById('temperature');
494
+ const tempValueDisplay = document.getElementById('tempValue');
495
+ const tokensInput = document.getElementById('maxTokens');
496
+ const tokensValueDisplay = document.getElementById('tokensValue');
497
+
498
+ // --- Helper Functions ---
499
+
500
+ // Logger for the Console Panel
501
+ function logToConsole(message, type = 'info') {
502
+ const entry = document.createElement('div');
503
+ entry.className = 'log-entry';
504
+
505
+ const time = new Date().toLocaleTimeString([], { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
506
+
507
+ let typeClass = 'log-info';
508
+ if (type === 'success') typeClass = 'log-success';
509
+ if (type === 'warn') typeClass = 'log-warn';
510
+
511
+ entry.innerHTML = `<span class="log-time">[${time}]</span> <span class="${typeClass}">${message}</span>`;
512
+ consoleLog.appendChild(entry);
513
+ consoleLog.scrollTop = consoleLog.scrollHeight;
514
+ }
515
+
516
+ // Initialize Transformers.js
517
+ async function initAI() {
518
+ try {
519
+ logToConsole('Initializing Transformers.js environment...', 'info');
520
+
521
+ // Create the text generation pipeline
522
+ generator = await pipeline('text-generation', CONFIG.modelId, {
523
+ progress_callback: CONFIG.progressCallback,
524
+ dtype: 'int8', // Use int8 quantization to save memory
525
+ device: 'cpu' // Explicitly force CPU for free tier stability
526
+ });
527
+
528
+ isModelLoaded = true;
529
+ logToConsole('Model loaded successfully: Xenova/distilgpt2', 'success');
530
+ logToConsole('System Ready. Enter a request below.', 'info');
531
+
532
+ } catch (error) {
533
+ console.error(error);
534
+ logToConsole(`Error loading model: ${error.message}`, 'warn');
535
+ alert("Failed to load AI model. Please check your internet connection and try again.");
536
+ }
537
+ }
538
+
539
+ // Process the code generation
540
+ async function handleCodeGeneration() {
541
+ if (!isModelLoaded) {
542
+ logToConsole('Model is still loading. Please wait...', 'warn');
543
+ return;
544
+ }
545
+
546
+ const userPrompt = promptInput.value.trim();
547
+ const currentCode = codeEditor.value;
548
+ const temperature = parseFloat(tempInput.value);
549
+ const maxNewTokens = parseInt(tokensInput.value);
550
+
551
+ if (!userPrompt) {
552
+ logToConsole('Please enter a modification request.', 'warn');
553
+ promptInput.focus();
554
+ return;
555
+ }
556
+
557
+ // UI State: Loading
558
+ generateBtn.disabled = true;
559
+ btnText.innerHTML = '<span class="loader" style="width:16px; height:16px; border-width:2px; margin-right:10px;"></span> Generating...';
560
+ logToConsole(`Processing request: "${userPrompt}"`, 'info');
561
+
562
+ // Construct the Prompt
563
+ // We provide the context (existing code) and the instruction (prompt)
564
+ const fullPrompt = `
565
+ <CODE>
566
+ ${currentCode}
567
+ </CODE>
568
+
569
+ <INSTRUCTION>
570
+ ${userPrompt}
571
+ </INSTRUCTION>
572
+
573
+ Please modify the code inside <CODE> tags to fulfill the instruction.
574
+ Return only the full updated code block.
575
+ `;
576
+
577
+ try {
578
+ // Run inference
579
+ const output = await generator(fullPrompt, {
580
+ max_new_tokens: maxNewTokens,
581
+ temperature: temperature,
582
+ do_sample: true,
583
+ top_k: 50,
584
+ return_full_text: false, // We want just the generated part
585
+ pad_token_id: 50256 // EOS token for GPT-2
586
+ });
587
+
588
+ const generatedText = output[0].generated_text;
589
+
590
+ // Parse logic: In a real app we might parse XML/Markdown.
591
+ // Since GPT-2 is small, it often just continues the code stream.
592
+ // We will attempt to extract just the new code block if formatted,
593
+ // or append/replace intelligently.
594
+
595
+ // For this demo, we will replace the editor content with the result
596
+ // or append it if it looks like a function addition.
597
+
598
+ logToConsole('Code generated successfully.', 'success');
599
+
600
+ // Simple cleanup for GPT-2 (it often adds quotes or extra text)
601
+ let cleanCode = generatedText.trim();
602
+
603
+ // If the model returns a code block wrapper, strip it
604
+ if (cleanCode.startsWith('```')) {
605
+ cleanCode = cleanCode.replace(/```(javascript|js|html|css)?\n?/, '').replace(/```$/, '');
606
+ }
607
+
608
+ codeEditor.value = cleanCode;
609
+ logToConsole('Editor updated with new code.', 'success');
610
+
611
+ } catch (error) {
612
+ console.error(error);
613
+ logToConsole(`Generation error: ${error.message}`, 'warn');
614
+ } finally {
615
+ // UI State: Reset
616
+ generateBtn.disabled = false;
617
+ btnText.innerHTML = '<i class="fa-solid fa-play"></i> Generate Code';
618
+ }
619
+ }
620
+
621
+ // --- Event Listeners ---
622
+
623
+ // Temperature Slider
624
+ tempInput.addEventListener('input', (e) => {
625
+ tempValueDisplay.textContent = e.target.value;
626
+ });
627
+
628
+ // Max Tokens Slider
629
+ tokensInput.addEventListener('input', (e) => {
630
+ tokensValueDisplay.textContent = e.target.value;
631
+ });
632
+
633
+ // Generate Button
634
+ generateBtn.addEventListener('click', handleCodeGeneration);
635
+
636
+ // Initialize on Load
637
+ window.addEventListener('DOMContentLoaded', () => {
638
+ // Small delay to allow UI to paint first
639
+ setTimeout(() => {
640
+ initAI();
641
+ }, 500);
642
+ });
643
+
644
+ </script>
645
+ </body>
646
+ </html>