llamameta commited on
Commit
fab23e0
·
verified ·
1 Parent(s): 5f60350

berhasil sblmnya claude

Browse files
Files changed (1) hide show
  1. index.html +387 -975
index.html CHANGED
@@ -1,989 +1,401 @@
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>GPT4Free Client - Browser Demo</title>
7
- <style>
8
- * {
9
- margin: 0;
10
- padding: 0;
11
- box-sizing: border-box;
12
- }
13
-
14
- body {
15
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
16
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
- min-height: 100vh;
18
- padding: 20px;
19
- }
20
-
21
- .container {
22
- max-width: 1200px;
23
- margin: 0 auto;
24
- background: white;
25
- border-radius: 20px;
26
- box-shadow: 0 20px 60px rgba(0,0,0,0.3);
27
- overflow: hidden;
28
- }
29
-
30
- .header {
31
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
32
- color: white;
33
- padding: 30px;
34
- text-align: center;
35
- }
36
-
37
- .header h1 {
38
- font-size: 2.5rem;
39
- margin-bottom: 10px;
40
- }
41
-
42
- .header p {
43
- opacity: 0.9;
44
- font-size: 1.1rem;
45
- }
46
-
47
- .status-bar {
48
- background: #f7fafc;
49
- padding: 15px 30px;
50
- display: flex;
51
- align-items: center;
52
- justify-content: space-between;
53
- border-bottom: 1px solid #e2e8f0;
54
- flex-wrap: wrap;
55
- gap: 10px;
56
- }
57
-
58
- .status {
59
- display: inline-flex;
60
- align-items: center;
61
- gap: 8px;
62
- padding: 8px 16px;
63
- border-radius: 20px;
64
- font-weight: 500;
65
- font-size: 0.9rem;
66
- }
67
-
68
- .status.loading {
69
- background: #fef3c7;
70
- color: #92400e;
71
- }
72
-
73
- .status.ready {
74
- background: #d1fae5;
75
- color: #065f46;
76
- }
77
-
78
- .status.error {
79
- background: #fee2e2;
80
- color: #991b1b;
81
- }
82
-
83
- .status::before {
84
- content: '';
85
- width: 8px;
86
- height: 8px;
87
- border-radius: 50%;
88
- background: currentColor;
89
- animation: pulse 2s infinite;
90
- }
91
-
92
- @keyframes pulse {
93
- 0%, 100% { opacity: 1; }
94
- 50% { opacity: 0.5; }
95
- }
96
-
97
- .provider-info {
98
- display: inline-flex;
99
- align-items: center;
100
- gap: 8px;
101
- padding: 4px 12px;
102
- background: #e0e7ff;
103
- color: #3730a3;
104
- border-radius: 12px;
105
- font-size: 0.85rem;
106
- font-weight: 500;
107
- }
108
-
109
- .controls {
110
- padding: 20px 30px;
111
- background: #f7fafc;
112
- border-bottom: 1px solid #e2e8f0;
113
- }
114
-
115
- .control-group {
116
- display: flex;
117
- gap: 15px;
118
- align-items: center;
119
- flex-wrap: wrap;
120
- }
121
-
122
- label {
123
- font-weight: 600;
124
- color: #4a5568;
125
- font-size: 0.9rem;
126
- }
127
-
128
- select, input[type="text"], input[type="password"] {
129
- padding: 8px 12px;
130
- border: 2px solid #e2e8f0;
131
- border-radius: 8px;
132
- font-size: 0.95rem;
133
- transition: all 0.3s;
134
- background: white;
135
- min-width: 150px;
136
- }
137
-
138
- select:focus, input:focus {
139
- outline: none;
140
- border-color: #667eea;
141
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
142
- }
143
-
144
- .main-content {
145
- display: grid;
146
- grid-template-columns: 1fr 1fr;
147
- gap: 30px;
148
- padding: 30px;
149
- }
150
-
151
- @media (max-width: 768px) {
152
- .main-content {
153
- grid-template-columns: 1fr;
154
- }
155
- }
156
-
157
- .card {
158
- background: white;
159
- border: 1px solid #e2e8f0;
160
- border-radius: 12px;
161
- overflow: hidden;
162
- }
163
-
164
- .card-header {
165
- background: #f7fafc;
166
- padding: 15px 20px;
167
- border-bottom: 1px solid #e2e8f0;
168
- }
169
-
170
- .card-header h2 {
171
- font-size: 1.2rem;
172
- color: #2d3748;
173
- display: flex;
174
- align-items: center;
175
- gap: 8px;
176
- }
177
-
178
- .card-body {
179
- padding: 20px;
180
- }
181
-
182
- .form-group {
183
- margin-bottom: 20px;
184
- }
185
-
186
- .form-group label {
187
- display: block;
188
- margin-bottom: 8px;
189
- }
190
-
191
- textarea {
192
- width: 100%;
193
- padding: 12px;
194
- border: 2px solid #e2e8f0;
195
- border-radius: 8px;
196
- font-size: 0.95rem;
197
- font-family: inherit;
198
- resize: vertical;
199
- min-height: 100px;
200
- transition: all 0.3s;
201
- }
202
-
203
- textarea:focus {
204
- outline: none;
205
- border-color: #667eea;
206
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
207
- }
208
-
209
- .btn {
210
- padding: 10px 20px;
211
- border: none;
212
- border-radius: 8px;
213
- font-weight: 600;
214
- font-size: 0.95rem;
215
- cursor: pointer;
216
- transition: all 0.3s;
217
- display: inline-flex;
218
- align-items: center;
219
- gap: 8px;
220
- }
221
-
222
- .btn-primary {
223
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
224
- color: white;
225
- }
226
-
227
- .btn-primary:hover {
228
- transform: translateY(-2px);
229
- box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
230
- }
231
-
232
- .btn-secondary {
233
- background: #e2e8f0;
234
- color: #4a5568;
235
- }
236
-
237
- .btn-secondary:hover {
238
- background: #cbd5e0;
239
- }
240
-
241
- .btn:disabled {
242
- opacity: 0.6;
243
- cursor: not-allowed;
244
- transform: none !important;
245
- }
246
-
247
- .output-box {
248
- background: #f7fafc;
249
- border: 2px solid #e2e8f0;
250
- border-radius: 8px;
251
- padding: 15px;
252
- min-height: 150px;
253
- max-height: 400px;
254
- overflow-y: auto;
255
- font-size: 0.95rem;
256
- line-height: 1.6;
257
- white-space: pre-wrap;
258
- word-wrap: break-word;
259
- }
260
-
261
- .output-box.image-output {
262
- text-align: center;
263
- display: flex;
264
- flex-direction: column;
265
- align-items: center;
266
- justify-content: center;
267
- }
268
-
269
- .output-box img {
270
- max-width: 100%;
271
- border-radius: 8px;
272
- margin: 10px 0;
273
- }
274
-
275
- .log-container {
276
- background: #1a202c;
277
- color: #a0aec0;
278
- padding: 20px;
279
- border-radius: 8px;
280
- font-family: 'Courier New', monospace;
281
- font-size: 0.85rem;
282
- max-height: 200px;
283
- overflow-y: auto;
284
- margin-top: 20px;
285
- }
286
-
287
- .log-entry {
288
- margin-bottom: 5px;
289
- }
290
-
291
- .log-time {
292
- color: #4a5568;
293
- }
294
-
295
- .log-success {
296
- color: #48bb78;
297
- }
298
-
299
- .log-error {
300
- color: #f56565;
301
- }
302
-
303
- .log-info {
304
- color: #4299e1;
305
- }
306
-
307
- .log-warning {
308
- color: #f6ad55;
309
- }
310
-
311
- .checkbox-group {
312
- display: flex;
313
- align-items: center;
314
- gap: 8px;
315
- margin: 10px 0;
316
- }
317
-
318
- input[type="checkbox"] {
319
- width: 18px;
320
- height: 18px;
321
- cursor: pointer;
322
- }
323
-
324
- .loading-spinner {
325
- display: inline-block;
326
- width: 16px;
327
- height: 16px;
328
- border: 2px solid #e2e8f0;
329
- border-top-color: #667eea;
330
- border-radius: 50%;
331
- animation: spin 1s linear infinite;
332
- }
333
-
334
- @keyframes spin {
335
- to { transform: rotate(360deg); }
336
- }
337
-
338
- .footer {
339
- background: #f7fafc;
340
- padding: 20px;
341
- text-align: center;
342
- color: #718096;
343
- font-size: 0.9rem;
344
- }
345
-
346
- .footer a {
347
- color: #667eea;
348
- text-decoration: none;
349
- }
350
-
351
- .footer a:hover {
352
- text-decoration: underline;
353
- }
354
-
355
- .mode-toggle {
356
- display: inline-flex;
357
- align-items: center;
358
- gap: 8px;
359
- padding: 6px;
360
- background: #e2e8f0;
361
- border-radius: 8px;
362
- }
363
-
364
- .mode-toggle button {
365
- padding: 6px 12px;
366
- border: none;
367
- background: transparent;
368
- color: #4a5568;
369
- border-radius: 6px;
370
- cursor: pointer;
371
- font-weight: 500;
372
- font-size: 0.9rem;
373
- }
374
-
375
- .mode-toggle button.active {
376
- background: white;
377
- color: #667eea;
378
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
379
- }
380
- </style>
381
  </head>
382
  <body>
383
- <div class="container">
384
- <div class="header">
385
- <h1>🤖 GPT4Free Browser Client</h1>
386
- <p>Run AI models directly in your browser - No API key required!</p>
387
- </div>
388
-
389
- <div class="status-bar">
390
- <div style="display: flex; align-items: center; gap: 12px;">
391
- <div class="status loading" id="status">
392
- <span>Initializing...</span>
393
- </div>
394
- <div class="provider-info" id="providerInfo" style="display: none;">
395
- <span id="currentProvider">Auto</span>
396
- </div>
397
- </div>
398
- <button class="btn btn-secondary" onclick="window.location.reload()">
399
- 🔄 Refresh Page
400
- </button>
401
- </div>
402
-
403
- <div class="controls">
404
- <div class="control-group">
405
- <div class="mode-toggle">
406
- <button id="modeAuto" class="active">Auto Provider</button>
407
- <button id="modeManual">Manual</button>
408
- </div>
409
-
410
- <div id="manualControls" style="display: none; flex: 1; display: flex; gap: 15px; align-items: center;">
411
- <label for="provider">Provider:</label>
412
- <select id="provider">
413
- <option value="">Default (Auto)</option>
414
- <option value="pollinations">Pollinations.AI</option>
415
- <option value="airforce">Api.Airforce</option>
416
- <option value="deepinfra">DeepInfra</option>
417
- <option value="huggingface">HuggingFace</option>
418
- <option value="together">Together</option>
419
- <option value="custom">Custom Endpoint</option>
420
- </select>
421
-
422
- <input type="text" id="customEndpoint" placeholder="https://api.example.com" style="display:none;">
 
 
 
 
 
 
 
 
 
 
423
  </div>
424
-
425
- <label for="apiKey">API Key:</label>
426
- <input type="password" id="apiKey" placeholder="Optional">
427
-
428
- <button class="btn btn-secondary" id="reloadModels">
429
- 🔄 Reload Models
430
- </button>
431
- </div>
432
- </div>
433
-
434
- <div class="main-content">
435
- <div class="card">
436
- <div class="card-header">
437
- <h2>💬 Chat Completion</h2>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  </div>
439
- <div class="card-body">
440
- <div class="form-group">
441
- <label for="chatModel">Model:</label>
442
- <select id="chatModel">
443
- <option>Loading models...</option>
444
- </select>
445
- </div>
446
-
447
- <div class="form-group">
448
- <label for="systemMessage">System Message (Optional):</label>
449
- <textarea id="systemMessage" placeholder="You are a helpful assistant..."></textarea>
450
- </div>
451
-
452
- <div class="form-group">
453
- <label for="userMessage">User Message:</label>
454
- <textarea id="userMessage" placeholder="Ask anything...">Explain quantum computing in simple terms.</textarea>
455
- </div>
456
-
457
- <div class="checkbox-group">
458
- <input type="checkbox" id="streamMode" checked>
459
- <label for="streamMode">Enable streaming</label>
460
- </div>
461
-
462
- <button class="btn btn-primary" id="sendChat">
463
- <span>Send Message</span>
464
- </button>
465
-
466
- <div class="form-group" style="margin-top: 20px;">
467
- <label>Response:</label>
468
- <div class="output-box" id="chatOutput"></div>
469
- </div>
470
  </div>
471
- </div>
472
-
473
- <div class="card">
474
- <div class="card-header">
475
- <h2>🎨 Image Generation</h2>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  </div>
477
- <div class="card-body">
478
- <div class="form-group">
479
- <label for="imageModel">Model:</label>
480
- <select id="imageModel">
481
- <option>Loading models...</option>
482
- </select>
483
- </div>
484
-
485
- <div class="form-group">
486
- <label for="imagePrompt">Prompt:</label>
487
- <textarea id="imagePrompt" placeholder="Describe the image you want...">A serene Japanese garden with cherry blossoms, koi pond, and Mount Fuji in the background, digital art style</textarea>
488
- </div>
489
-
490
- <button class="btn btn-primary" id="generateImage">
491
- <span>Generate Image</span>
492
- </button>
493
-
494
- <div class="form-group" style="margin-top: 20px;">
495
- <label>Output:</label>
496
- <div class="output-box image-output" id="imageOutput">
497
- <span style="color: #718096;">Image will appear here</span>
498
  </div>
499
- </div>
500
  </div>
501
- </div>
502
- </div>
503
-
504
- <div style="padding: 0 30px;">
505
- <div class="log-container" id="logContainer">
506
- <div class="log-entry">
507
- <span class="log-time">[00:00:00]</span>
508
- <span class="log-info"> System ready...</span>
509
  </div>
510
- </div>
511
- </div>
512
-
513
- <div class="footer">
514
- <p>Powered by <a href="https://github.com/xtekky/gpt4free" target="_blank">GPT4Free</a> |
515
- Running entirely in your browser |
516
- <a href="https://g4f.dev" target="_blank">g4f.dev</a></p>
517
  </div>
518
- </div>
519
-
520
- <script type="module">
521
- // Utility functions
522
- const $ = (id) => document.getElementById(id);
523
- const log = (message, type = 'info') => {
524
- const time = new Date().toLocaleTimeString();
525
- const logContainer = $('logContainer');
526
- const entry = document.createElement('div');
527
- entry.className = 'log-entry';
528
- entry.innerHTML = `
529
- <span class="log-time">[${time}]</span>
530
- <span class="log-${type}"> ${message}</span>
531
- `;
532
- logContainer.appendChild(entry);
533
- logContainer.scrollTop = logContainer.scrollHeight;
534
- console.log(`[${type}] ${message}`);
535
- };
536
-
537
- const setStatus = (text, type = 'loading') => {
538
- const status = $('status');
539
- status.className = `status ${type}`;
540
- status.innerHTML = `<span>${text}</span>`;
541
- };
542
-
543
- // Global variables
544
- let client = null;
545
- let manualClient = null;
546
- let currentModels = [];
547
- let isAutoMode = true;
548
-
549
- // Fallback models if API fails
550
- const FALLBACK_MODELS = {
551
- chat: [
552
- 'gpt-4.1', 'gpt-4o', 'gpt-4o-mini', 'gpt-4o-audio',
553
- 'gpt-5', 'gpt-5-nano', 'gpt-5-flux',
554
- 'deepseek-r1', 'deepseek-v3',
555
- 'llama-fast-roblox', 'llama-roblox', 'llama-4-scout',
556
- 'mistral-nemo-roblox', 'mistral-roblox', 'mistral-small-3.1-24b',
557
- 'qwen-2.5-coder-32b', 'glm', 'nova-fast',
558
- 'bidara', 'evil', 'hypnosis-tracy', 'midijourney', 'mirexa', 'rtist', 'sur', 'unity'
559
- ],
560
- image: [
561
- 'flux', 'flux-kontext', 'flux-schnell', 'sdxl-turbo',
562
- 'imagen-4', 'imagen-4-ultra', 'dalle-3'
563
- ]
564
- };
565
-
566
- // Import and initialize client
567
- async function initializeClient() {
568
- try {
569
- setStatus('Loading client library...', 'loading');
570
- log('Importing GPT4Free client from CDN...');
571
-
572
- // Import with timeout
573
- const importPromise = import('https://g4f.dev/dist/js/client.js');
574
- const timeoutPromise = new Promise((_, reject) =>
575
- setTimeout(() => reject(new Error('Import timeout')), 10000)
576
- );
577
-
578
- const module = await Promise.race([importPromise, timeoutPromise]);
579
-
580
- // Use default Client for auto provider selection
581
- const Client = module.default || module.Client;
582
-
583
- if (!Client) {
584
- throw new Error('Client class not found in module');
585
- }
586
-
587
- // Store client classes for manual mode
588
- window.ClientClasses = {
589
- Client,
590
- PollinationsAI: module.PollinationsAI || Client,
591
- DeepInfra: module.DeepInfra || Client,
592
- HuggingFace: module.HuggingFace || Client,
593
- Together: module.Together || Client
594
- };
595
-
596
- // Create default client with no specific provider (uses AnyProvider internally)
597
- client = new Client();
598
-
599
- log('Client library loaded successfully', 'success');
600
- await loadModels();
601
-
602
- } catch (error) {
603
- log(`Failed to load client: ${error.message}`, 'error');
604
- setStatus('Failed to initialize', 'error');
605
-
606
- // Try fallback initialization
607
- log('Using fallback models...', 'info');
608
- loadFallbackModels();
609
- }
610
- }
611
-
612
- // Create manual client for specific provider
613
- async function createManualClient() {
614
- const provider = $('provider').value;
615
- const apiKey = $('apiKey').value.trim();
616
- const customEndpoint = $('customEndpoint').value.trim();
617
-
618
- if (!provider) {
619
- manualClient = null;
620
- return;
621
- }
622
-
623
- try {
624
- log(`Creating ${provider} client...`, 'info');
625
- const options = apiKey ? { apiKey } : {};
626
-
627
- switch (provider) {
628
- case 'pollinations':
629
- manualClient = new window.ClientClasses.PollinationsAI(options);
630
- break;
631
- case 'airforce':
632
- options.baseUrl = 'https://api.airforce/v1';
633
- manualClient = new window.ClientClasses.Client(options);
634
- break;
635
- case 'deepinfra':
636
- manualClient = new window.ClientClasses.DeepInfra(options);
637
- break;
638
- case 'huggingface':
639
- manualClient = new window.ClientClasses.HuggingFace(options);
640
- break;
641
- case 'together':
642
- manualClient = new window.ClientClasses.Together(options);
643
- break;
644
- case 'custom':
645
- if (customEndpoint) {
646
- options.baseUrl = customEndpoint;
647
- }
648
- manualClient = new window.ClientClasses.Client(options);
649
- break;
650
- }
651
-
652
- log(`${provider} client created`, 'success');
653
-
654
- } catch (error) {
655
- log(`Failed to create ${provider} client: ${error.message}`, 'error');
656
- manualClient = null;
657
- }
658
- }
659
-
660
- // Load models from API
661
- async function loadModels() {
662
- setStatus('Loading models...', 'loading');
663
- log('Fetching available models...', 'info');
664
-
665
- try {
666
- // Use the appropriate client
667
- const activeClient = (isAutoMode || !manualClient) ? client : manualClient;
668
-
669
- if (!activeClient) {
670
- throw new Error('No client available');
671
- }
672
-
673
- // Try to load models with timeout
674
- const modelsPromise = activeClient.models.list();
675
- const timeoutPromise = new Promise((_, reject) =>
676
- setTimeout(() => reject(new Error('Model loading timeout')), 5000)
677
- );
678
-
679
- const models = await Promise.race([modelsPromise, timeoutPromise]);
680
-
681
- if (!models || !Array.isArray(models) || models.length === 0) {
682
- throw new Error('No models returned from API');
683
- }
684
-
685
- currentModels = models;
686
- log(`Loaded ${models.length} models`, 'success');
687
-
688
- populateModelSelects(models);
689
- setStatus('Ready', 'ready');
690
-
691
- } catch (error) {
692
- log(`Failed to load models: ${error.message}`, 'error');
693
- log('Using fallback models...', 'warning');
694
- loadFallbackModels();
695
- }
696
- }
697
-
698
- // Load fallback models
699
- function loadFallbackModels() {
700
- const chatSelect = $('chatModel');
701
- const imageSelect = $('imageModel');
702
-
703
- // Populate chat models
704
- chatSelect.innerHTML = '';
705
- FALLBACK_MODELS.chat.forEach(model => {
706
- const option = document.createElement('option');
707
- option.value = model;
708
- option.textContent = model;
709
- chatSelect.appendChild(option);
710
- });
711
-
712
- // Populate image models
713
- imageSelect.innerHTML = '';
714
- FALLBACK_MODELS.image.forEach(model => {
715
- const option = document.createElement('option');
716
- option.value = model;
717
- option.textContent = model;
718
- imageSelect.appendChild(option);
719
- });
720
-
721
- setStatus('Ready (Fallback)', 'ready');
722
- log('Fallback models loaded', 'success');
723
- }
724
-
725
- // Populate model select elements
726
- function populateModelSelects(models) {
727
- const chatSelect = $('chatModel');
728
- const imageSelect = $('imageModel');
729
-
730
- const chatModels = [];
731
- const imageModels = [];
732
-
733
- models.forEach(model => {
734
- if (model.type === 'image') {
735
- imageModels.push(model);
736
- } else {
737
- chatModels.push(model);
738
- }
739
- });
740
-
741
- // Populate chat models
742
- chatSelect.innerHTML = '';
743
- if (chatModels.length > 0) {
744
- chatModels.forEach(model => {
745
- const option = document.createElement('option');
746
- option.value = model.id;
747
- option.textContent = model.id;
748
- chatSelect.appendChild(option);
749
- });
750
- } else {
751
- chatSelect.innerHTML = '<option>No chat models available</option>';
752
- }
753
-
754
- // Populate image models
755
- imageSelect.innerHTML = '';
756
- if (imageModels.length > 0) {
757
- imageModels.forEach(model => {
758
- const option = document.createElement('option');
759
- option.value = model.id;
760
- option.textContent = model.id;
761
- imageSelect.appendChild(option);
762
- });
763
- } else {
764
- imageSelect.innerHTML = '<option>No image models available</option>';
765
- }
766
- }
767
-
768
- // Send chat message
769
- async function sendChatMessage() {
770
- const model = $('chatModel').value;
771
- const systemMessage = $('systemMessage').value.trim();
772
- const userMessage = $('userMessage').value.trim();
773
- const streaming = $('streamMode').checked;
774
-
775
- if (!userMessage) {
776
- log('User message is empty', 'error');
777
- return;
778
- }
779
-
780
- const outputEl = $('chatOutput');
781
- outputEl.textContent = 'Generating...';
782
-
783
- const messages = [];
784
- if (systemMessage) {
785
- messages.push({ role: 'system', content: systemMessage });
786
- }
787
- messages.push({ role: 'user', content: userMessage });
788
-
789
- log(`Sending chat request (model: ${model}, mode: ${isAutoMode ? 'auto' : 'manual'})`, 'info');
790
-
791
- try {
792
- // Use manual client if in manual mode, otherwise use auto client
793
- const activeClient = (isAutoMode || !manualClient) ? client : manualClient;
794
-
795
- if (!activeClient) {
796
- throw new Error('No client available');
797
- }
798
-
799
- // Create request options
800
- const requestOptions = {
801
- model: model,
802
- messages: messages,
803
- stream: streaming,
804
- temperature: 0.7
805
- };
806
-
807
- // In auto mode, explicitly use AnyProvider
808
- if (isAutoMode) {
809
- requestOptions.provider = 'AnyProvider';
810
- log('Using AnyProvider for automatic provider selection', 'info');
811
- }
812
-
813
- const response = await activeClient.chat.completions.create(requestOptions);
814
-
815
- if (streaming) {
816
- let fullResponse = '';
817
- for await (const chunk of response) {
818
- const delta = chunk.choices?.[0]?.delta?.content || '';
819
- fullResponse += delta;
820
- outputEl.textContent = fullResponse;
821
- }
822
- log('Chat streaming completed', 'success');
823
- } else {
824
- const content = response.choices?.[0]?.message?.content || 'No response';
825
- outputEl.textContent = content;
826
- log('Chat response received', 'success');
827
- }
828
-
829
- // Try to detect which provider was used (if available in response)
830
- if (response?.provider) {
831
- $('currentProvider').textContent = response.provider;
832
- $('providerInfo').style.display = 'inline-flex';
833
- }
834
-
835
- } catch (error) {
836
- log(`Chat error: ${error.message}`, 'error');
837
- outputEl.textContent = `Error: ${error.message}`;
838
-
839
- // If error in auto mode, suggest trying manual mode
840
- if (isAutoMode) {
841
- outputEl.textContent += '\n\nTip: Try switching to Manual mode and select a specific provider.';
842
- }
843
- }
844
- }
845
-
846
- // Generate image
847
- async function generateImage() {
848
- const model = $('imageModel').value;
849
- const prompt = $('imagePrompt').value.trim();
850
-
851
- if (!prompt) {
852
- log('Image prompt is empty', 'error');
853
- return;
854
- }
855
-
856
- const outputEl = $('imageOutput');
857
- outputEl.innerHTML = '<div class="loading-spinner"></div> Generating image...';
858
-
859
- log(`Generating image (model: ${model}, mode: ${isAutoMode ? 'auto' : 'manual'})`, 'info');
860
-
861
- try {
862
- // Use manual client if in manual mode, otherwise use auto client
863
- const activeClient = (isAutoMode || !manualClient) ? client : manualClient;
864
-
865
- if (!activeClient) {
866
- throw new Error('No client available');
867
- }
868
-
869
- // Create request options
870
- const requestOptions = {
871
- model: model,
872
- prompt: prompt,
873
- response_format: 'url'
874
- };
875
-
876
- // In auto mode, explicitly use AnyProvider
877
- if (isAutoMode) {
878
- requestOptions.provider = 'AnyProvider';
879
- log('Using AnyProvider for automatic provider selection', 'info');
880
- }
881
-
882
- const response = await activeClient.images.generate(requestOptions);
883
-
884
- // Handle various response formats
885
- let imageUrl;
886
-
887
- if (response && response.data && Array.isArray(response.data) && response.data.length > 0) {
888
- imageUrl = response.data[0].url || response.data[0].image;
889
- } else if (response && response.url) {
890
- imageUrl = response.url;
891
- } else if (response && response.image) {
892
- imageUrl = response.image;
893
- } else if (typeof response === 'string') {
894
- imageUrl = response;
895
- }
896
-
897
- if (!imageUrl) {
898
- log(`Full response: ${JSON.stringify(response)}`, 'info');
899
- throw new Error('No image URL found in response');
900
- }
901
-
902
- // Display the image
903
- outputEl.innerHTML = `
904
- <img src="${imageUrl}" alt="${prompt}" onerror="this.onerror=null; this.src='data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgZmlsbD0iI2VlZSIvPjx0ZXh0IHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjIwMCIgeT0iMTUwIiBzdHlsZT0iZmlsbDojYWFhO2ZvbnQtd2VpZ2h0OmJvbGQ7Zm9udC1zaXplOjE5cHg7Zm9udC1mYW1pbHk6QXJpYWwsSGVsdmV0aWNhLHNhbnMtc2VyaWY7ZG9taW5hbnQtYmFzZWxpbmU6Y2VudHJhbCI+SW1hZ2UgTG9hZCBFcnJvcjwvdGV4dD48L3N2Zz4=';" />
905
- <a href="${imageUrl}" target="_blank" style="color: #667eea; margin-top: 10px; display: block;">
906
- Open in new tab
907
- </a>
908
- `;
909
-
910
- log('Image generated successfully', 'success');
911
-
912
- // Try to detect which provider was used
913
- if (response?.provider) {
914
- $('currentProvider').textContent = response.provider;
915
- $('providerInfo').style.display = 'inline-flex';
916
- }
917
-
918
- } catch (error) {
919
- log(`Image generation error: ${error.message}`, 'error');
920
- outputEl.innerHTML = `<span style="color: #f56565;">Error: ${error.message}</span>`;
921
-
922
- if (isAutoMode) {
923
- outputEl.innerHTML += `<br><small style="color: #718096;">Tip: Try switching to Manual mode and select a specific provider.</small>`;
924
- }
925
- }
926
- }
927
-
928
- // Mode toggle handlers
929
- $('modeAuto').addEventListener('click', () => {
930
- isAutoMode = true;
931
- $('modeAuto').classList.add('active');
932
- $('modeManual').classList.remove('active');
933
- $('manualControls').style.display = 'none';
934
- $('providerInfo').style.display = 'inline-flex';
935
- $('currentProvider').textContent = 'Auto (AnyProvider)';
936
- log('Switched to Auto Provider mode (using AnyProvider)', 'info');
937
- });
938
-
939
- $('modeManual').addEventListener('click', () => {
940
- isAutoMode = false;
941
- $('modeManual').classList.add('active');
942
- $('modeAuto').classList.remove('active');
943
- $('manualControls').style.display = 'flex';
944
- $('providerInfo').style.display = 'none';
945
- log('Switched to Manual Provider mode', 'info');
946
- createManualClient();
947
- });
948
-
949
- // Event listeners
950
- $('provider').addEventListener('change', async (e) => {
951
- const isCustom = e.target.value === 'custom';
952
- $('customEndpoint').style.display = isCustom ? 'block' : 'none';
953
- if (!isAutoMode) {
954
- await createManualClient();
955
- await loadModels();
956
- }
957
- });
958
-
959
- $('apiKey').addEventListener('change', () => {
960
- // Recreate client when API key changes
961
- if (isAutoMode) {
962
- initializeClient();
963
- } else {
964
- createManualClient();
965
- }
966
- });
967
-
968
- $('reloadModels').addEventListener('click', () => loadModels());
969
- $('sendChat').addEventListener('click', () => sendChatMessage());
970
- $('generateImage').addEventListener('click', () => generateImage());
971
-
972
- // Handle Enter key in textareas
973
- $('userMessage').addEventListener('keydown', (e) => {
974
- if (e.key === 'Enter' && !e.shiftKey) {
975
- e.preventDefault();
976
- sendChatMessage();
977
- }
978
- });
979
-
980
- // Initialize on page load
981
- window.addEventListener('DOMContentLoaded', () => {
982
- log('Page loaded, initializing...', 'info');
983
- $('currentProvider').textContent = 'Auto (AnyProvider)';
984
- $('providerInfo').style.display = 'inline-flex';
985
- initializeClient();
986
- });
987
- </script>
988
  </body>
989
  </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="en" data-framework="javascript">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <meta name="description" content="A conversational AI system that listens, learns, and challenges">
8
+ <meta property="og:title" content="G4F Chat">
9
+ <meta property="og:image" content="https://g4f.dev/dist/img/android-chrome-512x512.png">
10
+ <meta property="og:description" content="A conversational AI system that listens, learns, and challenges">
11
+ <meta property="og:url" content="https://g4f.dev">
12
+ <link rel="stylesheet" href="https://g4f.dev/dist/css/style.css">
13
+ <link rel="stylesheet" href="https://g4f.dev/dist/css/all.min.css">
14
+ <link rel="apple-touch-icon" sizes="180x180" href="https://g4f.dev/dist/img/apple-touch-icon.png">
15
+ <link rel="icon" type="image/png" sizes="32x32" href="https://g4f.dev/dist/img/favicon-32x32.png">
16
+ <link rel="icon" type="image/png" sizes="16x16" href="https://g4f.dev/dist/img/favicon-16x16.png">
17
+ <link rel="manifest" href="https://g4f.dev/dist/img/site.webmanifest">
18
+ <script src="https://g4f.dev/dist/js/framework.js"></script>
19
+ <script src="https://g4f.dev/dist/js/chat.v1.js?v=1.1" defer></script>
20
+ <script src="https://g4f.dev/dist/js/recorder.js" async></script>
21
+ <script src="https://g4f.dev/dist/js/highlight.min.js" async></script>
22
+ <script src="https://g4f.dev/dist/js/highlightjs-copy.min.js" async></script>
23
+ <script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
24
+ <script src="https://cdn.jsdelivr.net/npm/jsencrypt/bin/jsencrypt.min.js"></script>
25
+ <script src="https://g4f.dev/dist/js/sanitize-html.js" async></script>
26
+ <script type="module" async>
27
+ import { Client, Custom, PollinationsAI, DeepInfra, Puter, HuggingFace } from "https://g4f.dev/dist/js/client.js";
28
+ window.providers = {
29
+ Azure: Client,
30
+ PollinationsAI,
31
+ ApiAirforce: Client,
32
+ DeepInfra,
33
+ Puter,
34
+ HuggingFace,
35
+ Custom
36
+ };
37
+ </script>
38
+ <link rel="stylesheet" href="https://g4f.dev/dist/css/dracula.min.css">
39
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe/dist/photoswipe.css">
40
+ <script>
41
+ MathJax = {
42
+ chtml: {
43
+ scale: 1,
44
+ displayAlign: 'left'
45
+ },
46
+ tex: {
47
+ inlineMath: [['$', '$'], ['\\(', '\\)']],
48
+ displayMath: [['$$', '$$'], ['\\[', '\\]']]
49
+ },
50
+ };
51
+ </script>
52
+ <script id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" async></script>
53
+ <script>
54
+ var tag = document.createElement('script');
55
+ tag.src = "https://www.youtube.com/iframe_api";
56
+ var firstScriptTag = document.getElementsByTagName('script')[0];
57
+ firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
58
+ </script>
59
+ <template>
60
+ <script type="module" src="https://cdn.jsdelivr.net/npm/mistral-tokenizer-js" async>
61
+ import mistralTokenizer from "mistral-tokenizer-js"
62
+ </script>
63
+ <script type="module" src="https://cdn.jsdelivr.net/gh/belladoreai/llama-tokenizer-js@master/llama-tokenizer.js" async>
64
+ import llamaTokenizer from "llama-tokenizer-js"
65
+ </script>
66
+ <script src="https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/cl100k_base.js" async></script>
67
+ <script src="https://cdn.jsdelivr.net/npm/gpt-tokenizer/dist/o200k_base.js" async></script>
68
+ </template>
69
+ <script type="module" src="https://g4f.dev/dist/js/photoswipe.js" async></script>
70
+ <script async>
71
+ if (localStorage.getItem("countTokens") != "false") {
72
+ const template = document.head.querySelector('template');
73
+ document.head.appendChild(template.content);
74
+ template.remove();
75
+ }
76
+ </script>
77
+ <script>
78
+ const user_image = '<img src="https://g4f.dev/dist/img/user.png" alt="your avatar">';
79
+ const gpt_image = '<img src="https://g4f.dev/dist/img/gpt.png" alt="your avatar">';
80
+ window.conversation_id = "";
81
+ </script>
82
+ <title>G4F Chat</title>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  </head>
84
  <body>
85
+ <script async>
86
+ localStorage.getItem("darkMode") == "false" ? document.body.classList.add("white") : null;
87
+ </script>
88
+ <div class="media-overlay"></div>
89
+ <div class="gradient"></div>
90
+ <div class="container">
91
+ <div class="sidebar">
92
+ <div class="sidebar-container">
93
+ <div class="sidebar-header">
94
+ <div class="sidebar-logo" data-translate="true">G4F Chat</div>
95
+ <div class="mobile-sidebar-toggle">
96
+ <i class="fa-solid fa-bars"></i>
97
+ </div>
98
+ </div>
99
+ <div class="top">
100
+ <button class="new_convo" onclick="new_conversation()">
101
+ <i class="fa-regular fa-plus"></i>
102
+ <span>New Conversation</span>
103
+ </button>
104
+ <button class="new_convo" onclick="new_conversation(true)">
105
+ <i class="fa-solid fa-user-secret"></i>
106
+ <span>Private Conversation</span>
107
+ </button>
108
+ </div>
109
+ <div class="bottom_buttons">
110
+ <button onclick="open_settings();">
111
+ <i class="fa-solid fa-gear"></i>
112
+ <span>Open Settings</span>
113
+ </button>
114
+ <div class="info">
115
+ <i class="fa fa-question-circle"></i>
116
+ <span class="convo-title">support ~ <a href="https://discord.gg/qXA4Wf4Fsm" target="_blank">discord.gg/qXA4Wf4Fsm</a>
117
+ </span>
118
+ </div>
119
+ <div class="info">
120
+ <i class="fa-brands fa-discord"></i>
121
+ <span class="convo-title">discord ~ <a href="https://discord.gg/5E39JUWUFa" target="_blank">discord.gg/5E39JUWUFa</a>
122
+ </span>
123
+ </div>
124
+ <div class="info">
125
+ <i class="fa-brands fa-github"></i>
126
+ <span class="convo-title">github ~ <a href="https://github.com/xtekky/gpt4free" target="_blank">@xtekky/gpt4free</a>
127
+ </span>
128
+ </div>
129
+ <div class="info">
130
+ <i class="fa-solid fa-star"></i>
131
+ <span id="version_text" class="convo-title"></span>
132
+ </div>
133
+ </div>
134
+ </div>
135
  </div>
136
+ <div class="settings hidden">
137
+ <div class="paper">
138
+ <div class="settings-top-bar">
139
+ <button class="settings-back-button" onclick="open_settings();">
140
+ <i class="fa-solid fa-arrow-left"></i>
141
+ </button>
142
+ <span>Settings</span>
143
+ </div>
144
+ <div class="field">
145
+ <span class="label">Enable Dark Mode</span>
146
+ <input type="checkbox" id="darkMode" checked />
147
+ <label for="darkMode" class="toogle" title=""></label>
148
+ </div>
149
+ <div class="field">
150
+ <span class="label">Web Access with DuckDuckGo</span>
151
+ <input type="checkbox" id="switch" />
152
+ <label for="switch" class="toogle" title="Add the pages of the first 5 search results to the query."></label>
153
+ </div>
154
+ <div class="field">
155
+ <span class="label">Disable Conversation History</span>
156
+ <input type="checkbox" id="history" />
157
+ <label for="history" class="toogle" title="To improve the reaction time or if you have trouble with large conversations."></label>
158
+ </div>
159
+ <div class="field">
160
+ <span class="label">Hide System-prompt</span>
161
+ <input type="checkbox" id="hide-systemPrompt" />
162
+ <label for="hide-systemPrompt" class="toogle" title="For more space on phones"></label>
163
+ </div>
164
+ <div class="field">
165
+ <span class="label">Download generated media</span>
166
+ <input type="checkbox" id="download_media" checked/>
167
+ <label for="download_media" class="toogle" title="Download and save generated images, audios and videos"></label>
168
+ </div>
169
+ <div class="field">
170
+ <span class="label">Refine files with spaCy</span>
171
+ <input type="checkbox" id="refine"/>
172
+ <label for="refine" class="toogle" title=""></label>
173
+ </div>
174
+ <div class="field">
175
+ <span class="label">Report errors</span>
176
+ <input type="checkbox" id="report_error" checked/>
177
+ <label for="report_error" class="toogle" title=""></label>
178
+ </div>
179
+ <div class="field">
180
+ <span class="label">Count words and tokens</span>
181
+ <input type="checkbox" id="countTokens" checked/>
182
+ <label for="countTokens" class="toogle" title=""></label>
183
+ </div>
184
+ <div class="field">
185
+ <span class="label">Automatic Orientation (16:9 or 9:16)</span>
186
+ <input type="checkbox" id="automaticOrientation" checked/>
187
+ <label for="automaticOrientation" class="toogle" title=""></label>
188
+ </div>
189
+ <div class="field box">
190
+ <label for="systemPrompt" class="label">System prompt</label>
191
+ <textarea id="systemPrompt" placeholder="You are a helpful assistant." data-example="If you need to generate images, you can use the following format: ![keywords](/generate/filename.jpg). This will enable the use of an image generation tool."></textarea>
192
+ </div>
193
+ <div class="field box">
194
+ <label for="userInput-height" class="label" title="">Input max. height</label>
195
+ <input type="number" id="userInput-height" value="200"/>
196
+ </div>
197
+ <div class="field box">
198
+ <label for="recognition-language" class="label" title="">Speech recognition language</label>
199
+ <input type="text" id="recognition-language" value="" placeholder="navigator.language"/>
200
+ </div>
201
+ <div class="field mem0 hidden">
202
+ <span class="label">Enable Memory with Mem0</span>
203
+ <input type="checkbox" id="mem0"/>
204
+ <label for="mem0" class="toogle" title=""></label>
205
+ <button onclick="import_memory()">
206
+ <i class="fa-solid fa-arrow-up-from-bracket"></i>
207
+ <span>Import Messages to Mem0</span>
208
+ </button>
209
+ </div>
210
+ <div class="field box hidden">
211
+ <label for="mem0-api_key" class="label" title="">Mem0 API:</label>
212
+ <input type="text" id="mem0-api_key" name="mem0[api_key]" placeholder="api_key"/>
213
+ </div>
214
+ <div class="field box hidden">
215
+ <label for="Custom-api_base" class="label" title="">Custom Provider (Base Url):</label>
216
+ <input type="text" id="Custom-api_base" name="Custom[api_base]" placeholder="http://localhost:8080/v1"/>
217
+ </div>
218
+ <div class="field box hidden">
219
+ <label for="Custom-api_key" class="label" title="">Custom Provider:</label>
220
+ <input type="text" id="Custom-api_key" name="Custom[api_key]" placeholder="api_key"/>
221
+ </div>
222
+ <div class="field box hidden">
223
+ <label for="BingCreateImages-api_key" class="label" title="">Microsoft Designer in Bing:</label>
224
+ <input type="text" id="BingCreateImages-api_key" name="BingCreateImages[api_key]" placeholder="&quot;_U&quot; cookie"/>
225
+ </div>
226
+ </div>
227
+ <div class="bottom_buttons">
228
+ <button onclick="delete_conversations()">
229
+ <i class="fa-solid fa-trash"></i>
230
+ <span>Clear Conversations</span>
231
+ </button>
232
+ <button onclick="save_storage()">
233
+ <i class="fa-solid fa-download"></i>
234
+ <a href="" onclick="return false;">Export Conversations</a>
235
+ </button>
236
+ <button onclick="save_storage(true)">
237
+ <i class="fa-solid fa-pencil"></i>
238
+ <a href="" onclick="return false;">Export Settings</a>
239
+ </button>
240
+ <button onclick="this.querySelector('.fa-spin').classList.remove('hidden'); framework.translateAll().then(()=>{window.location.reload()}).catch(()=>{this.querySelector('.fa-spin').classList.add('hidden')})">
241
+ <i class="fa-solid fa-language"></i>
242
+ <a href="" onclick="return false;">Translate UI</a>
243
+ <i class="fas fa-spinner fa-spin hidden"></i>
244
+ </button>
245
+ <button id="showLog">
246
+ <i class="fa-solid fa-terminal"></i>
247
+ <a href="" onclick="return false;">Show log</a>
248
+ </button>
249
+ </div>
250
  </div>
251
+ <div class="provider_forms hidden">
252
+ <div class="bottom_buttons">
253
+ <button id="close_provider_forms">
254
+ <i class="fa-regular fa-circle-xmark"></i>
255
+ <a href="" onclick="return false;">Close</a>
256
+ </button>
257
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  </div>
259
+ <div class="chat-container">
260
+ <div class="chat-top-panel">
261
+ <div class="mobile-sidebar-toggle">
262
+ <i class="fa-solid fa-bars"></i>
263
+ </div>
264
+ <div class="convo-title">New Conversation</div>
265
+ <button class="new_convo_icon" onclick="new_conversation()">
266
+ <i class="fa-regular fa-plus"></i>
267
+ </button>
268
+ </div>
269
+ <textarea id="chatPrompt" class="box" placeholder="System prompt"></textarea>
270
+ <button class="slide-header">
271
+ <i class="fa-solid fa-angles-up"></i>
272
+ </button>
273
+ <div class="chat-body" id="chatBody"></div>
274
+ <div class="chat-footer">
275
+ <div class="chat-toolbar">
276
+ <div id="input-count" class="">
277
+ <button class="hide-input">
278
+ <i class="fa-solid fa-angles-down"></i>
279
+ </button>
280
+ <label for="agree" class="text" onclick="this.innerText='';"></label>
281
+ </div>
282
+ <div class="stop_generating stop_generating-hidden">
283
+ <button id="cancelButton">
284
+ <span>Stop Generating</span>
285
+ <i class="fa-solid fa-stop"></i>
286
+ </button>
287
+ </div>
288
+ <div class="regenerate">
289
+ <button id="regenerateButton">
290
+ <span>Regenerate</span>
291
+ <i class="fa-solid fa-rotate"></i>
292
+ </button>
293
+ </div>
294
+ </div>
295
+ <div class="media-player">
296
+ <i class="fa-regular fa-x"></i>
297
+ </div>
298
+ <div class="media-select hidden">
299
+ <label class="image-select" for="image" title="">
300
+ <input type="file" id="image" name="image" accept="image/*" required/>
301
+ <i class="fa-regular fa-image"></i>
302
+ </label>
303
+ <label class="capture-camera" for="camera">
304
+ <input type="file" id="camera" name="camera" accept="image/*" capture="camera" required/>
305
+ <i class="fa-solid fa-camera"></i>
306
+ </label>
307
+ <label class="capture-audio" for="audio">
308
+ <i class="fa-solid fa-microphone"></i>
309
+ </label>
310
+ <label class="add-link" for="link">
311
+ <i class="fa-solid fa-link"></i>
312
+ </label>
313
+ <button class="close">
314
+ <i class="fa-solid fa-xmark"></i>
315
+ </button>
316
+ </div>
317
+ <div class="user-input">
318
+ <div class="input-area">
319
+ <a id="download"></a><!--Download Button -->
320
+ <textarea id="userInput" class="box" placeholder="Type a message..." cols="30" rows="10"
321
+ style="white-space: pre-wrap;resize: none;"></textarea>
322
+ <label class="file-label image-label">
323
+ <i class="fa-brands fa-usb"></i>
324
+ </label>
325
+ <label class="file-label" for="file">
326
+ <input type="file" id="file" name="file" accept="*/*" required multiple/>
327
+ <i class="fa-solid fa-paperclip"></i>
328
+ </label>
329
+ <label class="micro-label" for="micro">
330
+ <i class="fa-solid fa-microphone-slash"></i>
331
+ </label>
332
+ <button class="code" title="Insert code block">
333
+ <i class="fa-solid fa-code"></i>
334
+ </button>
335
+ <div class="send-buttons">
336
+ <button id="addButton" aria-label="Add message">
337
+ <i class="fa-solid fa-square-plus" aria-hidden="true"></i>
338
+ <span>Add</span>
339
+ </button>
340
+
341
+ <button id="sendButton" aria-label="Send message">
342
+ <i class="fa-regular fa-paper-plane" aria-hidden="true"></i>
343
+ <span>Send</span>
344
+ </button>
345
+ </div>
346
+ </div>
347
+ </div>
348
+ <div class="chat-buttons">
349
+ <div class="field">
350
+ <button id="search">
351
+ <i class="fa-solid fa-search"></i>
352
+ </button>
353
+ </div>
354
+ <div class="field">
355
+ <select name="model" id="model">
356
+ <option value="" selected="selected">Model: Default</option>
357
+ </select>
358
+ <select name="model2" id="model2" class="hidden model"></select>
359
+ <div class="model-selector hidden">
360
+ <div id="model-suggestions" class="suggestions-dropdown"></div>
361
+ <input
362
+ type="text"
363
+ id="model-search"
364
+ placeholder="Search models (e.g. 'gpt-4', 'claude')..."
365
+ autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false"
366
+ >
367
+ </div>
368
+ </div>
369
+ <div class="field">
370
+ <button id="model_edit" class="">
371
+ <i class="fa-solid fa-edit"></i>
372
+ </button>
373
+ </div>
374
+ <div class="field">
375
+ <select name="provider" id="provider">
376
+ <option value="AnyProvider">Provider: Auto</option>
377
+ </select>
378
+ </div>
379
+ <div class="field">
380
+ <button id="pin">
381
+ <i class="fa-solid fa-thumbtack"></i>
382
+ </button>
383
+ </div>
384
+ <div id="pin_container" class="field"></div>
385
+ </div>
386
+ </div>
387
  </div>
388
+ <div class="log hidden">
389
+ <div class="log-top-bar">
390
+ <button class="log-back-button" onclick="open_settings();">
391
+ <i class="fa-solid fa-arrow-left"></i>
392
+ </button>
393
+ <span>Logs</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  </div>
 
395
  </div>
396
+ <div class="hljs-iframe-container hidden">
397
+ <iframe class="hljs-iframe"></iframe>
 
 
 
 
 
 
398
  </div>
 
 
 
 
 
 
 
399
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  </body>
401
  </html>