Spaces:
Running
Running
Update public/index.html
Browse files- public/index.html +53 -12
public/index.html
CHANGED
|
@@ -125,10 +125,14 @@
|
|
| 125 |
</main>
|
| 126 |
|
| 127 |
<script>
|
|
|
|
|
|
|
|
|
|
| 128 |
marked.setOptions({
|
| 129 |
highlight: function(code, lang) {
|
|
|
|
| 130 |
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
|
| 131 |
-
return hljs.highlight(code, { language }).value;
|
| 132 |
}
|
| 133 |
});
|
| 134 |
|
|
@@ -293,7 +297,7 @@
|
|
| 293 |
|
| 294 |
function renderMessages(messages) {
|
| 295 |
const container = document.getElementById('chat-window');
|
| 296 |
-
const isScrolledToBottom = container.scrollHeight - container.clientHeight <= container.scrollTop +
|
| 297 |
|
| 298 |
container.innerHTML = messages.map(m => {
|
| 299 |
let html = '';
|
|
@@ -328,7 +332,8 @@
|
|
| 328 |
}
|
| 329 |
});
|
| 330 |
|
| 331 |
-
if
|
|
|
|
| 332 |
}
|
| 333 |
|
| 334 |
function pollGeneratingChat(id) {
|
|
@@ -419,6 +424,9 @@
|
|
| 419 |
const decoder = new TextDecoder("utf-8");
|
| 420 |
let aiContent = "";
|
| 421 |
let aiReasoning = "";
|
|
|
|
|
|
|
|
|
|
| 422 |
|
| 423 |
while (true) {
|
| 424 |
const { value, done } = await reader.read();
|
|
@@ -450,17 +458,50 @@
|
|
| 450 |
}
|
| 451 |
}
|
| 452 |
|
| 453 |
-
|
| 454 |
-
if (
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 461 |
}
|
| 462 |
|
| 463 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
|
| 465 |
} catch (error) {
|
| 466 |
console.error(error);
|
|
|
|
| 125 |
</main>
|
| 126 |
|
| 127 |
<script>
|
| 128 |
+
// NEW FEATURE: Toggle high-cost rendering during stream to prevent memory crashes
|
| 129 |
+
let enableHighlighting = true;
|
| 130 |
+
|
| 131 |
marked.setOptions({
|
| 132 |
highlight: function(code, lang) {
|
| 133 |
+
if (!enableHighlighting) return code; // Skip highlighting while streaming!
|
| 134 |
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
|
| 135 |
+
try { return hljs.highlight(code, { language }).value; } catch(e) { return code; }
|
| 136 |
}
|
| 137 |
});
|
| 138 |
|
|
|
|
| 297 |
|
| 298 |
function renderMessages(messages) {
|
| 299 |
const container = document.getElementById('chat-window');
|
| 300 |
+
const isScrolledToBottom = container.scrollHeight - container.clientHeight <= container.scrollTop + 50;
|
| 301 |
|
| 302 |
container.innerHTML = messages.map(m => {
|
| 303 |
let html = '';
|
|
|
|
| 332 |
}
|
| 333 |
});
|
| 334 |
|
| 335 |
+
// Prevent unwanted jump to bottom if chat is empty or user scrolled up
|
| 336 |
+
if (isScrolledToBottom && messages.length > 0) scrollToBottom();
|
| 337 |
}
|
| 338 |
|
| 339 |
function pollGeneratingChat(id) {
|
|
|
|
| 424 |
const decoder = new TextDecoder("utf-8");
|
| 425 |
let aiContent = "";
|
| 426 |
let aiReasoning = "";
|
| 427 |
+
|
| 428 |
+
enableHighlighting = false; // SPEED UP: Disable expensive highlighting during stream
|
| 429 |
+
let lastRenderTime = Date.now();
|
| 430 |
|
| 431 |
while (true) {
|
| 432 |
const { value, done } = await reader.read();
|
|
|
|
| 458 |
}
|
| 459 |
}
|
| 460 |
|
| 461 |
+
// THROTTLE RENDER: Only update DOM max 5 times a second to prevent memory crashes
|
| 462 |
+
if (Date.now() - lastRenderTime > 200) {
|
| 463 |
+
let tempHtml = "";
|
| 464 |
+
if (aiReasoning) tempHtml += `<div class="reasoning-block"><i>Thinking Process</i><br/>${marked.parse(aiReasoning)}</div>`;
|
| 465 |
+
if (aiContent) tempHtml += DOMPurify.sanitize(marked.parse(aiContent));
|
| 466 |
+
|
| 467 |
+
document.getElementById('temp-ai-msg').innerHTML = tempHtml || "<span class='animate-pulse text-gray-400'>Thinking...</span>";
|
| 468 |
+
|
| 469 |
+
const distanceToBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
|
| 470 |
+
if (distanceToBottom < 100) scrollToBottom();
|
| 471 |
+
|
| 472 |
+
lastRenderTime = Date.now();
|
| 473 |
+
}
|
| 474 |
}
|
| 475 |
|
| 476 |
+
// FINAL RENDER: Stream is complete, turn highlighting back on and do a final pristine render
|
| 477 |
+
enableHighlighting = true;
|
| 478 |
+
|
| 479 |
+
let finalHtml = "";
|
| 480 |
+
if (aiReasoning) finalHtml += `<div class="reasoning-block"><i>Thinking Process</i><br/>${marked.parse(aiReasoning)}</div>`;
|
| 481 |
+
if (aiContent) finalHtml += DOMPurify.sanitize(marked.parse(aiContent));
|
| 482 |
+
|
| 483 |
+
const finalMsgElement = document.getElementById('temp-ai-msg');
|
| 484 |
+
finalMsgElement.innerHTML = finalHtml || "Done.";
|
| 485 |
+
finalMsgElement.removeAttribute('id'); // Remove ID so we don't accidentally overwrite it
|
| 486 |
+
|
| 487 |
+
// Attach copy buttons explicitly since we aren't calling renderMessages()
|
| 488 |
+
finalMsgElement.querySelectorAll('pre').forEach(pre => {
|
| 489 |
+
if (!pre.querySelector('.copy-btn')) {
|
| 490 |
+
const btn = document.createElement('button');
|
| 491 |
+
btn.className = 'copy-btn';
|
| 492 |
+
btn.innerText = 'Copy';
|
| 493 |
+
btn.onclick = () => {
|
| 494 |
+
const codeText = pre.querySelector('code')?.innerText || pre.innerText.replace('Copy', '');
|
| 495 |
+
navigator.clipboard.writeText(codeText.trim());
|
| 496 |
+
btn.innerText = 'Copied!';
|
| 497 |
+
setTimeout(() => btn.innerText = 'Copy', 2000);
|
| 498 |
+
};
|
| 499 |
+
pre.appendChild(btn);
|
| 500 |
+
}
|
| 501 |
+
});
|
| 502 |
+
|
| 503 |
+
scrollToBottom();
|
| 504 |
+
loadSidebar(); // Sync sidebar updates but PREVENT wiping the main chat DOM
|
| 505 |
|
| 506 |
} catch (error) {
|
| 507 |
console.error(error);
|