styling update
This commit is contained in:
97
script.js
97
script.js
@@ -330,8 +330,18 @@ async function handleCallback() {
|
||||
function addMessage(text, type = "user") {
|
||||
const msg = document.createElement("div");
|
||||
msg.className = `message ${type}`;
|
||||
msg.innerHTML = `<div class="message-text">${escapeHtml(text)}</div>`;
|
||||
|
||||
// Render markdown for bot messages, escape HTML for user messages
|
||||
const content = type === "assistant" ? renderMarkdown(text) : escapeHtml(text);
|
||||
msg.innerHTML = `<div class="message-text">${content}</div>`;
|
||||
|
||||
chatMessages.appendChild(msg);
|
||||
|
||||
// Add copy buttons to code blocks for assistant messages
|
||||
if (type === "assistant") {
|
||||
addCopyButtonsToCodeBlocks(msg);
|
||||
}
|
||||
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
@@ -367,6 +377,87 @@ function escapeHtml(text) {
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function renderMarkdown(text) {
|
||||
// Check if libraries are loaded
|
||||
if (typeof marked === 'undefined' || typeof DOMPurify === 'undefined') {
|
||||
logger.error('Markdown libraries not loaded', {
|
||||
markedAvailable: typeof marked !== 'undefined',
|
||||
dompurifyAvailable: typeof DOMPurify !== 'undefined'
|
||||
});
|
||||
return escapeHtml(text);
|
||||
}
|
||||
|
||||
try {
|
||||
// Configure marked.js for optimal chat rendering
|
||||
marked.setOptions({
|
||||
breaks: true, // Convert line breaks to <br>
|
||||
gfm: true, // GitHub-flavored markdown
|
||||
headerIds: false, // No IDs for headers
|
||||
mangle: false // Don't obfuscate emails
|
||||
});
|
||||
|
||||
// Parse markdown to HTML
|
||||
const rawHtml = marked.parse(text);
|
||||
|
||||
// Sanitize HTML to prevent XSS attacks (include button element for copy functionality)
|
||||
const cleanHtml = DOMPurify.sanitize(rawHtml, {
|
||||
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 's', 'code', 'pre',
|
||||
'a', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||
'blockquote', 'hr', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'button'],
|
||||
ALLOWED_ATTR: ['href', 'target', 'rel', 'class']
|
||||
});
|
||||
|
||||
return cleanHtml;
|
||||
} catch (error) {
|
||||
logger.error('Markdown rendering failed', error);
|
||||
return escapeHtml(text);
|
||||
}
|
||||
}
|
||||
|
||||
function addCopyButtonsToCodeBlocks(element) {
|
||||
// Find all <pre> elements in the given element
|
||||
const preElements = element.querySelectorAll('pre');
|
||||
|
||||
preElements.forEach((pre) => {
|
||||
// Skip if button already exists
|
||||
if (pre.querySelector('.copy-button')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create copy button
|
||||
const button = document.createElement('button');
|
||||
button.className = 'copy-button';
|
||||
button.textContent = 'Copy';
|
||||
|
||||
// Add click handler
|
||||
button.addEventListener('click', async () => {
|
||||
const code = pre.querySelector('code');
|
||||
const text = code ? code.textContent : pre.textContent;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
button.textContent = 'Copied!';
|
||||
button.classList.add('copied');
|
||||
|
||||
// Reset after 2 seconds
|
||||
setTimeout(() => {
|
||||
button.textContent = 'Copy';
|
||||
button.classList.remove('copied');
|
||||
}, 2000);
|
||||
} catch (error) {
|
||||
logger.error('Failed to copy code to clipboard', error);
|
||||
button.textContent = 'Failed';
|
||||
setTimeout(() => {
|
||||
button.textContent = 'Copy';
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
|
||||
// Add button to pre element
|
||||
pre.appendChild(button);
|
||||
});
|
||||
}
|
||||
|
||||
async function sendMessage(text) {
|
||||
if (!text.trim()) return;
|
||||
|
||||
@@ -438,11 +529,13 @@ async function sendMessage(text) {
|
||||
|
||||
if (data.chunk) {
|
||||
assistantMessage += data.chunk;
|
||||
textDiv.textContent = assistantMessage;
|
||||
textDiv.innerHTML = renderMarkdown(assistantMessage);
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
if (data.done) {
|
||||
// Add copy buttons to code blocks after streaming is complete
|
||||
addCopyButtonsToCodeBlocks(msgDiv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user