berhasil sblmnya claude
Browse files- index.html +387 -975
index.html
CHANGED
@@ -1,989 +1,401 @@
|
|
1 |
<!DOCTYPE html>
|
2 |
-
<html lang="en">
|
3 |
<head>
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
.
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
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 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
423 |
</div>
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
438 |
</div>
|
439 |
-
<div class="
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
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 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
476 |
</div>
|
477 |
-
<div class="
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
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 |
-
|
502 |
-
|
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: . 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=""_U" 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>
|