Reality123b's picture
Update application/static/js/components/uiManager.js
aa7dcbd verified
// application/static/js/components/uiManager.js
import Initialize from "./initialize.js";
import Chat from "./chat.js";
import RenderSymbols from "./renderSymbols.js";
class UIManager{
constructor(){
this.initializer = new Initialize(this);
this.chat = new Chat(this);
this.renderSymbols = new RenderSymbols();
this.menu = document.getElementById('menu');
this.hamburger = document.getElementById('hamburger');
this.messagesDiv = document.getElementById('messages');
this.container = document.getElementById('container')
this.prevChatsCont = document.getElementById('prevChatsCont');
this.textBox = document.getElementById('textBox');
this.sendBtn = document.getElementById('sendBtn');
this.newChat = document.getElementById('newChat');
this.models = document.getElementById('models');
this.initialized = false;
this.webSearchBtn = document.getElementById('webSearch');
this.webSearch = false;
this.aiDiv;
this.userDiv;
this.aiP;
this.userP;
this.imageMode = false;
this.dialog = null;
// Add the image input element (hidden)
this.imageInput = document.createElement('input');
this.imageInput.type = 'file';
this.imageInput.id = 'imageInput';
this.imageInput.accept = 'image/*';
this.imageInput.style.display = 'none';
document.body.appendChild(this.imageInput); // Append to body
// Add the paperclip icon (label for the input)
this.imageUploadLabel = document.createElement('label');
this.imageUploadLabel.htmlFor = 'imageInput'; // Connect label to input
this.imageUploadLabel.className = 'image-upload-label';
this.imageUploadLabel.innerHTML = '<i class="fas fa-paperclip"></i>'; // FontAwesome icon
this.textBox.parentNode.insertBefore(this.imageUploadLabel, this.textBox);
// Event listener for the paperclip icon (image upload)
this.imageUploadLabel.addEventListener('click', () => {
this.imageInput.click(); // Trigger the file input
});
this.imageInput.addEventListener('change', () => {
this.handleImageUpload();
});
console.log("UIManager constructor called"); // Log constructor
}
async run(){
console.log("UIManager run called"); // Log run
await this.initializer.initialize();
this.handleTextBoxHeight();
this.events();
}
events(){
this.sendBtn.addEventListener('click',()=>{
this.send();
})
window.addEventListener('keydown',(e)=>{
if (e.key === '/' && document.activeElement === this.textBox) {
e.preventDefault();
this.showDialog();
}
if(e.key=='Enter' && !this.sendBtn.disabled){
this.send();
}
})
this.newChat.addEventListener('click', async ()=>{
await this.initializer.initialize();
})
this.webSearchBtn.addEventListener('click', ()=>{
if(this.webSearch){
this.webSearchBtn.style.color = 'white';
} else{
this.webSearchBtn.style.color = 'rgba(30,30,250,0.8)';
}
this.webSearch = !this.webSearch;
})
document.getElementById('closeAlert').onclick = ()=>{
document.getElementById('alert').style.display = 'none'
}
}
showDialog() {
if (this.dialog) {
this.dialog.remove();
this.dialog = null;
return;
}
this.dialog = document.createElement('div');
this.dialog.style.position = 'absolute';
this.dialog.style.top = `${this.textBox.offsetTop - 100}px`;
this.dialog.style.left = `${this.textBox.offsetLeft}px`;
this.dialog.style.backgroundColor = 'white';
this.dialog.style.border = '1px solid black';
this.dialog.style.padding = '10px';
this.dialog.style.zIndex = '1000';
this.dialog.innerHTML = `
<button id="dialogImage">Image</button>
<button id="dialogSearch">Search</button>
`;
document.body.appendChild(this.dialog);
document.getElementById('dialogImage').addEventListener('click', () => {
this.imageMode = true;
this.textBox.value = "/image ";
this.dialog.remove();
this.dialog = null;
});
document.getElementById('dialogSearch').addEventListener('click', () => {
this.webSearch = true;
this.webSearchBtn.style.color = 'rgba(30,30,250,0.8)';
this.textBox.value = "/search ";
this.dialog.remove();
this.dialog = null;
});
}
async send(){
let promptText = this.textBox.value;
let imageBase64 = null;
if (this.imageMode) {
const file = await this.getImageFile();
if (file) {
imageBase64 = await this.getBase64(file);
}
promptText = promptText.replace(/^\/image\s*/, '');
this.imageMode = false; // Reset image mode after processing
}
this.appendUserMsg(promptText, imageBase64); // Pass image data
this.appendAiMsg();
await this.chat.chat();
}
async getImageFile() {
return new Promise(resolve => {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.onchange = e => {
const file = e.target.files[0];
resolve(file);
};
input.click();
});
}
async getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
async handleImageUpload() {
const file = this.imageInput.files[0];
if (!file) return;
this.imageMode = true; // Set image mode
const reader = new FileReader();
reader.onload = (e) => {
// Display image preview
if (this.userDiv) {
const imgPreview = document.createElement('img');
imgPreview.src = e.target.result;
imgPreview.className = 'uploaded-image-preview';
this.userDiv.appendChild(imgPreview); // Add to current userDiv
}
// Generate and display caption
this.generateAndDisplayCaption(e.target.result);
}
reader.readAsDataURL(file);
this.imageInput.value = ''; // Clear the input
}
async generateAndDisplayCaption(imageData) {
try {
const caption = await this.initializer.image_captioning.generate_caption(imageData);
if (this.userDiv) { // Check if userDiv exists.
const captionElement = document.createElement('div');
captionElement.className = 'image-caption';
captionElement.textContent = `Caption: ${caption}`;
this.userDiv.appendChild(captionElement);
}
} catch (error) {
console.error("Error generating caption:", error);
}
}
appendUserMsg(msg=false, imageBase64 = null){
this.userDiv = document.createElement('div');
this.userDiv.className = 'user';
this.userP = document.createElement('p');
if(msg){
this.userP.innerText = msg;
} else{
this.userP.innerText = this.textBox.value;
}
if (imageBase64) {
const img = document.createElement('img');
img.src = imageBase64;
img.style.maxWidth = '100%';
img.style.height = 'auto';
this.userDiv.appendChild(img);
}
this.userDiv.appendChild(this.userP);
this.messagesDiv.appendChild(this.userDiv);
}
appendAiMsg(msg=false){
this.aiDiv = document.createElement('div');
this.aiDiv.className = 'ai';
this.aiP = document.createElement('p');
if(msg){
this.aiP.innerText=msg;
}
this.aiDiv.appendChild(this.aiP);
this.messagesDiv.appendChild(this.aiDiv);
}
handleTextBoxHeight() {
this.textBox.oninput = () => {
this.textBox.style.height = 'auto';
if (this.textBox.scrollHeight <= 150) {
if(this.textBox.scrollHeight>60){
this.textBox.style.height = `${this.textBox.scrollHeight}px`;
}
} else {
this.textBox.style.height = '150px';
}
};
}
addChat(){
const prevChat = document.createElement('div');
prevChat.innerText = this.initializer.convTitle;
prevChat.className = 'prevChat';
prevChat.id = this.initializer.convId;
this.prevChatsCont.prepend(prevChat);
prevChat.style.backgroundColor = 'rgb(53, 53, 53)';
prevChat.addEventListener('click', ()=>{
this.initializer.reInitialize(prevChat.id);
})
}
addReadAloudButton(parentDiv) {
const btn = document.createElement('button');
btn.className = 'read-aloud-btn';
btn.innerHTML = '<i class="fas fa-volume-up"></i>'; // FontAwesome icon
btn.addEventListener('click', () => {
this.speakText(parentDiv.querySelector('p').innerText);
});
parentDiv.appendChild(btn);
}
async speakText(text) {
try {
const response = await fetch(`/tts?text=${encodeURIComponent(text)}`);
if (!response.ok) {
console.error("TTS request failed:", response.status, response.statusText);
return;
}
const audioBlob = await response.blob();
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
audio.play();
} catch (error) {
console.error("Error during TTS:", error);
}
}
displayMemory(memoryText) {
let memoryDisplay = document.querySelector('.memory-display');
if (!memoryDisplay) {
memoryDisplay = document.createElement('div');
memoryDisplay.className = 'memory-display';
this.messagesDiv.parentNode.insertBefore(memoryDisplay, this.messagesDiv); //show above messages
}
memoryDisplay.textContent = `Memory: ${memoryText}`;
}
showReasoning() {
const reasoningDisplay = document.createElement('div');
reasoningDisplay.className = 'reasoning-display';
reasoningDisplay.textContent = 'Reasoning...';
reasoningDisplay.dataset.startTime = Date.now(); // Store start time
this.messagesDiv.appendChild(reasoningDisplay); // Add to messages
return reasoningDisplay;
}
updateReasoningTime(reasoningDisplay) {
const endTime = Date.now();
const startTime = parseInt(reasoningDisplay.dataset.startTime, 10);
const duration = (endTime - startTime) / 1000; // in seconds
reasoningDisplay.textContent = `Reasoning... (${duration.toFixed(2)}s)`;
}
}
export default UIManager