Spaces:
Sleeping
Sleeping
Update application/static/js/components/uiManager.js
Browse files
application/static/js/components/uiManager.js
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import Initialize from "./initialize.js";
|
2 |
import Chat from "./chat.js";
|
3 |
import RenderSymbols from "./renderSymbols.js";
|
@@ -10,7 +11,7 @@ class UIManager{
|
|
10 |
this.hamburger = document.getElementById('hamburger');
|
11 |
this.messagesDiv = document.getElementById('messages');
|
12 |
this.container = document.getElementById('container')
|
13 |
-
this.prevChatsCont = document.getElementById('prevChatsCont');
|
14 |
this.textBox = document.getElementById('textBox');
|
15 |
this.sendBtn = document.getElementById('sendBtn');
|
16 |
this.newChat = document.getElementById('newChat');
|
@@ -24,6 +25,32 @@ class UIManager{
|
|
24 |
this.userP;
|
25 |
this.imageMode = false;
|
26 |
this.dialog = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
}
|
28 |
async run(){
|
29 |
await this.initializer.initialize();
|
@@ -35,11 +62,10 @@ class UIManager{
|
|
35 |
this.send();
|
36 |
})
|
37 |
window.addEventListener('keydown',(e)=>{
|
38 |
-
|
39 |
e.preventDefault();
|
40 |
this.showDialog();
|
41 |
}
|
42 |
-
|
43 |
if(e.key=='Enter' && !this.sendBtn.disabled){
|
44 |
this.send();
|
45 |
}
|
@@ -54,14 +80,14 @@ class UIManager{
|
|
54 |
this.webSearchBtn.style.color = 'rgba(30,30,250,0.8)';
|
55 |
}
|
56 |
this.webSearch = !this.webSearch;
|
57 |
-
|
58 |
})
|
59 |
document.getElementById('closeAlert').onclick = ()=>{
|
60 |
document.getElementById('alert').style.display = 'none'
|
61 |
}
|
62 |
}
|
63 |
|
64 |
-
|
65 |
if (this.dialog) {
|
66 |
this.dialog.remove();
|
67 |
this.dialog = null;
|
@@ -98,29 +124,22 @@ class UIManager{
|
|
98 |
});
|
99 |
}
|
100 |
|
101 |
-
|
102 |
-
|
103 |
async send(){
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
if (this.imageMode) {
|
108 |
const file = await this.getImageFile();
|
109 |
if (file) {
|
110 |
imageBase64 = await this.getBase64(file);
|
111 |
}
|
112 |
promptText = promptText.replace(/^\/image\s*/, '');
|
113 |
-
this.imageMode = false;
|
114 |
}
|
115 |
-
|
116 |
-
this.appendUserMsg(promptText, imageBase64);
|
117 |
this.appendAiMsg();
|
118 |
-
|
119 |
-
|
120 |
await this.chat.chat();
|
121 |
}
|
122 |
|
123 |
-
|
124 |
async getImageFile() {
|
125 |
return new Promise(resolve => {
|
126 |
const input = document.createElement('input');
|
@@ -133,8 +152,7 @@ class UIManager{
|
|
133 |
input.click();
|
134 |
});
|
135 |
}
|
136 |
-
|
137 |
-
async getBase64(file) {
|
138 |
return new Promise((resolve, reject) => {
|
139 |
const reader = new FileReader();
|
140 |
reader.readAsDataURL(file);
|
@@ -142,6 +160,44 @@ class UIManager{
|
|
142 |
reader.onerror = error => reject(error);
|
143 |
});
|
144 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
145 |
|
146 |
|
147 |
appendUserMsg(msg=false, imageBase64 = null){
|
@@ -198,5 +254,57 @@ class UIManager{
|
|
198 |
this.initializer.reInitialize(prevChat.id);
|
199 |
})
|
200 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
}
|
202 |
export default UIManager
|
|
|
1 |
+
// application/static/js/uiManager.js
|
2 |
import Initialize from "./initialize.js";
|
3 |
import Chat from "./chat.js";
|
4 |
import RenderSymbols from "./renderSymbols.js";
|
|
|
11 |
this.hamburger = document.getElementById('hamburger');
|
12 |
this.messagesDiv = document.getElementById('messages');
|
13 |
this.container = document.getElementById('container')
|
14 |
+
this.prevChatsCont = document.getElementById('prevChatsCont');
|
15 |
this.textBox = document.getElementById('textBox');
|
16 |
this.sendBtn = document.getElementById('sendBtn');
|
17 |
this.newChat = document.getElementById('newChat');
|
|
|
25 |
this.userP;
|
26 |
this.imageMode = false;
|
27 |
this.dialog = null;
|
28 |
+
|
29 |
+
// Add the image input element (hidden)
|
30 |
+
this.imageInput = document.createElement('input');
|
31 |
+
this.imageInput.type = 'file';
|
32 |
+
this.imageInput.id = 'imageInput';
|
33 |
+
this.imageInput.accept = 'image/*';
|
34 |
+
this.imageInput.style.display = 'none';
|
35 |
+
document.body.appendChild(this.imageInput); // Append to body
|
36 |
+
|
37 |
+
// Add the paperclip icon (label for the input)
|
38 |
+
this.imageUploadLabel = document.createElement('label');
|
39 |
+
this.imageUploadLabel.htmlFor = 'imageInput'; // Connect label to input
|
40 |
+
this.imageUploadLabel.className = 'image-upload-label';
|
41 |
+
this.imageUploadLabel.innerHTML = '<i class="fas fa-paperclip"></i>'; // FontAwesome icon
|
42 |
+
this.textBox.parentNode.insertBefore(this.imageUploadLabel, this.textBox);
|
43 |
+
|
44 |
+
// Event listener for the paperclip icon (image upload)
|
45 |
+
this.imageUploadLabel.addEventListener('click', () => {
|
46 |
+
this.imageInput.click(); // Trigger the file input
|
47 |
+
});
|
48 |
+
|
49 |
+
this.imageInput.addEventListener('change', () => {
|
50 |
+
this.handleImageUpload();
|
51 |
+
});
|
52 |
+
|
53 |
+
|
54 |
}
|
55 |
async run(){
|
56 |
await this.initializer.initialize();
|
|
|
62 |
this.send();
|
63 |
})
|
64 |
window.addEventListener('keydown',(e)=>{
|
65 |
+
if (e.key === '/' && document.activeElement === this.textBox) {
|
66 |
e.preventDefault();
|
67 |
this.showDialog();
|
68 |
}
|
|
|
69 |
if(e.key=='Enter' && !this.sendBtn.disabled){
|
70 |
this.send();
|
71 |
}
|
|
|
80 |
this.webSearchBtn.style.color = 'rgba(30,30,250,0.8)';
|
81 |
}
|
82 |
this.webSearch = !this.webSearch;
|
83 |
+
|
84 |
})
|
85 |
document.getElementById('closeAlert').onclick = ()=>{
|
86 |
document.getElementById('alert').style.display = 'none'
|
87 |
}
|
88 |
}
|
89 |
|
90 |
+
showDialog() {
|
91 |
if (this.dialog) {
|
92 |
this.dialog.remove();
|
93 |
this.dialog = null;
|
|
|
124 |
});
|
125 |
}
|
126 |
|
|
|
|
|
127 |
async send(){
|
128 |
+
let promptText = this.textBox.value;
|
129 |
+
let imageBase64 = null;
|
130 |
+
if (this.imageMode) {
|
|
|
131 |
const file = await this.getImageFile();
|
132 |
if (file) {
|
133 |
imageBase64 = await this.getBase64(file);
|
134 |
}
|
135 |
promptText = promptText.replace(/^\/image\s*/, '');
|
136 |
+
this.imageMode = false; // Reset image mode after processing
|
137 |
}
|
138 |
+
this.appendUserMsg(promptText, imageBase64); // Pass image data
|
|
|
139 |
this.appendAiMsg();
|
|
|
|
|
140 |
await this.chat.chat();
|
141 |
}
|
142 |
|
|
|
143 |
async getImageFile() {
|
144 |
return new Promise(resolve => {
|
145 |
const input = document.createElement('input');
|
|
|
152 |
input.click();
|
153 |
});
|
154 |
}
|
155 |
+
async getBase64(file) {
|
|
|
156 |
return new Promise((resolve, reject) => {
|
157 |
const reader = new FileReader();
|
158 |
reader.readAsDataURL(file);
|
|
|
160 |
reader.onerror = error => reject(error);
|
161 |
});
|
162 |
}
|
163 |
+
async handleImageUpload() {
|
164 |
+
const file = this.imageInput.files[0];
|
165 |
+
if (!file) return;
|
166 |
+
|
167 |
+
this.imageMode = true; // Set image mode
|
168 |
+
|
169 |
+
const reader = new FileReader();
|
170 |
+
reader.onload = (e) => {
|
171 |
+
// Display image preview
|
172 |
+
if (this.userDiv) {
|
173 |
+
const imgPreview = document.createElement('img');
|
174 |
+
imgPreview.src = e.target.result;
|
175 |
+
imgPreview.className = 'uploaded-image-preview';
|
176 |
+
this.userDiv.appendChild(imgPreview); // Add to current userDiv
|
177 |
+
}
|
178 |
+
|
179 |
+
// Generate and display caption
|
180 |
+
this.generateAndDisplayCaption(e.target.result);
|
181 |
+
|
182 |
+
|
183 |
+
}
|
184 |
+
reader.readAsDataURL(file);
|
185 |
+
this.imageInput.value = ''; // Clear the input
|
186 |
+
}
|
187 |
+
|
188 |
+
async generateAndDisplayCaption(imageData) {
|
189 |
+
try {
|
190 |
+
const caption = await this.initializer.image_captioning.generate_caption(imageData);
|
191 |
+
if (this.userDiv) { // Check if userDiv exists.
|
192 |
+
const captionElement = document.createElement('div');
|
193 |
+
captionElement.className = 'image-caption';
|
194 |
+
captionElement.textContent = `Caption: ${caption}`;
|
195 |
+
this.userDiv.appendChild(captionElement);
|
196 |
+
}
|
197 |
+
} catch (error) {
|
198 |
+
console.error("Error generating caption:", error);
|
199 |
+
}
|
200 |
+
}
|
201 |
|
202 |
|
203 |
appendUserMsg(msg=false, imageBase64 = null){
|
|
|
254 |
this.initializer.reInitialize(prevChat.id);
|
255 |
})
|
256 |
}
|
257 |
+
|
258 |
+
addReadAloudButton(parentDiv) {
|
259 |
+
const btn = document.createElement('button');
|
260 |
+
btn.className = 'read-aloud-btn';
|
261 |
+
btn.innerHTML = '<i class="fas fa-volume-up"></i>'; // FontAwesome icon
|
262 |
+
btn.addEventListener('click', () => {
|
263 |
+
this.speakText(parentDiv.querySelector('p').innerText);
|
264 |
+
});
|
265 |
+
parentDiv.appendChild(btn);
|
266 |
+
}
|
267 |
+
|
268 |
+
|
269 |
+
async speakText(text) {
|
270 |
+
try {
|
271 |
+
const response = await fetch(`/tts?text=${encodeURIComponent(text)}`);
|
272 |
+
if (!response.ok) {
|
273 |
+
console.error("TTS request failed:", response.status, response.statusText);
|
274 |
+
return;
|
275 |
+
}
|
276 |
+
const audioBlob = await response.blob();
|
277 |
+
const audioUrl = URL.createObjectURL(audioBlob);
|
278 |
+
const audio = new Audio(audioUrl);
|
279 |
+
audio.play();
|
280 |
+
} catch (error) {
|
281 |
+
console.error("Error during TTS:", error);
|
282 |
+
}
|
283 |
+
}
|
284 |
+
|
285 |
+
displayMemory(memoryText) {
|
286 |
+
let memoryDisplay = document.querySelector('.memory-display');
|
287 |
+
if (!memoryDisplay) {
|
288 |
+
memoryDisplay = document.createElement('div');
|
289 |
+
memoryDisplay.className = 'memory-display';
|
290 |
+
this.messagesDiv.parentNode.insertBefore(memoryDisplay, this.messagesDiv); //show above messages
|
291 |
+
}
|
292 |
+
memoryDisplay.textContent = `Memory: ${memoryText}`;
|
293 |
+
}
|
294 |
+
showReasoning() {
|
295 |
+
const reasoningDisplay = document.createElement('div');
|
296 |
+
reasoningDisplay.className = 'reasoning-display';
|
297 |
+
reasoningDisplay.textContent = 'Reasoning...';
|
298 |
+
reasoningDisplay.dataset.startTime = Date.now(); // Store start time
|
299 |
+
this.messagesDiv.appendChild(reasoningDisplay); // Add to messages
|
300 |
+
return reasoningDisplay;
|
301 |
+
}
|
302 |
+
|
303 |
+
updateReasoningTime(reasoningDisplay) {
|
304 |
+
const endTime = Date.now();
|
305 |
+
const startTime = parseInt(reasoningDisplay.dataset.startTime, 10);
|
306 |
+
const duration = (endTime - startTime) / 1000; // in seconds
|
307 |
+
reasoningDisplay.textContent = `Reasoning... (${duration.toFixed(2)}s)`;
|
308 |
+
}
|
309 |
}
|
310 |
export default UIManager
|