diff --git a/public/assets/js/base.js b/public/assets/js/base.js
index ab78e62..c3e9384 100644
--- a/public/assets/js/base.js
+++ b/public/assets/js/base.js
@@ -13,6 +13,17 @@ document.addEventListener('DOMContentLoaded', () => {
return DOMPurify.sanitize(marked.parse(text));
}
+ function enhanceChatLinks(container = chatEl) {
+ if (!container) {
+ return;
+ }
+
+ container.querySelectorAll('a[href]').forEach((link) => {
+ link.setAttribute('target', '_blank');
+ link.setAttribute('rel', 'noopener noreferrer');
+ });
+ }
+
function addMessage(role, html = '', extra = '') {
const msg = document.createElement('div');
msg.className = 'message ' + role;
@@ -23,6 +34,9 @@ document.addEventListener('DOMContentLoaded', () => {
msg.appendChild(bubble);
chatEl.appendChild(msg);
+
+ enhanceChatLinks(bubble);
+
chatEl.scrollTop = chatEl.scrollHeight;
return bubble;
@@ -182,6 +196,7 @@ document.addEventListener('DOMContentLoaded', () => {
function renderBubbleContent(bubble, raw) {
bubble.innerHTML = renderMarkdown(raw);
cleanupThinkSpans(bubble);
+ enhanceChatLinks(bubble);
chatEl.scrollTop = chatEl.scrollHeight;
}
@@ -191,6 +206,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (!res.ok) return;
const messages = await res.json();
messages.forEach(m => addMessage(m.role, renderMarkdown(m.text)));
+ enhanceChatLinks(chatEl);
} catch {}
}
@@ -288,6 +304,7 @@ document.addEventListener('DOMContentLoaded', () => {
}
} catch {
bubble.innerHTML += '
Error occurred.';
+ enhanceChatLinks(bubble);
} finally {
if (renderTimer) {
clearTimeout(renderTimer);