So i’m trying to implement my own ai code assistant chat in vscode,so i used marked js for parsing the text output i get from the backend via openAI api and stream those values to the frontend which works well and good,but i’m having issues with prism js for syntax highlighting because it’s not working at all and i can’t debug why,it actually worked when i added a code block statically to my html page but hasn’t since i have been getting values to be parsed from the server. because the entire html script is long i will divide the snippets into smaller reader chunks that’s most important.
so here’s where i’m adding prism js cdns needed :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
<!-- Prism.js Core CSS -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet">
<!-- Prism.js Core JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js" integrity="sha512-7Z9J3l1+EYfeaPKcGXu3MS/7T+w19WtKQY/n+xzmw4hZhJ9tyYmcUS+4QqAlzhicE5LAfMQSF3iFTK9bQdTxXg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- Prism.js Autoloader Plugin -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js" integrity="sha512-SkmBfuA2hqjzEVpmnMt/LINrjop3GKWqsuLSSB3e7iBmYK7JuWw4ldmmxwD9mdm2IRTTi0OxSAfEGvgEi0i2Kw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<title>Chat UI</title>
This is my script section where i basically do all the parsing :
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script>
const vscode = acquireVsCodeApi();
//const chatInput = document.getElementById('chat-input');
//const sendButton = document.querySelector('.send-btns');
const chatBox = document.querySelector('.chatbox');
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0,
v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function typeText(element, text, speed = 20) {
let index = 0;
let html = '';
function type() {
if (index < text.length) {
if (text.charAt(index) === '<') {
// Handle HTML tags
let tag = '';
while (text.charAt(index) !== '>' && index < text.length) {
tag += text.charAt(index);
index++;
}
tag += '>';
html += tag;
index++;
} else {
// Handle text
html += text.charAt(index);
index++;
}
element.innerHTML = html;
setTimeout(type, speed);
}
}
type();
}
function sendMessage(){
const chatBox = document.getElementById('chatbox');
const chatInput = document.getElementById('chat-input');
const message = chatInput.value;
if (message.trim()) {
vscode.postMessage({ command: 'sendMessage', value:{
"project_path":"random",
"storage_name":"PreciousKent8Storage",
"user_prompt":message,
"thread_id":"thread_YiYMOcOKifXsKbAYRbAXuZGF",
"attachments":[
{
"file_id": "file-gXrvfXdCGAbCy2uswrEwCUoY",
"tools": [
{
"type": "file_search"
}
]
}
]}
})
const userMessageDiv = document.createElement('div');
userMessageDiv.classList.add('user-message-div');
userMessageDiv.innerHTML = `
<div class="profile-img-div">
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi67elRmCcCkrdJ36cRr6NJCKslrOl7eMTotxXVvBCaYJuBadcb5PxaYm8k9wnmOw8CgoAS9jVAVA1aA7uTcNMa1fhhOXaSX_eucNfmpomSZLPSezm_TH9qa4jYcB54DlOhZdjA3UZz1r3SGnc1uQCd5IQJtwV7Ubm_suNz0U6xHNb5cg/s220/IMG_20240415_205336_841.jpg" alt="User Image"/>
</div>
<div id="user-message-${generateUUID()}" class="user-message-box">
${message}
</div>
`;
chatbox.appendChild(userMessageDiv);
chatInput.value='';
}
}
function ParseMessage(text){
const renderer = new marked.Renderer();
renderer.code = (code, language) => {
// Define a base class for all code blocks
let langBlock="language-"+code.lang
//console.log("let's see lang",code);
// Return custom HTML for the code block
return `<div class="code-block">
<div class="code-toolbar">
<div class="language-name">${code.lang}</div>
<div class="toolbar-actions">
<button id="copy-code"><i class="fas fa-clipboard"></i> Copy Code</button>
<button id="add-to-file"><i class="fa-solid fa-file"></i> Add to File</button>
<button id="create-new-file"><i class="fa-solid fa-file"></i> Create New File</button>
</div>
</div>
<pre class="scroll-container"><code class=langBlock>${code.text}</code></pre>
</div>`
};
// Set options for marked.js with custom renderer
marked.setOptions({
renderer: renderer,
langPrefix: '' // Prevent marked.js from adding its own lang class
});
const htmlContent = marked.parse(text);
return htmlContent;
}
// Handle messages from the extension
window.addEventListener('message', event => {
const message = event.data; // The message sent from the extension
switch (message.command) {
case 'receiveMessage':
const questionId=message.data.question_id;
if(localStorage.getItem(questionId)==null){
const aiMessageDiv = document.createElement('div');
aiMessageDiv.classList.add('message-div');
aiMessageDiv.innerHTML = `
<div class="ai-profile-img-div">
<img src="../ai-image" alt="AI Image"/>
</div>
<div id="ai-reply-${message.data.question_id}" class="ai-message-box">
${ParseMessage(message.data.message.value)}
</div>
`;
document.querySelector('.chatbox').appendChild(aiMessageDiv);
localStorage.setItem(questionId, questionId);
}else{
current_message=document.getElementById(`ai-reply-${questionId}`);
current_message.innerHTML='';
typeText(current_message,ParseMessage(message.data.message.value))
}
break;
}
});
document.addEventListener('DOMContentLoaded', () => {
vscode.postMessage({
command: 'sendMessage',
value: {
"project_path":"random",
"storage_name":"PreciousKent8Storage"
}
});
});
function addScrollListener(scrollElement) {
let isScrolling;
// Event listener to show the scrollbar thumb on scroll
scrollElement.addEventListener('scroll', () => {
scrollElement.classList.add('show-scroll');
// Clear the timeout for continuous scrolling
clearTimeout(isScrolling);
// Remove the class after scrolling stops
isScrolling = setTimeout(() => {
scrollElement.classList.remove('show-scroll');
}, 1000); // 1 second delay after scroll stops
});
}
const scrollElements = document.querySelectorAll('.scroll-container');
scrollElements.forEach(scrollElement=>{
addScrollListener(scrollElement);
});
</script>
so this part of the code :
// Handle messages from the extension
window.addEventListener('message', event => {
const message = event.data; // The message sent from the extension
switch (message.command) {
case 'receiveMessage':
const questionId=message.data.question_id;
if(localStorage.getItem(questionId)==null){
const aiMessageDiv = document.createElement('div');
aiMessageDiv.classList.add('message-div');
aiMessageDiv.innerHTML = `
<div class="ai-profile-img-div">
<img src="../ai-image" alt="AI Image"/>
</div>
<div id="ai-reply-${message.data.question_id}" class="ai-message-box">
${ParseMessage(message.data.message.value)}
</div>
`;
document.querySelector('.chatbox').appendChild(aiMessageDiv);
localStorage.setItem(questionId, questionId);
}else{
current_message=document.getElementById(`ai-reply-${questionId}`);
current_message.innerHTML='';
typeText(current_message,ParseMessage(message.data.message.value))
}
break;
}
});
is basically the entry point where i get the values from my websocket server.
my current css styles for the code blocks are below(the ones to change the token styles do not work at all) :
.code-block {
position: relative;
background: black;
border-radius: 8px; /* Rounded corners */
margin-top:0.6rem;
margin-bottom:0.6rem;
margin-right:1rem;
overflow: hidden;
padding-top:2rem!important;
box-sizing: border-box;
}
.code-toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
background: #333;
color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
font-family: 'Roboto', sans-serif;
z-index: 10;
box-sizing: border-box;
}
.code-toolbar .toolbar-actions {
display: flex;
gap: 0.5rem;
box-sizing: border-box;
}
.code-toolbar button {
background: transparent;
display:flex;
color: #fff;
border: none;
border-radius: 4px;
padding: 0.5rem;
cursor: pointer;
box-sizing: border-box;
}
.code-toolbar button:hover {
background: transparent;
}
.code-toolbar .language-name {
font-weight: 400;
color:#a3a2a2;
}
pre {
margin: 0;
background-color:transparent!important;
padding: 1rem;
}
code {
display: block;
background-color:transparent!important;
}
/* Custom styles for specific tokens */
.token.function {
color: #ffcc00; /* Custom color for function names */
}
.token.class-name {
color: #ff6b6b; /* Custom color for class names */
}
.token.variable {
color: #5ccfe6; /* Custom color for variables */
}
.token.comment {
color: #6a9955; /* Custom color for comments */
}
.token.keyword {
color: #569cd6; /* Custom color for keywords */
}
.token.string {
color: #ce9178; /* Custom color for strings */
}