chatgpt web frontend: no more using innerHTML. now using markdown-it instead of marked
This commit is contained in:
parent
6e93b90027
commit
a034a5a2b9
5 changed files with 121 additions and 93 deletions
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
<!-- Include the JavaScript files at bottom to avoid blocking UI -->
|
<!-- Include the JavaScript files at bottom to avoid blocking UI -->
|
||||||
<script src="messageInput.js" defer></script>
|
<script src="messageInput.js" defer></script>
|
||||||
<script src="marked.min.js"></script>
|
<script src="markdown-it.min.js"></script>
|
||||||
<script src="messagesArea.js" defer></script>
|
<script src="messagesArea.js" defer></script>
|
||||||
<script src="controller.js" defer></script>
|
<script src="controller.js" defer></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
2
api_webchat/markdown-it.min.js
vendored
Normal file
2
api_webchat/markdown-it.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
api_webchat/marked.min.js
vendored
6
api_webchat/marked.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +1,7 @@
|
||||||
const messageInputTemplate = document.createElement('template');
|
const messageInputTemplate = document.createElement('template');
|
||||||
messageInputTemplate.innerHTML = `
|
|
||||||
<style>
|
const messagesInputStyle = document.createElement('style');
|
||||||
|
messagesInputStyle .textContent = `
|
||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
@ -11,15 +12,15 @@ messageInputTemplate.innerHTML = `
|
||||||
}
|
}
|
||||||
#messageInputField {
|
#messageInputField {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding: 10px; /* Increased padding */
|
padding: 10px;
|
||||||
font-size: 1rem; /* Adjusted font size to match message output */
|
font-size: 1rem;
|
||||||
border: 1px solid lightgrey; /* Light grey border */
|
border: 1px solid lightgrey;
|
||||||
border-radius: 10px; /* More rounded edges */
|
border-radius: 10px;
|
||||||
margin-right: var(--padding);
|
margin-right: var(--padding);
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
#messageInputField:focus {
|
#messageInputField:focus {
|
||||||
border-color: darkgrey; /* Dark grey border on focus */
|
border-color: darkgrey;
|
||||||
}
|
}
|
||||||
#sendButton {
|
#sendButton {
|
||||||
width: 44px;
|
width: 44px;
|
||||||
|
|
@ -28,16 +29,41 @@ messageInputTemplate.innerHTML = `
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 2px solid darkgrey;
|
border: 2px solid darkgrey;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
<input type="text" id="messageInputField" placeholder="" focus="true" autocomplete="off">
|
|
||||||
<button id="sendButton"><span class="" height="24" width="24" data-state="closed"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" class="text-white dark:text-black"><path d="M7 11L12 6L17 11M12 18V7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></span></button>
|
|
||||||
`;
|
`;
|
||||||
|
messageInputTemplate.content.appendChild(messagesInputStyle);
|
||||||
|
|
||||||
|
const inputField = document.createElement('input');
|
||||||
|
inputField.type = 'text';
|
||||||
|
inputField.id = 'messageInputField';
|
||||||
|
inputField.placeholder = '';
|
||||||
|
inputField.autocomplete = 'off';
|
||||||
|
messageInputTemplate.content.appendChild(inputField);
|
||||||
|
|
||||||
|
const sendButton = document.createElement('button');
|
||||||
|
sendButton.id = 'sendButton';
|
||||||
|
|
||||||
|
const sendIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||||
|
sendIcon.setAttribute('width', '24');
|
||||||
|
sendIcon.setAttribute('height', '24');
|
||||||
|
sendIcon.setAttribute('viewBox', '0 0 24 24');
|
||||||
|
sendIcon.setAttribute('fill', 'none');
|
||||||
|
sendIcon.classList.add('text-white', 'dark:text-black');
|
||||||
|
|
||||||
|
const sendPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||||
|
sendPath.setAttribute('d', 'M7 11L12 6L17 11M12 18V7');
|
||||||
|
sendPath.setAttribute('stroke', 'currentColor');
|
||||||
|
sendPath.setAttribute('stroke-width', '2');
|
||||||
|
sendPath.setAttribute('stroke-linecap', 'round');
|
||||||
|
sendPath.setAttribute('stroke-linejoin', 'round');
|
||||||
|
|
||||||
|
sendIcon.appendChild(sendPath);
|
||||||
|
sendButton.appendChild(sendIcon);
|
||||||
|
messageInputTemplate.content.appendChild(sendButton);
|
||||||
|
|
||||||
// stop button <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="h-2 w-2 text-gizmo-gray-950 dark:text-gray-200" height="16" width="16"><path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2z" stroke-width="0"></path></svg>
|
|
||||||
class MessageInput extends HTMLElement {
|
class MessageInput extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
const shadowRoot = this.attachShadow({mode: 'open'});
|
const shadowRoot = this.attachShadow({ mode: 'open' });
|
||||||
shadowRoot.appendChild(messageInputTemplate.content.cloneNode(true));
|
shadowRoot.appendChild(messageInputTemplate.content.cloneNode(true));
|
||||||
|
|
||||||
this._messageInputField = shadowRoot.querySelector('#messageInputField');
|
this._messageInputField = shadowRoot.querySelector('#messageInputField');
|
||||||
|
|
@ -88,4 +114,5 @@ class MessageInput extends HTMLElement {
|
||||||
this.worker.postMessage({ type: 'chatMessage', message: messageContent });
|
this.worker.postMessage({ type: 'chatMessage', message: messageContent });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('message-input', MessageInput);
|
customElements.define('message-input', MessageInput);
|
||||||
|
|
@ -1,28 +1,28 @@
|
||||||
const messagesAreaTemplate = document.createElement('template');
|
const messagesAreaTemplate = document.createElement('template');
|
||||||
messagesAreaTemplate.innerHTML = `
|
|
||||||
<style>
|
const messagesAreaStyle = document.createElement('style');
|
||||||
|
messagesAreaStyle.textContent = `
|
||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%; /* Adjust as needed */
|
height: 100%;
|
||||||
overflow-y: auto; /* Makes the area scrollable */
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
#messages {
|
#messages {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-end; /* Align messages to the bottom */
|
justify-content: flex-end;
|
||||||
min-height: 100%; /* Ensure it takes the full height */
|
min-height: 100%;
|
||||||
margin: var(--margin);
|
margin: var(--margin);
|
||||||
}
|
}
|
||||||
.message {
|
.message {
|
||||||
margin-bottom: var(--margin);
|
margin-bottom: var(--margin);
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token {
|
.token {
|
||||||
display: inline; /* Tokens are inline elements */
|
display: inline;
|
||||||
opacity: 0; /* Start with the token invisible */
|
opacity: 0;
|
||||||
animation: fadeIn 1000ms forwards; /* Apply the fade-in animation */
|
animation: fadeIn 1000ms forwards;
|
||||||
}
|
}
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
to {
|
to {
|
||||||
|
|
@ -34,19 +34,19 @@ messagesAreaTemplate.innerHTML = `
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<div id="messages"></div>
|
|
||||||
`;
|
`;
|
||||||
|
messagesAreaTemplate.content.appendChild(messagesAreaStyle);
|
||||||
|
|
||||||
|
const messagesDiv = document.createElement('div');
|
||||||
|
messagesDiv.id = 'messages';
|
||||||
|
messagesAreaTemplate.content.appendChild(messagesDiv);
|
||||||
|
|
||||||
class MessagesArea extends HTMLElement {
|
class MessagesArea extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.accumulatingMessageEl = null; // No initial message container
|
this.accumulatingMessageEl = null;
|
||||||
|
|
||||||
const shadowRoot = this.attachShadow({mode: 'open'});
|
const shadowRoot = this.attachShadow({ mode: 'open' });
|
||||||
shadowRoot.appendChild(messagesAreaTemplate.content.cloneNode(true));
|
shadowRoot.appendChild(messagesAreaTemplate.content.cloneNode(true));
|
||||||
|
|
||||||
this.messages = shadowRoot.querySelector('#messages');
|
this.messages = shadowRoot.querySelector('#messages');
|
||||||
|
|
@ -63,7 +63,7 @@ class MessagesArea extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.accumulatingMessageEl = document.createElement('div');
|
this.accumulatingMessageEl = document.createElement('div');
|
||||||
this.accumulatingMessageEl.classList.add('message', 'bot'); // Tag as 'bot' message
|
this.accumulatingMessageEl.classList.add('message', 'bot');
|
||||||
this.messages.appendChild(this.accumulatingMessageEl);
|
this.messages.appendChild(this.accumulatingMessageEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,13 +76,13 @@ class MessagesArea extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
appendUserMessage(messageText, source="You") {
|
appendUserMessage(messageText, source="You") {
|
||||||
console.log("appendUserMessage: "+messageText);
|
console.log("appendUserMessage: " + messageText);
|
||||||
const header = document.createElement('h2');
|
const header = document.createElement('h2');
|
||||||
header.textContent = source;
|
header.textContent = source;
|
||||||
this.messages.appendChild(header);
|
this.messages.appendChild(header);
|
||||||
|
|
||||||
const messageElement = document.createElement('div');
|
const messageElement = document.createElement('div');
|
||||||
messageElement.classList.add('message', 'user'); // Tag as 'user' message
|
messageElement.classList.add('message', 'user');
|
||||||
messageElement.textContent = messageText;
|
messageElement.textContent = messageText;
|
||||||
this.messages.appendChild(messageElement);
|
this.messages.appendChild(messageElement);
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
|
|
@ -108,22 +108,27 @@ class MessagesArea extends HTMLElement {
|
||||||
scrollToBottom() {
|
scrollToBottom() {
|
||||||
this.messages.scrollTop = this.messages.scrollHeight;
|
this.messages.scrollTop = this.messages.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
flushAccumulatingMessage() {
|
flushAccumulatingMessage() {
|
||||||
if (this.accumulatingMessageEl) {
|
if (this.accumulatingMessageEl) {
|
||||||
// remove all the animation spans and interpret the text as markdown.
|
// Raccogliere tutti i token in un testo completo
|
||||||
let fullText = '';
|
let fullText = '';
|
||||||
this.accumulatingMessageEl.querySelectorAll('.token').forEach(tokenEl => {
|
this.accumulatingMessageEl.querySelectorAll('.token').forEach(tokenEl => {
|
||||||
fullText += tokenEl.textContent;
|
fullText += tokenEl.textContent;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convert Markdown to HTML
|
// Convertire Markdown in nodi DOM usando la libreria markdown-it
|
||||||
let htmlText = marked.parse(fullText);
|
const md = window.markdownit();
|
||||||
console.log("htmlText: "+htmlText);
|
const html = md.render(fullText);
|
||||||
this.accumulatingMessageEl.innerHTML = htmlText;
|
const tempDiv = document.createElement('div');
|
||||||
|
tempDiv.innerHTML = html;
|
||||||
|
this.accumulatingMessageEl.innerHTML = ''; // Rimuovere i token esistenti
|
||||||
|
Array.from(tempDiv.childNodes).forEach(node => {
|
||||||
|
this.accumulatingMessageEl.appendChild(node);
|
||||||
|
});
|
||||||
|
|
||||||
this.accumulatingMessageEl = null; // Clear the reference for the next message
|
this.accumulatingMessageEl = null;
|
||||||
}
|
}
|
||||||
//this.scrollToBottom();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue