diff --git a/docs/README.md b/docs/README.md
index 1a1bcc7..091ecf0 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -25,7 +25,7 @@ Your web application, titled **"Unity Chat U1 6.6"**, provides an interactive ch
- **Speech Recognition:** Users can dictate messages through voice input, which captures speech and translates it into textual inputs in real-time.
- **Message Handling:**
- - **Markdown Support:** AI-generated responses utilize Markdown, enhanced with syntax highlighting (via highlight.js) for clarity in code snippets.
+ - **Markdown Support:** AI-generated responses utilize Markdown with highlight.js, offering syntax highlighting for over 100 languages and automatically adapting to the active site theme.
- **Image Embedding:** Automatically embeds images generated by Pollinations based on AI conversation content.
- **Editing and Regeneration:** Users can edit their messages or regenerate AI responses conveniently from within the chat interface.
diff --git a/index.html b/index.html
index b33a062..59219ef 100644
--- a/index.html
+++ b/index.html
@@ -468,7 +468,8 @@
Voice Chat
-
+
+
diff --git a/js/chat/chat-init.js b/js/chat/chat-init.js
index d82bcfb..f2882d2 100644
--- a/js/chat/chat-init.js
+++ b/js/chat/chat-init.js
@@ -13,10 +13,6 @@ document.addEventListener("DOMContentLoaded", () => {
if (newTitle && newTitle !== currentSession.name) Storage.renameSession(currentSession.id, newTitle);
}
};
- const highlightAllCodeBlocks = () => {
- if (!window.hljs) return;
- chatBox.querySelectorAll("pre code").forEach(block => hljs.highlightElement(block));
- };
const appendMessage = ({ role, content, index, imageUrls = [], audioUrls = [] }) => {
const container = document.createElement("div");
container.classList.add("message");
@@ -116,8 +112,8 @@ document.addEventListener("DOMContentLoaded", () => {
block.parentNode.insertAdjacentElement("afterend", buttonContainer);
});
chatBox.appendChild(container);
- chatBox.scrollTop = chatBox.scrollHeight;
- highlightAllCodeBlocks();
+ chatBox.scrollTop = chatBox.scrollHeight;
+ window.highlightUtils?.highlightAllCodeBlocks(chatBox);
};
const downloadCodeAsTxt = (codeContent, language) => {
const blob = new Blob([codeContent], { type: "text/plain" });
@@ -394,7 +390,7 @@ document.addEventListener("DOMContentLoaded", () => {
}
}
});
- highlightAllCodeBlocks();
+ window.highlightUtils?.highlightAllCodeBlocks(chatBox);
};
window.addNewMessage = ({ role, content, imageUrls = [], audioUrls = [] }) => {
const currentSession = Storage.getCurrentSession();
@@ -439,14 +435,14 @@ document.addEventListener("DOMContentLoaded", () => {
chatBox.scrollTop = chatBox.scrollHeight;
sendToPolliLib(() => {
loadingDiv.remove();
- highlightAllCodeBlocks();
+ window.highlightUtils?.highlightAllCodeBlocks(chatBox);
}, newContent);
showToast("User message updated and new response generated");
} else {
currentSession.messages[msgIndex].content = newContent;
Storage.updateSessionMessages(currentSession.id, currentSession.messages);
renderStoredMessages(currentSession.messages);
- highlightAllCodeBlocks();
+ window.highlightUtils?.highlightAllCodeBlocks(chatBox);
showToast("AI message updated");
}
};
@@ -484,7 +480,7 @@ document.addEventListener("DOMContentLoaded", () => {
console.log(`Sending re-generate request for user message: ${userMessage} (with unique suffix: ${uniqueUserMessage})`);
window.sendToPolliLib(() => {
loadingDiv.remove();
- highlightAllCodeBlocks();
+ window.highlightUtils?.highlightAllCodeBlocks(chatBox);
checkAndUpdateSessionTitle();
showToast("Response regenerated successfully");
}, uniqueUserMessage);
diff --git a/js/chat/chat-storage.js b/js/chat/chat-storage.js
index d72f5ec..afa67cf 100644
--- a/js/chat/chat-storage.js
+++ b/js/chat/chat-storage.js
@@ -25,15 +25,6 @@ document.addEventListener("DOMContentLoaded", () => {
}
}
}
- function highlightAllCodeBlocks() {
- if (!window.hljs) {
- return;
- }
- const codeBlocks = chatBox.querySelectorAll("pre code");
- codeBlocks.forEach((block) => {
- hljs.highlightElement(block);
- });
- }
function appendMessage({ role, content, index, imageUrls = [], audioUrls = [] }) {
const container = document.createElement("div");
container.classList.add("message");
@@ -147,9 +138,9 @@ document.addEventListener("DOMContentLoaded", () => {
buttonContainer.appendChild(downloadCodeBtn);
block.parentNode.insertAdjacentElement("afterend", buttonContainer);
});
- chatBox.appendChild(container);
- chatBox.scrollTop = chatBox.scrollHeight;
- highlightAllCodeBlocks();
+ chatBox.appendChild(container);
+ chatBox.scrollTop = chatBox.scrollHeight;
+ window.highlightUtils?.highlightAllCodeBlocks(chatBox);
}
function downloadCodeAsTxt(codeContent, language) {
const blob = new Blob([codeContent], { type: "text/plain" });
@@ -400,7 +391,7 @@ document.addEventListener("DOMContentLoaded", () => {
audioUrls: storedAudio
});
});
- highlightAllCodeBlocks();
+ window.highlightUtils?.highlightAllCodeBlocks(chatBox);
chatInput.disabled = false;
chatInput.focus();
}
@@ -450,18 +441,18 @@ document.addEventListener("DOMContentLoaded", () => {
chatBox.scrollTop = chatBox.scrollHeight;
window.sendToPolliLib(() => {
loadingDiv.remove();
- highlightAllCodeBlocks();
+ window.highlightUtils?.highlightAllCodeBlocks(chatBox);
}, newContent);
showToast("User message updated and new response generated");
} else {
currentSession.messages[msgIndex].content = newContent;
Storage.updateSessionMessages(currentSession.id, currentSession.messages);
renderStoredMessages(currentSession.messages);
- highlightAllCodeBlocks();
- showToast("AI message updated");
- }
- }
- function reGenerateAIResponse(aiIndex) {
+ window.highlightUtils?.highlightAllCodeBlocks(chatBox);
+ showToast("AI message updated");
+ }
+ }
+ function reGenerateAIResponse(aiIndex) {
console.log(`Re-generating AI response for index: ${aiIndex}`);
const currentSession = Storage.getCurrentSession();
if (aiIndex < 0 || aiIndex >= currentSession.messages.length || currentSession.messages[aiIndex].role !== "ai") {
@@ -496,12 +487,12 @@ document.addEventListener("DOMContentLoaded", () => {
chatBox.scrollTop = chatBox.scrollHeight;
const uniqueUserMessage = `${userMessage} [regen-${Date.now()}-${Math.random().toString(36).substring(2)}]`;
console.log(`Sending re-generate request for user message: ${userMessage} (with unique suffix: ${uniqueUserMessage})`);
- window.sendToPolliLib(() => {
- loadingDiv.remove();
- highlightAllCodeBlocks();
- showToast("Response regenerated successfully");
- }, uniqueUserMessage);
- }
+ window.sendToPolliLib(() => {
+ loadingDiv.remove();
+ window.highlightUtils?.highlightAllCodeBlocks(chatBox);
+ showToast("Response regenerated successfully");
+ }, uniqueUserMessage);
+ }
if (voiceToggleBtn) {
voiceToggleBtn.addEventListener("click", window._chatInternals.toggleAutoSpeak);
diff --git a/js/ui/highlight.js b/js/ui/highlight.js
new file mode 100644
index 0000000..86ad11f
--- /dev/null
+++ b/js/ui/highlight.js
@@ -0,0 +1,49 @@
+(function() {
+ const HLJS_BASE = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/";
+
+ const hljsThemeMap = {
+ light: "github",
+ dark: "github-dark",
+ hacker: "a11y-dark",
+ oled: "atom-one-dark",
+ "subtle-light": "atom-one-light",
+ burple: "atom-one-dark",
+ "pretty-pink": "github",
+ nord: "nord",
+ "solarized-light": "solarized-light",
+ "solarized-dark": "solarized-dark",
+ "gruvbox-light": "gruvbox-light",
+ "gruvbox-dark": "gruvbox-dark",
+ cyberpunk: "atom-one-dark",
+ dracula: "dracula",
+ monokai: "monokai",
+ "material-dark": "atom-one-dark",
+ "material-light": "atom-one-light",
+ "pastel-dream": "github",
+ "ocean-breeze": "github",
+ "vintage-paper": "github",
+ honeycomb: "github",
+ "rainbow-throwup": "github",
+ serenity: "atom-one-light"
+ };
+
+ let hljsThemeLink = document.getElementById("hljs-theme-link");
+ if (!hljsThemeLink) {
+ hljsThemeLink = document.createElement("link");
+ hljsThemeLink.id = "hljs-theme-link";
+ hljsThemeLink.rel = "stylesheet";
+ document.head.appendChild(hljsThemeLink);
+ }
+
+ function updateHighlightTheme(themeValue) {
+ const hlTheme = hljsThemeMap[themeValue] || "github-dark";
+ hljsThemeLink.href = `${HLJS_BASE}${hlTheme}.min.css`;
+ }
+
+ function highlightAllCodeBlocks(container = document) {
+ if (!window.hljs) return;
+ container.querySelectorAll("pre code").forEach(block => hljs.highlightElement(block));
+ }
+
+ window.highlightUtils = { updateHighlightTheme, highlightAllCodeBlocks };
+})();
diff --git a/js/ui/ui.js b/js/ui/ui.js
index 846d68c..7e9522f 100644
--- a/js/ui/ui.js
+++ b/js/ui/ui.js
@@ -38,13 +38,6 @@ document.addEventListener("DOMContentLoaded", () => {
themeLinkElement.rel = "stylesheet";
document.head.appendChild(themeLinkElement);
}
- let hljsThemeLink = document.getElementById("hljs-theme-link");
- if (!hljsThemeLink) {
- hljsThemeLink = document.createElement("link");
- hljsThemeLink.id = "hljs-theme-link";
- hljsThemeLink.rel = "stylesheet";
- document.head.appendChild(hljsThemeLink);
- }
const allThemes = [
{ value: "light", label: "Light", file: "themes/light.css" },
@@ -72,38 +65,6 @@ document.addEventListener("DOMContentLoaded", () => {
{ value: "serenity", label: "Serenity", file: "themes/serenity.css" }
];
- const hljsThemeMap = {
- light: "github",
- dark: "github-dark",
- hacker: "a11y-dark",
- oled: "atom-one-dark",
- "subtle-light": "atom-one-light",
- burple: "atom-one-dark",
- "pretty-pink": "github",
- nord: "nord",
- "solarized-light": "solarized-light",
- "solarized-dark": "solarized-dark",
- "gruvbox-light": "gruvbox-light",
- "gruvbox-dark": "gruvbox-dark",
- cyberpunk: "atom-one-dark",
- dracula: "dracula",
- monokai: "monokai",
- "material-dark": "atom-one-dark",
- "material-light": "atom-one-light",
- "pastel-dream": "github",
- "ocean-breeze": "github",
- "vintage-paper": "github",
- honeycomb: "github",
- "rainbow-throwup": "github",
- serenity: "atom-one-light"
- };
-
- const HLJS_BASE = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/";
-
- function updateHighlightTheme(themeValue) {
- const hlTheme = hljsThemeMap[themeValue] || "github-dark";
- hljsThemeLink.href = `${HLJS_BASE}${hlTheme}.min.css`;
- }
function populateThemeDropdowns() {
themeSelect.innerHTML = "";
@@ -130,7 +91,7 @@ document.addEventListener("DOMContentLoaded", () => {
themeSelectSettings.value = savedTheme;
const found = allThemes.find(t => t.value === savedTheme);
themeLinkElement.href = found ? found.file : "themes/dark.css";
- updateHighlightTheme(savedTheme);
+ window.highlightUtils?.updateHighlightTheme(savedTheme);
}
loadUserTheme();
@@ -140,7 +101,7 @@ document.addEventListener("DOMContentLoaded", () => {
themeSelectSettings.value = newThemeValue;
const found = allThemes.find(t => t.value === newThemeValue);
themeLinkElement.href = found ? found.file : "";
- updateHighlightTheme(newThemeValue);
+ window.highlightUtils?.updateHighlightTheme(newThemeValue);
}
themeSelect.addEventListener("change", () => {