Spaces:
Running
Running
hehe update playground
Browse filesfixed many bugs upgraded the relaiablity :)
- playground.html +385 -319
playground.html
CHANGED
@@ -5,16 +5,17 @@
|
|
5 |
<meta charset="UTF-8">
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
<title>Loki.AI Playground</title>
|
8 |
-
|
9 |
<link
|
10 |
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@300;400;500;600&display=swap"
|
11 |
rel="stylesheet">
|
12 |
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" rel="stylesheet" />
|
13 |
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
14 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
|
15 |
-
|
16 |
<style>
|
17 |
@import url('https://fonts.googleapis.com/css2?family=Encode+Sans:[email protected]&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Protest+Revolution&display=swap');
|
|
|
18 |
:root {
|
19 |
--bg-dark: #0a0a0f;
|
20 |
--bg-darker: #040409;
|
@@ -30,6 +31,7 @@
|
|
30 |
--header-height: 60px;
|
31 |
--input-height: 80px;
|
32 |
}
|
|
|
33 |
* {
|
34 |
margin: 0;
|
35 |
padding: 0;
|
@@ -37,16 +39,20 @@
|
|
37 |
scrollbar-width: thin;
|
38 |
scrollbar-color: var(--primary-blue) transparent;
|
39 |
}
|
|
|
40 |
*::-webkit-scrollbar {
|
41 |
width: 8px;
|
42 |
}
|
|
|
43 |
*::-webkit-scrollbar-track {
|
44 |
background: transparent;
|
45 |
}
|
|
|
46 |
*::-webkit-scrollbar-thumb {
|
47 |
background-color: var(--primary-blue);
|
48 |
border-radius: 20px;
|
49 |
}
|
|
|
50 |
body {
|
51 |
font-family: 'Inter', sans-serif;
|
52 |
background-color: var(--bg-dark);
|
@@ -58,6 +64,7 @@
|
|
58 |
overflow: hidden;
|
59 |
perspective: 2000px;
|
60 |
}
|
|
|
61 |
.chat-wrapper {
|
62 |
position: relative;
|
63 |
width: 100%;
|
@@ -74,6 +81,7 @@
|
|
74 |
transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);
|
75 |
border: 1px solid var(--border-color);
|
76 |
}
|
|
|
77 |
.chat-header {
|
78 |
background-color: var(--bg-deepest);
|
79 |
padding: 15px 20px;
|
@@ -84,12 +92,14 @@
|
|
84 |
flex-shrink: 0;
|
85 |
height: var(--header-height);
|
86 |
}
|
|
|
87 |
.chat-container {
|
88 |
display: none;
|
89 |
flex-direction: column;
|
90 |
height: calc(100% - var(--header-height));
|
91 |
position: relative;
|
92 |
}
|
|
|
93 |
.chat-messages {
|
94 |
flex: 1;
|
95 |
overflow-y: auto;
|
@@ -102,6 +112,7 @@
|
|
102 |
height: calc(100% - var(--input-height));
|
103 |
margin-bottom: var(--input-height);
|
104 |
}
|
|
|
105 |
.chat-input {
|
106 |
position: absolute;
|
107 |
bottom: 0;
|
@@ -116,12 +127,14 @@
|
|
116 |
z-index: 10;
|
117 |
align-items: center;
|
118 |
}
|
|
|
119 |
.chat-input-container {
|
120 |
position: relative;
|
121 |
flex: 1;
|
122 |
display: flex;
|
123 |
align-items: center;
|
124 |
}
|
|
|
125 |
.chat-input input {
|
126 |
width: 100%;
|
127 |
padding: 12px 16px;
|
@@ -135,10 +148,12 @@
|
|
135 |
font-family: 'JetBrains Mono', monospace;
|
136 |
outline: none;
|
137 |
}
|
|
|
138 |
.chat-input input:focus {
|
139 |
border-color: var(--primary-blue);
|
140 |
box-shadow: 0 0 0 3px rgba(74, 108, 247, 0.2);
|
141 |
}
|
|
|
142 |
.message {
|
143 |
max-width: 80%;
|
144 |
width: fit-content;
|
@@ -150,17 +165,20 @@
|
|
150 |
animation: fadeIn 0.4s forwards;
|
151 |
margin-bottom: 8px;
|
152 |
}
|
|
|
153 |
.message.user {
|
154 |
align-self: flex-end;
|
155 |
background-color: var(--primary-blue);
|
156 |
color: white;
|
157 |
}
|
|
|
158 |
.message.bot {
|
159 |
align-self: flex-start;
|
160 |
background-color: rgba(40, 40, 70, 0.8);
|
161 |
color: var(--text-light);
|
162 |
border: 1px solid var(--border-color);
|
163 |
}
|
|
|
164 |
.message::before {
|
165 |
content: '';
|
166 |
position: absolute;
|
@@ -172,9 +190,11 @@
|
|
172 |
opacity: 0;
|
173 |
transition: opacity 0.3s ease;
|
174 |
}
|
|
|
175 |
.message:hover::before {
|
176 |
opacity: 1;
|
177 |
}
|
|
|
178 |
.initial-input {
|
179 |
flex: 1;
|
180 |
display: flex;
|
@@ -185,6 +205,7 @@
|
|
185 |
text-align: center;
|
186 |
background: linear-gradient(145deg, var(--bg-dark), var(--bg-darker));
|
187 |
}
|
|
|
188 |
.initial-input h2 {
|
189 |
font-size: 24px;
|
190 |
margin-bottom: 16px;
|
@@ -192,11 +213,13 @@
|
|
192 |
font-weight: 600;
|
193 |
font-family: 'JetBrains Mono', monospace;
|
194 |
}
|
|
|
195 |
.input-container {
|
196 |
width: 100%;
|
197 |
max-width: 400px;
|
198 |
position: relative;
|
199 |
}
|
|
|
200 |
.initial-input input {
|
201 |
width: 100%;
|
202 |
padding: 16px 24px;
|
@@ -209,6 +232,7 @@
|
|
209 |
outline: none;
|
210 |
transition: all 0.3s ease;
|
211 |
}
|
|
|
212 |
.send-icon {
|
213 |
position: absolute;
|
214 |
right: 12px;
|
@@ -224,55 +248,84 @@
|
|
224 |
cursor: pointer;
|
225 |
transition: all 0.3s ease;
|
226 |
}
|
|
|
227 |
.send-icon:hover {
|
228 |
background-color: var(--accent-color);
|
229 |
transform: translateY(-50%) scale(1.05);
|
230 |
border-radius: 15px;
|
231 |
}
|
|
|
232 |
@keyframes fadeIn {
|
233 |
from {
|
234 |
opacity: 0;
|
235 |
transform: translateY(20px);
|
236 |
}
|
|
|
237 |
to {
|
238 |
opacity: 1;
|
239 |
transform: translateY(0);
|
240 |
}
|
241 |
}
|
|
|
242 |
/* Header actions */
|
243 |
.header-actions {
|
244 |
display: flex;
|
245 |
align-items: center;
|
246 |
gap: 10px;
|
|
|
247 |
}
|
|
|
248 |
.model-select {
|
249 |
background-color: black;
|
250 |
color: var(--text-light);
|
251 |
border: 1px solid var(--border-color);
|
252 |
border-radius: 8px;
|
253 |
-
padding:
|
254 |
font-family: 'JetBrains Mono', monospace;
|
255 |
-
font-size:
|
256 |
cursor: pointer;
|
257 |
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
259 |
.clear-chat {
|
260 |
background-color: var(--delete-red);
|
261 |
color: white;
|
262 |
border: none;
|
263 |
border-radius: 8px;
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
cursor: pointer;
|
268 |
-
transition: all 0.3s ease;
|
269 |
display: flex;
|
270 |
align-items: center;
|
271 |
-
|
|
|
|
|
272 |
}
|
|
|
273 |
.clear-chat:hover {
|
274 |
background-color: #ff6666;
|
275 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
.watermark {
|
277 |
position: absolute;
|
278 |
bottom: 10px;
|
@@ -283,150 +336,190 @@
|
|
283 |
transition: opacity 0.3s ease;
|
284 |
z-index: 11;
|
285 |
}
|
|
|
286 |
.watermark:hover {
|
287 |
opacity: 0.8;
|
288 |
}
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
295 |
}
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
300 |
}
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
overflow-y: auto;
|
306 |
}
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
}
|
311 |
-
|
312 |
-
|
313 |
-
|
|
|
|
|
|
|
314 |
}
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
.
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
}
|
374 |
-
}
|
375 |
-
@media screen and (max-width: 480px) {
|
376 |
-
.chat-wrapper {
|
377 |
-
border-radius: 12px;
|
378 |
-
}
|
379 |
-
.message {
|
380 |
-
max-width: 95%;
|
381 |
-
font-size: 12px;
|
382 |
-
padding: 8px 12px;
|
383 |
-
}
|
384 |
-
.chat-input input {
|
385 |
-
font-size: 12px;
|
386 |
-
}
|
387 |
-
.header-actions {
|
388 |
-
flex-direction: column;
|
389 |
-
gap: 5px;
|
390 |
-
}
|
391 |
-
.clear-chat {
|
392 |
-
width: 50%;
|
393 |
-
}
|
394 |
-
.model-select{
|
395 |
-
width: 20% !important;
|
396 |
-
}
|
397 |
-
}
|
398 |
-
/* Additional responsive adjustments to existing CSS */
|
399 |
-
* {
|
400 |
-
-webkit-tap-highlight-color: transparent;
|
401 |
-
}
|
402 |
-
body {
|
403 |
-
touch-action: manipulation;
|
404 |
-
}
|
405 |
-
input, button {
|
406 |
-
-webkit-appearance: none;
|
407 |
-
-moz-appearance: none;
|
408 |
-
appearance: none;
|
409 |
}
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
border-radius: 15px;
|
415 |
-
}
|
416 |
}
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
}
|
422 |
-
.chat-input input {
|
423 |
-
font-size: 16px; /* Larger font for touch devices */
|
424 |
-
}
|
425 |
}
|
|
|
426 |
</style>
|
427 |
</head>
|
428 |
|
429 |
<body>
|
|
|
|
|
|
|
|
|
|
|
430 |
<div class="chat-wrapper">
|
431 |
<div class="chat-header">
|
432 |
<h2 style="font-family: 'JetBrains Mono', monospace;">LOKI.AI Playground</h2>
|
@@ -435,8 +528,8 @@ input, button {
|
|
435 |
<option value="gpt-4o-mini">GPT-4o Mini</option>
|
436 |
<option value="gpt-4o">GPT-4o</option>
|
437 |
<option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
|
438 |
-
<option value="searchgpt">SearchGPT(
|
439 |
-
<option value="claude-3-haiku">Claude 3 Haiku</option>
|
440 |
<option value="llama-3.1-8b">Llama 3.1 8B</option>
|
441 |
<option value="llama-3.1-70b">Llama 3.1 70B</option>
|
442 |
<option value="llama-3.1-405b">Llama 3.1 405b</option>
|
@@ -447,14 +540,13 @@ input, button {
|
|
447 |
<option value="command">Command</option>
|
448 |
</select>
|
449 |
<button id="clearChatButton" class="clear-chat">
|
450 |
-
<svg xmlns="http://www.w3.org/2000/svg"
|
451 |
-
stroke
|
452 |
<polyline points="3 6 5 6 21 6"></polyline>
|
453 |
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
454 |
<line x1="10" y1="11" x2="10" y2="17"></line>
|
455 |
<line x1="14" y1="11" x2="14" y2="17"></line>
|
456 |
</svg>
|
457 |
-
Clear
|
458 |
</button>
|
459 |
</div>
|
460 |
</div>
|
@@ -496,69 +588,71 @@ input, button {
|
|
496 |
const sendButton = document.getElementById('sendButton');
|
497 |
const modelSelect = document.getElementById('modelSelect');
|
498 |
const clearChatButton = document.getElementById('clearChatButton');
|
499 |
-
|
500 |
let currentStreamingMessage = null;
|
|
|
501 |
let conversationHistory = [];
|
502 |
function scrollToBottom() {
|
503 |
const chatMessages = document.getElementById('chatMessages');
|
504 |
chatMessages.scrollTop = chatMessages.scrollHeight;
|
505 |
}
|
506 |
function appendMessage(content, type = 'bot', isStreaming = false) {
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
|
|
|
|
562 |
function clearChat() {
|
563 |
chatMessages.innerHTML = '';
|
564 |
conversationHistory = [];
|
@@ -566,16 +660,13 @@ input, button {
|
|
566 |
chatContainer.style.display = 'none';
|
567 |
chatWrapper.classList.remove('active');
|
568 |
}
|
569 |
-
|
570 |
async function sendInitialMessage() {
|
571 |
const userMessage = initialChatInput.value.trim();
|
572 |
const selectedModel = modelSelect.value;
|
573 |
if (!userMessage) return;
|
574 |
-
|
575 |
initialInput.style.display = 'none';
|
576 |
chatContainer.style.display = 'flex';
|
577 |
chatWrapper.classList.add('active');
|
578 |
-
|
579 |
appendMessage(userMessage, 'user');
|
580 |
initialChatInput.value = '';
|
581 |
scrollToBottom();
|
@@ -586,15 +677,12 @@ input, button {
|
|
586 |
console.error("API Error:", error);
|
587 |
}
|
588 |
}
|
589 |
-
|
590 |
async function sendMessage() {
|
591 |
const userMessage = chatInput.value.trim();
|
592 |
const selectedModel = modelSelect.value;
|
593 |
if (!userMessage) return;
|
594 |
-
|
595 |
appendMessage(userMessage, 'user');
|
596 |
chatInput.value = '';
|
597 |
-
|
598 |
try {
|
599 |
await callApi(userMessage, selectedModel);
|
600 |
} catch (error) {
|
@@ -602,140 +690,118 @@ input, button {
|
|
602 |
console.error("API Error:", error);
|
603 |
}
|
604 |
}
|
605 |
-
|
606 |
async function callApi(userMessage, model) {
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
.
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
appendMessage(formattedContent, 'bot', true);
|
641 |
-
}
|
642 |
-
} catch (err) {
|
643 |
-
console.warn("Parsing error:", err);
|
644 |
}
|
645 |
-
}
|
|
|
|
|
646 |
}
|
|
|
|
|
|
|
647 |
}
|
648 |
} else {
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
const jsonData = JSON.parse(jsonString);
|
696 |
-
const delta = jsonData.choices?.[0]?.delta || {};
|
697 |
-
let content = delta.content || "";
|
698 |
-
|
699 |
-
// Comprehensive newline conversion
|
700 |
-
content = content
|
701 |
-
.replace(/\r\n/g, '<br>') // Windows-style newlines
|
702 |
-
.replace(/\n/g, '<br>') // Unix/Linux-style newlines
|
703 |
-
.replace(/\r/g, '<br>'); // Old Mac-style newlines
|
704 |
-
|
705 |
-
if (content) {
|
706 |
-
appendMessage(content, 'bot', true);
|
707 |
-
}
|
708 |
-
} catch (err) {
|
709 |
-
console.warn("Parsing error:", err);
|
710 |
}
|
711 |
-
}
|
|
|
|
|
712 |
}
|
|
|
|
|
|
|
713 |
}
|
714 |
-
} else {
|
715 |
-
throw new Error(`API responded with status ${response.status}`);
|
716 |
}
|
717 |
-
|
718 |
-
console.error("API call error:", error);
|
719 |
-
throw error;
|
720 |
}
|
721 |
-
}
|
722 |
-
|
723 |
-
return fullResponse.trim();
|
724 |
-
}
|
725 |
// Event Listeners
|
726 |
initialSendIcon.addEventListener('click', sendInitialMessage);
|
727 |
initialChatInput.addEventListener('keypress', (event) => {
|
728 |
if (event.key === 'Enter') sendInitialMessage();
|
729 |
});
|
730 |
-
|
731 |
sendButton.addEventListener('click', sendMessage);
|
732 |
chatInput.addEventListener('keypress', (event) => {
|
733 |
if (event.key === 'Enter') sendMessage();
|
734 |
});
|
735 |
-
|
736 |
// Clear Chat Button Event Listener
|
737 |
clearChatButton.addEventListener('click', clearChat);
|
738 |
</script>
|
739 |
</body>
|
740 |
-
<!-- random comments to get 800 lines -->
|
741 |
</html>
|
|
|
5 |
<meta charset="UTF-8">
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
<title>Loki.AI Playground</title>
|
8 |
+
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
9 |
<link
|
10 |
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@300;400;500;600&display=swap"
|
11 |
rel="stylesheet">
|
12 |
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" rel="stylesheet" />
|
13 |
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
14 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
|
15 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
|
16 |
<style>
|
17 |
@import url('https://fonts.googleapis.com/css2?family=Encode+Sans:[email protected]&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Protest+Revolution&display=swap');
|
18 |
+
|
19 |
:root {
|
20 |
--bg-dark: #0a0a0f;
|
21 |
--bg-darker: #040409;
|
|
|
31 |
--header-height: 60px;
|
32 |
--input-height: 80px;
|
33 |
}
|
34 |
+
|
35 |
* {
|
36 |
margin: 0;
|
37 |
padding: 0;
|
|
|
39 |
scrollbar-width: thin;
|
40 |
scrollbar-color: var(--primary-blue) transparent;
|
41 |
}
|
42 |
+
|
43 |
*::-webkit-scrollbar {
|
44 |
width: 8px;
|
45 |
}
|
46 |
+
|
47 |
*::-webkit-scrollbar-track {
|
48 |
background: transparent;
|
49 |
}
|
50 |
+
|
51 |
*::-webkit-scrollbar-thumb {
|
52 |
background-color: var(--primary-blue);
|
53 |
border-radius: 20px;
|
54 |
}
|
55 |
+
|
56 |
body {
|
57 |
font-family: 'Inter', sans-serif;
|
58 |
background-color: var(--bg-dark);
|
|
|
64 |
overflow: hidden;
|
65 |
perspective: 2000px;
|
66 |
}
|
67 |
+
|
68 |
.chat-wrapper {
|
69 |
position: relative;
|
70 |
width: 100%;
|
|
|
81 |
transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);
|
82 |
border: 1px solid var(--border-color);
|
83 |
}
|
84 |
+
|
85 |
.chat-header {
|
86 |
background-color: var(--bg-deepest);
|
87 |
padding: 15px 20px;
|
|
|
92 |
flex-shrink: 0;
|
93 |
height: var(--header-height);
|
94 |
}
|
95 |
+
|
96 |
.chat-container {
|
97 |
display: none;
|
98 |
flex-direction: column;
|
99 |
height: calc(100% - var(--header-height));
|
100 |
position: relative;
|
101 |
}
|
102 |
+
|
103 |
.chat-messages {
|
104 |
flex: 1;
|
105 |
overflow-y: auto;
|
|
|
112 |
height: calc(100% - var(--input-height));
|
113 |
margin-bottom: var(--input-height);
|
114 |
}
|
115 |
+
|
116 |
.chat-input {
|
117 |
position: absolute;
|
118 |
bottom: 0;
|
|
|
127 |
z-index: 10;
|
128 |
align-items: center;
|
129 |
}
|
130 |
+
|
131 |
.chat-input-container {
|
132 |
position: relative;
|
133 |
flex: 1;
|
134 |
display: flex;
|
135 |
align-items: center;
|
136 |
}
|
137 |
+
|
138 |
.chat-input input {
|
139 |
width: 100%;
|
140 |
padding: 12px 16px;
|
|
|
148 |
font-family: 'JetBrains Mono', monospace;
|
149 |
outline: none;
|
150 |
}
|
151 |
+
|
152 |
.chat-input input:focus {
|
153 |
border-color: var(--primary-blue);
|
154 |
box-shadow: 0 0 0 3px rgba(74, 108, 247, 0.2);
|
155 |
}
|
156 |
+
|
157 |
.message {
|
158 |
max-width: 80%;
|
159 |
width: fit-content;
|
|
|
165 |
animation: fadeIn 0.4s forwards;
|
166 |
margin-bottom: 8px;
|
167 |
}
|
168 |
+
|
169 |
.message.user {
|
170 |
align-self: flex-end;
|
171 |
background-color: var(--primary-blue);
|
172 |
color: white;
|
173 |
}
|
174 |
+
|
175 |
.message.bot {
|
176 |
align-self: flex-start;
|
177 |
background-color: rgba(40, 40, 70, 0.8);
|
178 |
color: var(--text-light);
|
179 |
border: 1px solid var(--border-color);
|
180 |
}
|
181 |
+
|
182 |
.message::before {
|
183 |
content: '';
|
184 |
position: absolute;
|
|
|
190 |
opacity: 0;
|
191 |
transition: opacity 0.3s ease;
|
192 |
}
|
193 |
+
|
194 |
.message:hover::before {
|
195 |
opacity: 1;
|
196 |
}
|
197 |
+
|
198 |
.initial-input {
|
199 |
flex: 1;
|
200 |
display: flex;
|
|
|
205 |
text-align: center;
|
206 |
background: linear-gradient(145deg, var(--bg-dark), var(--bg-darker));
|
207 |
}
|
208 |
+
|
209 |
.initial-input h2 {
|
210 |
font-size: 24px;
|
211 |
margin-bottom: 16px;
|
|
|
213 |
font-weight: 600;
|
214 |
font-family: 'JetBrains Mono', monospace;
|
215 |
}
|
216 |
+
|
217 |
.input-container {
|
218 |
width: 100%;
|
219 |
max-width: 400px;
|
220 |
position: relative;
|
221 |
}
|
222 |
+
|
223 |
.initial-input input {
|
224 |
width: 100%;
|
225 |
padding: 16px 24px;
|
|
|
232 |
outline: none;
|
233 |
transition: all 0.3s ease;
|
234 |
}
|
235 |
+
|
236 |
.send-icon {
|
237 |
position: absolute;
|
238 |
right: 12px;
|
|
|
248 |
cursor: pointer;
|
249 |
transition: all 0.3s ease;
|
250 |
}
|
251 |
+
|
252 |
.send-icon:hover {
|
253 |
background-color: var(--accent-color);
|
254 |
transform: translateY(-50%) scale(1.05);
|
255 |
border-radius: 15px;
|
256 |
}
|
257 |
+
|
258 |
@keyframes fadeIn {
|
259 |
from {
|
260 |
opacity: 0;
|
261 |
transform: translateY(20px);
|
262 |
}
|
263 |
+
|
264 |
to {
|
265 |
opacity: 1;
|
266 |
transform: translateY(0);
|
267 |
}
|
268 |
}
|
269 |
+
|
270 |
/* Header actions */
|
271 |
.header-actions {
|
272 |
display: flex;
|
273 |
align-items: center;
|
274 |
gap: 10px;
|
275 |
+
justify-content: center;
|
276 |
}
|
277 |
+
|
278 |
.model-select {
|
279 |
background-color: black;
|
280 |
color: var(--text-light);
|
281 |
border: 1px solid var(--border-color);
|
282 |
border-radius: 8px;
|
283 |
+
padding: 8px 12px;
|
284 |
font-family: 'JetBrains Mono', monospace;
|
285 |
+
font-size: 14px;
|
286 |
cursor: pointer;
|
287 |
transition: all 0.3s ease;
|
288 |
+
max-width: 150px;
|
289 |
+
overflow-y: auto;
|
290 |
+
-webkit-appearance: none;
|
291 |
+
-moz-appearance: none;
|
292 |
+
appearance: none;
|
293 |
+
background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23ffffff%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpolyline%20points%3D%226%209%2012%2015%2018%209%22%3E%3C%2Fpolyline%3E%3C%2Fsvg%3E");
|
294 |
+
background-repeat: no-repeat;
|
295 |
+
background-position: right 8px center;
|
296 |
+
padding-right: 28px;
|
297 |
}
|
298 |
+
|
299 |
+
.model-select option {
|
300 |
+
background-color: var(--bg-dark);
|
301 |
+
color: var(--text-light);
|
302 |
+
padding: 8px;
|
303 |
+
}
|
304 |
+
|
305 |
.clear-chat {
|
306 |
background-color: var(--delete-red);
|
307 |
color: white;
|
308 |
border: none;
|
309 |
border-radius: 8px;
|
310 |
+
width: 36px;
|
311 |
+
height: 36px;
|
312 |
+
padding: 8px;
|
|
|
|
|
313 |
display: flex;
|
314 |
align-items: center;
|
315 |
+
justify-content: center;
|
316 |
+
cursor: pointer;
|
317 |
+
transition: all 0.3s ease;
|
318 |
}
|
319 |
+
|
320 |
.clear-chat:hover {
|
321 |
background-color: #ff6666;
|
322 |
}
|
323 |
+
|
324 |
+
.clear-chat svg {
|
325 |
+
width: 18px;
|
326 |
+
height: 18px;
|
327 |
+
}
|
328 |
+
|
329 |
.watermark {
|
330 |
position: absolute;
|
331 |
bottom: 10px;
|
|
|
336 |
transition: opacity 0.3s ease;
|
337 |
z-index: 11;
|
338 |
}
|
339 |
+
|
340 |
.watermark:hover {
|
341 |
opacity: 0.8;
|
342 |
}
|
343 |
+
|
344 |
+
@media screen and (max-width: 768px) {
|
345 |
+
.chat-header {
|
346 |
+
padding: 8px 12px;
|
347 |
+
height: auto;
|
348 |
+
min-height: 50px;
|
349 |
+
}
|
350 |
+
|
351 |
+
.header-actions {
|
352 |
+
gap: 8px;
|
353 |
+
}
|
354 |
+
|
355 |
+
.model-select {
|
356 |
+
font-size: 12px;
|
357 |
+
padding: 6px 24px 6px 8px;
|
358 |
+
max-width: 120px;
|
359 |
+
}
|
360 |
+
|
361 |
+
.clear-chat {
|
362 |
+
width: 32px;
|
363 |
+
height: 32px;
|
364 |
+
padding: 6px;
|
365 |
+
}
|
366 |
+
|
367 |
+
.clear-chat svg {
|
368 |
+
width: 16px;
|
369 |
+
height: 16px;
|
370 |
+
}
|
371 |
+
|
372 |
+
.chat-input {
|
373 |
+
padding: 8px;
|
374 |
+
height: auto;
|
375 |
+
min-height: 60px;
|
376 |
+
}
|
377 |
+
|
378 |
+
.chat-input input {
|
379 |
+
padding: 10px 12px;
|
380 |
+
font-size: 14px;
|
381 |
+
height: 40px;
|
382 |
+
}
|
383 |
}
|
384 |
+
|
385 |
+
@media screen and (max-width: 480px) {
|
386 |
+
.chat-header h2 {
|
387 |
+
font-size: 14px;
|
388 |
+
}
|
389 |
+
|
390 |
+
.message {
|
391 |
+
font-size: 12px;
|
392 |
+
}
|
393 |
+
|
394 |
+
.header-actions {
|
395 |
+
flex-direction: row;
|
396 |
+
gap: 6px;
|
397 |
+
}
|
398 |
+
|
399 |
+
.model-select {
|
400 |
+
max-width: 100px;
|
401 |
+
font-size: 11px;
|
402 |
+
}
|
403 |
+
|
404 |
+
.clear-chat {
|
405 |
+
width: 28px;
|
406 |
+
height: 28px;
|
407 |
+
padding: 5px;
|
408 |
+
}
|
409 |
+
|
410 |
+
.clear-chat svg {
|
411 |
+
width: 14px;
|
412 |
+
height: 14px;
|
413 |
+
}
|
414 |
+
|
415 |
+
.chat-input input {
|
416 |
+
font-size: 13px;
|
417 |
+
height: 36px;
|
418 |
+
}
|
419 |
+
|
420 |
+
.initial-input input {
|
421 |
+
font-size: 15px;
|
422 |
+
}
|
423 |
+
|
424 |
+
.initial-input h2 {
|
425 |
+
font-size: 20px;
|
426 |
+
}
|
427 |
}
|
428 |
+
|
429 |
+
/* Additional responsive adjustments to existing CSS */
|
430 |
+
* {
|
431 |
+
-webkit-tap-highlight-color: transparent;
|
|
|
432 |
}
|
433 |
+
|
434 |
+
body {
|
435 |
+
touch-action: manipulation;
|
436 |
}
|
437 |
+
|
438 |
+
input,
|
439 |
+
button {
|
440 |
+
-webkit-appearance: none;
|
441 |
+
-moz-appearance: none;
|
442 |
+
appearance: none;
|
443 |
}
|
444 |
+
|
445 |
+
@media (hover: hover) {
|
446 |
+
.send-icon:hover {
|
447 |
+
background-color: var(--accent-color);
|
448 |
+
transform: translateY(-50%) scale(1.05);
|
449 |
+
border-radius: 15px;
|
450 |
+
}
|
451 |
+
}
|
452 |
+
|
453 |
+
@media (pointer: coarse) {
|
454 |
+
.send-icon {
|
455 |
+
width: 45px;
|
456 |
+
height: 45px;
|
457 |
+
}
|
458 |
+
|
459 |
+
.chat-input input {
|
460 |
+
font-size: 16px;
|
461 |
+
/* Larger font for touch devices */
|
462 |
+
}
|
463 |
+
}
|
464 |
+
|
465 |
+
.model-select::-webkit-scrollbar {
|
466 |
+
width: 4px;
|
467 |
+
}
|
468 |
+
|
469 |
+
.model-select::-webkit-scrollbar-track {
|
470 |
+
background: var(--bg-dark);
|
471 |
+
}
|
472 |
+
|
473 |
+
.model-select::-webkit-scrollbar-thumb {
|
474 |
+
background-color: var(--primary-blue);
|
475 |
+
border-radius: 4px;
|
476 |
+
}
|
477 |
+
|
478 |
+
.chat-input input:focus,
|
479 |
+
.initial-input input:focus,
|
480 |
+
.model-select:focus,
|
481 |
+
.clear-chat:focus,
|
482 |
+
.send-icon:focus {
|
483 |
+
outline: 2px solid var(--primary-blue);
|
484 |
+
outline-offset: 2px;
|
485 |
+
}
|
486 |
+
.github-link {
|
487 |
+
display: flex;
|
488 |
+
align-items: center;
|
489 |
+
justify-content: center;
|
490 |
+
width: 36px;
|
491 |
+
height: 36px;
|
492 |
+
z-index: 999;
|
493 |
+
border-radius: 8px;
|
494 |
+
background-color: transparent;
|
495 |
+
border: 1px solid var(--border-color);
|
496 |
+
transition: all 0.3s ease;
|
497 |
+
color: var(--text-light);
|
498 |
+
position: absolute; /* Allows placement in the corner */
|
499 |
+
bottom: 0; /* Aligns to the top */
|
500 |
+
left: 0; /* Aligns to the left */
|
501 |
+
margin: 10px; /* Adds spacing from the edges */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
502 |
}
|
503 |
+
|
504 |
+
.github-link:hover {
|
505 |
+
background-color: var(--hover-color);
|
506 |
+
transform: translateY(-2px);
|
|
|
|
|
507 |
}
|
508 |
+
|
509 |
+
.github-link svg {
|
510 |
+
width: 22px;
|
511 |
+
height: 22px;
|
|
|
|
|
|
|
|
|
512 |
}
|
513 |
+
|
514 |
</style>
|
515 |
</head>
|
516 |
|
517 |
<body>
|
518 |
+
<a href="https://github.com/ParthSadaria" target="_blank" rel="noopener noreferrer" class="github-link" aria-label="Visit Parth Sadaria's GitHub profile">
|
519 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
520 |
+
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
|
521 |
+
</svg>
|
522 |
+
</a>
|
523 |
<div class="chat-wrapper">
|
524 |
<div class="chat-header">
|
525 |
<h2 style="font-family: 'JetBrains Mono', monospace;">LOKI.AI Playground</h2>
|
|
|
528 |
<option value="gpt-4o-mini">GPT-4o Mini</option>
|
529 |
<option value="gpt-4o">GPT-4o</option>
|
530 |
<option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
|
531 |
+
<option value="searchgpt">SearchGPT(Web-access)</option>
|
532 |
+
<option value="claude-sonnet" <option value="claude-3-haiku">Claude 3 Haiku</option>
|
533 |
<option value="llama-3.1-8b">Llama 3.1 8B</option>
|
534 |
<option value="llama-3.1-70b">Llama 3.1 70B</option>
|
535 |
<option value="llama-3.1-405b">Llama 3.1 405b</option>
|
|
|
540 |
<option value="command">Command</option>
|
541 |
</select>
|
542 |
<button id="clearChatButton" class="clear-chat">
|
543 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
544 |
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
545 |
<polyline points="3 6 5 6 21 6"></polyline>
|
546 |
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
547 |
<line x1="10" y1="11" x2="10" y2="17"></line>
|
548 |
<line x1="14" y1="11" x2="14" y2="17"></line>
|
549 |
</svg>
|
|
|
550 |
</button>
|
551 |
</div>
|
552 |
</div>
|
|
|
588 |
const sendButton = document.getElementById('sendButton');
|
589 |
const modelSelect = document.getElementById('modelSelect');
|
590 |
const clearChatButton = document.getElementById('clearChatButton');
|
|
|
591 |
let currentStreamingMessage = null;
|
592 |
+
let isStreamingInProgress = false;
|
593 |
let conversationHistory = [];
|
594 |
function scrollToBottom() {
|
595 |
const chatMessages = document.getElementById('chatMessages');
|
596 |
chatMessages.scrollTop = chatMessages.scrollHeight;
|
597 |
}
|
598 |
function appendMessage(content, type = 'bot', isStreaming = false) {
|
599 |
+
if (type === 'bot' && isStreaming) {
|
600 |
+
if (!currentStreamingMessage) {
|
601 |
+
currentStreamingMessage = document.createElement('div');
|
602 |
+
currentStreamingMessage.className = `message ${type}`;
|
603 |
+
chatMessages.appendChild(currentStreamingMessage);
|
604 |
+
|
605 |
+
// Add to conversation history only at the start of streaming
|
606 |
+
if (!isStreamingInProgress) {
|
607 |
+
conversationHistory.push({
|
608 |
+
role: 'assistant',
|
609 |
+
content: ''
|
610 |
+
});
|
611 |
+
isStreamingInProgress = true;
|
612 |
+
}
|
613 |
+
}
|
614 |
+
|
615 |
+
const formattedContent = content
|
616 |
+
.replace(/(\d+)\.\s*/g, '<br>$1. ')
|
617 |
+
.replace(/\n/g, '<br>')
|
618 |
+
.replace(/\*\*(.*?)\*\*/g, (match, p1) => '<strong>' + p1 + '</strong>');
|
619 |
+
|
620 |
+
currentStreamingMessage.innerHTML += formattedContent;
|
621 |
+
|
622 |
+
// Update the last message in conversation history
|
623 |
+
if (conversationHistory.length > 0) {
|
624 |
+
conversationHistory[conversationHistory.length - 1].content += content;
|
625 |
+
}
|
626 |
+
|
627 |
+
chatMessages.scrollTop = chatMessages.scrollHeight;
|
628 |
+
} else {
|
629 |
+
if (currentStreamingMessage) {
|
630 |
+
isStreamingInProgress = false;
|
631 |
+
currentStreamingMessage = null;
|
632 |
+
}
|
633 |
+
|
634 |
+
const messageBox = document.createElement('div');
|
635 |
+
messageBox.className = `message ${type}`;
|
636 |
+
|
637 |
+
const formattedContent = content
|
638 |
+
.replace(/(\d+)\.\s*/g, '<br>$1. ')
|
639 |
+
.replace(/\n/g, '<br>')
|
640 |
+
.replace(/\*\*(.*?)\*\*/g, (match, p1) => '<strong>' + p1 + '</strong>');
|
641 |
+
|
642 |
+
messageBox.innerHTML = formattedContent;
|
643 |
+
chatMessages.appendChild(messageBox);
|
644 |
+
|
645 |
+
// Add to conversation history only for new messages
|
646 |
+
if (type === 'user') {
|
647 |
+
conversationHistory.push({
|
648 |
+
role: 'user',
|
649 |
+
content: content
|
650 |
+
});
|
651 |
+
}
|
652 |
+
|
653 |
+
chatMessages.scrollTop = chatMessages.scrollHeight;
|
654 |
+
}
|
655 |
+
}
|
656 |
function clearChat() {
|
657 |
chatMessages.innerHTML = '';
|
658 |
conversationHistory = [];
|
|
|
660 |
chatContainer.style.display = 'none';
|
661 |
chatWrapper.classList.remove('active');
|
662 |
}
|
|
|
663 |
async function sendInitialMessage() {
|
664 |
const userMessage = initialChatInput.value.trim();
|
665 |
const selectedModel = modelSelect.value;
|
666 |
if (!userMessage) return;
|
|
|
667 |
initialInput.style.display = 'none';
|
668 |
chatContainer.style.display = 'flex';
|
669 |
chatWrapper.classList.add('active');
|
|
|
670 |
appendMessage(userMessage, 'user');
|
671 |
initialChatInput.value = '';
|
672 |
scrollToBottom();
|
|
|
677 |
console.error("API Error:", error);
|
678 |
}
|
679 |
}
|
|
|
680 |
async function sendMessage() {
|
681 |
const userMessage = chatInput.value.trim();
|
682 |
const selectedModel = modelSelect.value;
|
683 |
if (!userMessage) return;
|
|
|
684 |
appendMessage(userMessage, 'user');
|
685 |
chatInput.value = '';
|
|
|
686 |
try {
|
687 |
await callApi(userMessage, selectedModel);
|
688 |
} catch (error) {
|
|
|
690 |
console.error("API Error:", error);
|
691 |
}
|
692 |
}
|
|
|
693 |
async function callApi(userMessage, model) {
|
694 |
+
let fullResponse = "";
|
695 |
+
if (model === "searchgpt") {
|
696 |
+
const url = `https://parthsadaria-lokiai.hf.space/searchgpt?q=${encodeURIComponent(userMessage)}&stream=true&systemprompt=You are **SearchGPT**, an AI with internet access. Reply directly and accurately to user requests.`;
|
697 |
+
try {
|
698 |
+
const response = await fetch(url);
|
699 |
+
if (response.ok) {
|
700 |
+
const reader = response.body.getReader();
|
701 |
+
const decoder = new TextDecoder("utf-8");
|
702 |
+
let done = false;
|
703 |
+
while (!done) {
|
704 |
+
const { value, done: streamDone } = await reader.read();
|
705 |
+
done = streamDone;
|
706 |
+
if (value) {
|
707 |
+
const chunk = decoder.decode(value);
|
708 |
+
const cleanedChunk = chunk.trim().replace(/^data:\s*/, '');
|
709 |
+
const jsonChunks = cleanedChunk.split("data:").filter(Boolean);
|
710 |
+
jsonChunks.forEach(jsonString => {
|
711 |
+
try {
|
712 |
+
const jsonData = JSON.parse(jsonString);
|
713 |
+
const content = jsonData.choices?.[0]?.message?.content || "";
|
714 |
+
if (content) {
|
715 |
+
// Comprehensive newline conversion
|
716 |
+
const formattedContent = content
|
717 |
+
.replace(/\r\n/g, '<br>') // Windows-style newlines
|
718 |
+
.replace(/\n/g, '<br>') // Unix/Linux-style newlines
|
719 |
+
.replace(/\r/g, '<br>'); // Old Mac-style newlines
|
720 |
+
appendMessage(formattedContent, 'bot', true);
|
721 |
+
}
|
722 |
+
} catch (err) {
|
723 |
+
console.warn("Parsing error:", err);
|
724 |
+
}
|
725 |
+
});
|
|
|
|
|
|
|
|
|
|
|
726 |
}
|
727 |
+
}
|
728 |
+
} else {
|
729 |
+
throw new Error(`API responded with status ${response.status}`);
|
730 |
}
|
731 |
+
} catch (error) {
|
732 |
+
console.error("API call error:", error);
|
733 |
+
throw error;
|
734 |
}
|
735 |
} else {
|
736 |
+
const url = "https://parthsadaria-lokiai.hf.space/chat/completions";
|
737 |
+
const payload = {
|
738 |
+
model: model,
|
739 |
+
messages: [
|
740 |
+
...conversationHistory
|
741 |
+
],
|
742 |
+
stream: true
|
743 |
+
};
|
744 |
+
const headers = {
|
745 |
+
"Content-Type": "application/json"
|
746 |
+
};
|
747 |
+
try {
|
748 |
+
const response = await fetch(url, {
|
749 |
+
method: "POST",
|
750 |
+
headers: headers,
|
751 |
+
body: JSON.stringify(payload)
|
752 |
+
});
|
753 |
+
if (response.ok) {
|
754 |
+
const reader = response.body.getReader();
|
755 |
+
const decoder = new TextDecoder("utf-8");
|
756 |
+
let done = false;
|
757 |
+
while (!done) {
|
758 |
+
const { value, done: streamDone } = await reader.read();
|
759 |
+
done = streamDone;
|
760 |
+
if (value) {
|
761 |
+
const chunk = decoder.decode(value);
|
762 |
+
const cleanedChunk = chunk.trim().replace(/^data:\s*/, '');
|
763 |
+
const jsonChunks = cleanedChunk.split("data:").filter(Boolean);
|
764 |
+
jsonChunks.forEach(jsonString => {
|
765 |
+
try {
|
766 |
+
const jsonData = JSON.parse(jsonString);
|
767 |
+
const delta = jsonData.choices?.[0]?.delta || {};
|
768 |
+
let content = delta.content || "";
|
769 |
+
|
770 |
+
// Comprehensive newline conversion
|
771 |
+
content = content
|
772 |
+
.replace(/\r\n/g, '<br>') // Windows-style newlines
|
773 |
+
.replace(/\n/g, '<br>') // Unix/Linux-style newlines
|
774 |
+
.replace(/\r/g, '<br>'); // Old Mac-style newlines
|
775 |
+
if (content) {
|
776 |
+
appendMessage(content, 'bot', true);
|
777 |
+
}
|
778 |
+
} catch (err) {
|
779 |
+
console.warn("Parsing error:", err);
|
780 |
+
}
|
781 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
782 |
}
|
783 |
+
}
|
784 |
+
} else {
|
785 |
+
throw new Error(`API responded with status ${response.status}`);
|
786 |
}
|
787 |
+
} catch (error) {
|
788 |
+
console.error("API call error:", error);
|
789 |
+
throw error;
|
790 |
}
|
|
|
|
|
791 |
}
|
792 |
+
return fullResponse.trim();
|
|
|
|
|
793 |
}
|
|
|
|
|
|
|
|
|
794 |
// Event Listeners
|
795 |
initialSendIcon.addEventListener('click', sendInitialMessage);
|
796 |
initialChatInput.addEventListener('keypress', (event) => {
|
797 |
if (event.key === 'Enter') sendInitialMessage();
|
798 |
});
|
|
|
799 |
sendButton.addEventListener('click', sendMessage);
|
800 |
chatInput.addEventListener('keypress', (event) => {
|
801 |
if (event.key === 'Enter') sendMessage();
|
802 |
});
|
|
|
803 |
// Clear Chat Button Event Listener
|
804 |
clearChatButton.addEventListener('click', clearChat);
|
805 |
</script>
|
806 |
</body>
|
|
|
807 |
</html>
|