Spaces:
Running
Running
Update static/appS.js
Browse files- static/appS.js +136 -274
static/appS.js
CHANGED
@@ -1,310 +1,172 @@
|
|
1 |
document.addEventListener('DOMContentLoaded', () => {
|
2 |
-
|
3 |
-
const GotitB = document.querySelector(".explainChoix button");
|
4 |
-
const explain = document.querySelector(".explainChoix");
|
5 |
-
const SummarizeInput = document.querySelector(".SummarizeInput");
|
6 |
-
const CaptionInput = document.querySelector(".CaptionInput");
|
7 |
const fileUpload = document.getElementById('file-upload');
|
8 |
const imageUpload = document.getElementById('image-upload');
|
9 |
const fileBtn = document.getElementById('file-btn');
|
10 |
const imageBtn = document.getElementById('image-btn');
|
11 |
-
const
|
12 |
|
13 |
-
|
14 |
-
|
15 |
-
explain.style.opacity = "0";
|
16 |
-
});
|
17 |
-
|
18 |
-
// Handle mode switching
|
19 |
-
document.querySelectorAll('.select-options input[type="radio"]').forEach(radio => {
|
20 |
-
radio.addEventListener('change', (e) => {
|
21 |
-
if (e.target.checked) {
|
22 |
-
const selectedValue = e.target.value;
|
23 |
-
if (selectedValue === "Summarize") {
|
24 |
-
SummarizeInput.classList.add("active");
|
25 |
-
SummarizeInput.classList.remove("innactive");
|
26 |
-
CaptionInput.classList.remove("active");
|
27 |
-
CaptionInput.classList.add("innactive");
|
28 |
-
} else {
|
29 |
-
SummarizeInput.classList.add("innactive");
|
30 |
-
SummarizeInput.classList.remove("active");
|
31 |
-
CaptionInput.classList.remove("innactive");
|
32 |
-
CaptionInput.classList.add("active");
|
33 |
-
}
|
34 |
-
}
|
35 |
-
});
|
36 |
-
});
|
37 |
|
38 |
// File upload handlers
|
39 |
fileBtn.addEventListener('click', () => fileUpload.click());
|
40 |
imageBtn.addEventListener('click', () => imageUpload.click());
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
if (isSummarizeMode) {
|
48 |
-
await handleSummarize();
|
49 |
-
} else {
|
50 |
-
await handleCaption();
|
51 |
-
}
|
52 |
-
});
|
53 |
});
|
54 |
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
displayError('Please upload a document first');
|
60 |
-
return;
|
61 |
}
|
|
|
62 |
|
63 |
-
|
|
|
64 |
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
// Display results
|
75 |
-
displaySummaryResult(file.name, result.summary, result.audio_url, result.pdf_url);
|
76 |
-
} catch (error) {
|
77 |
-
displayError(error.message || 'Failed to summarize document');
|
78 |
-
}
|
79 |
-
}
|
80 |
-
|
81 |
-
// Handle image captioning
|
82 |
-
async function handleCaption() {
|
83 |
-
const file = imageUpload.files[0];
|
84 |
-
if (!file) {
|
85 |
-
displayError('Please upload an image first');
|
86 |
-
return;
|
87 |
-
}
|
88 |
-
|
89 |
-
try {
|
90 |
-
// Show loading state
|
91 |
-
convo.innerHTML = '';
|
92 |
-
displayFileInfo(file.name, 'image');
|
93 |
-
displayThinkingMessage();
|
94 |
-
|
95 |
-
// Call API
|
96 |
-
const result = await captionImage(file);
|
97 |
-
|
98 |
-
// Display results
|
99 |
-
displayCaptionResult(file.name, result.answer, result.audio);
|
100 |
-
} catch (error) {
|
101 |
-
displayError(error.message || 'Failed to generate caption');
|
102 |
-
}
|
103 |
}
|
104 |
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
formData.append('file', file);
|
109 |
-
formData.append('length', length);
|
110 |
-
|
111 |
-
const response = await fetch('/summarize/', {
|
112 |
-
method: 'POST',
|
113 |
-
body: formData
|
114 |
-
});
|
115 |
-
|
116 |
-
if (!response.ok) {
|
117 |
-
const error = await response.json();
|
118 |
-
throw new Error(error.error || 'Summarization failed');
|
119 |
-
}
|
120 |
-
|
121 |
-
return await response.json();
|
122 |
-
}
|
123 |
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
|
128 |
-
const
|
129 |
-
|
130 |
-
body: formData
|
131 |
-
});
|
132 |
|
133 |
-
if (
|
134 |
-
|
135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
}
|
137 |
-
|
138 |
-
return await response.json();
|
139 |
-
}
|
140 |
-
|
141 |
-
// UI Helper Functions
|
142 |
-
function displayFileInfo(filename, type) {
|
143 |
-
const bubble = document.createElement('div');
|
144 |
-
bubble.className = 'bubble right';
|
145 |
-
bubble.innerHTML = `
|
146 |
-
<div class="label">You</div>
|
147 |
-
<div class="text">${type === 'document' ? '📄' : '🖼️'} ${filename}</div>
|
148 |
-
`;
|
149 |
-
convo.appendChild(bubble);
|
150 |
-
}
|
151 |
|
152 |
-
|
153 |
-
|
154 |
-
bubble.className = 'bubble left';
|
155 |
-
bubble.innerHTML = `
|
156 |
-
<div class="label">Aidan</div>
|
157 |
-
<div class="text">
|
158 |
-
<div style="display:flex;align-items:center;gap:8px">
|
159 |
-
<span>Processing your ${document.querySelector('input[name="option"]:checked').value.toLowerCase()}...</span>
|
160 |
-
<div class="loader"></div>
|
161 |
-
</div>
|
162 |
-
</div>
|
163 |
-
`;
|
164 |
convo.appendChild(bubble);
|
165 |
convo.scrollTop = convo.scrollHeight;
|
|
|
166 |
}
|
167 |
|
168 |
-
function
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
bubble.className = 'bubble left';
|
174 |
-
bubble.innerHTML = `
|
175 |
-
<div class="label">Aidan</div>
|
176 |
-
<div class="text">
|
177 |
-
<strong>Summary:</strong><br><br>
|
178 |
-
${summary.replace(/\n/g, '<br>')}
|
179 |
-
<div class="action-buttons" style="margin-top: 10px; display: flex; gap: 10px;">
|
180 |
-
${audioUrl ? `
|
181 |
-
<button class="audio-control" data-audio="${audioUrl}">
|
182 |
-
<i class="fa-solid fa-volume-high"></i> Audio
|
183 |
-
</button>` : ''}
|
184 |
-
${pdfUrl ? `
|
185 |
-
<a href="${pdfUrl}" download="${filename.split('.')[0]}_summary.pdf" class="download-btn">
|
186 |
-
<i class="fa-solid fa-download"></i> PDF
|
187 |
-
</a>` : ''}
|
188 |
-
</div>
|
189 |
-
</div>
|
190 |
-
`;
|
191 |
-
convo.appendChild(bubble);
|
192 |
-
setupAudioControls();
|
193 |
-
convo.scrollTop = convo.scrollHeight;
|
194 |
-
}
|
195 |
|
196 |
-
|
197 |
-
// Remove thinking message
|
198 |
-
convo.removeChild(convo.lastChild);
|
199 |
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
</button>
|
212 |
-
</div>` : ''}
|
213 |
-
</div>
|
214 |
-
`;
|
215 |
-
convo.appendChild(bubble);
|
216 |
-
setupAudioControls();
|
217 |
-
convo.scrollTop = convo.scrollHeight;
|
218 |
-
}
|
219 |
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
const audio = new Audio(audioUrl);
|
224 |
-
const icon = button.querySelector('i');
|
225 |
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
}
|
236 |
});
|
237 |
-
});
|
238 |
-
}
|
239 |
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
251 |
}
|
252 |
|
253 |
-
//
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
border: 2px solid #f3f3f3;
|
258 |
-
border-top: 2px solid #3b82f6;
|
259 |
-
border-radius: 50%;
|
260 |
-
width: 16px;
|
261 |
-
height: 16px;
|
262 |
-
animation: spin 1s linear infinite;
|
263 |
-
}
|
264 |
-
@keyframes spin {
|
265 |
-
0% { transform: rotate(0deg); }
|
266 |
-
100% { transform: rotate(360deg); }
|
267 |
-
}
|
268 |
-
.download-btn, .audio-control {
|
269 |
-
display: inline-flex;
|
270 |
-
align-items: center;
|
271 |
-
gap: 5px;
|
272 |
-
padding: 5px 10px;
|
273 |
-
background: #3b82f6;
|
274 |
-
color: white;
|
275 |
-
border: none;
|
276 |
-
border-radius: 5px;
|
277 |
-
cursor: pointer;
|
278 |
-
text-decoration: none;
|
279 |
-
font-size: 14px;
|
280 |
-
}
|
281 |
-
.download-btn:hover, .audio-control:hover {
|
282 |
-
background: #2563eb;
|
283 |
-
}
|
284 |
-
.bubble {
|
285 |
-
max-width: 80%;
|
286 |
-
padding: 12px 16px;
|
287 |
-
margin: 8px 0;
|
288 |
-
border-radius: 18px;
|
289 |
-
position: relative;
|
290 |
-
clear: both;
|
291 |
-
}
|
292 |
-
.bubble.left {
|
293 |
-
background: #f1f1f1;
|
294 |
-
float: left;
|
295 |
-
margin-right: auto;
|
296 |
-
}
|
297 |
-
.bubble.right {
|
298 |
-
background: #3b82f6;
|
299 |
-
color: white;
|
300 |
-
float: right;
|
301 |
-
margin-left: auto;
|
302 |
-
}
|
303 |
-
.label {
|
304 |
-
font-weight: bold;
|
305 |
-
font-size: 12px;
|
306 |
-
margin-bottom: 4px;
|
307 |
-
}
|
308 |
-
`;
|
309 |
-
document.head.appendChild(style);
|
310 |
});
|
|
|
1 |
document.addEventListener('DOMContentLoaded', () => {
|
2 |
+
const convo = document.querySelector(".convo");
|
|
|
|
|
|
|
|
|
3 |
const fileUpload = document.getElementById('file-upload');
|
4 |
const imageUpload = document.getElementById('image-upload');
|
5 |
const fileBtn = document.getElementById('file-btn');
|
6 |
const imageBtn = document.getElementById('image-btn');
|
7 |
+
const sendButtons = document.querySelectorAll('.sendingQA');
|
8 |
|
9 |
+
let selectedFile = null;
|
10 |
+
let filePreviewBubble = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
// File upload handlers
|
13 |
fileBtn.addEventListener('click', () => fileUpload.click());
|
14 |
imageBtn.addEventListener('click', () => imageUpload.click());
|
15 |
|
16 |
+
fileUpload.addEventListener('change', (e) => {
|
17 |
+
if (e.target.files.length) {
|
18 |
+
selectedFile = e.target.files[0];
|
19 |
+
displayFilePreview(selectedFile);
|
20 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
});
|
22 |
|
23 |
+
imageUpload.addEventListener('change', (e) => {
|
24 |
+
if (e.target.files.length) {
|
25 |
+
selectedFile = e.target.files[0];
|
26 |
+
displayFilePreview(selectedFile);
|
|
|
|
|
27 |
}
|
28 |
+
});
|
29 |
|
30 |
+
function displayFilePreview(file) {
|
31 |
+
if (filePreviewBubble) filePreviewBubble.remove();
|
32 |
|
33 |
+
filePreviewBubble = document.createElement('div');
|
34 |
+
filePreviewBubble.className = 'file-preview-bubble bubble right';
|
35 |
+
filePreviewBubble.innerHTML = `
|
36 |
+
<div class="label">You</div>
|
37 |
+
<div class="text">📎 Selected: ${file.name}</div>
|
38 |
+
`;
|
39 |
+
convo.appendChild(filePreviewBubble);
|
40 |
+
convo.scrollTop = convo.scrollHeight;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
}
|
42 |
|
43 |
+
function createMessageBubble(text, sender = "You", audioSrc = null) {
|
44 |
+
const bubble = document.createElement('div');
|
45 |
+
bubble.className = `bubble ${sender === "You" ? "right" : "left"}`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
+
const label = document.createElement('div');
|
48 |
+
label.className = "label";
|
49 |
+
label.innerText = sender;
|
50 |
|
51 |
+
const message = document.createElement('div');
|
52 |
+
message.className = "text";
|
|
|
|
|
53 |
|
54 |
+
if (sender === "You") {
|
55 |
+
message.innerText = text;
|
56 |
+
} else {
|
57 |
+
message.innerHTML = text;
|
58 |
+
|
59 |
+
if (audioSrc) {
|
60 |
+
const audioContainer = document.createElement('div');
|
61 |
+
audioContainer.style.display = 'flex';
|
62 |
+
audioContainer.style.alignItems = 'center';
|
63 |
+
audioContainer.style.gap = '10px';
|
64 |
+
audioContainer.style.marginTop = '8px';
|
65 |
+
|
66 |
+
const audio = new Audio(audioSrc);
|
67 |
+
const audioIcon = document.createElement('i');
|
68 |
+
audioIcon.className = 'fa-solid fa-volume-high audio-toggle';
|
69 |
+
audioIcon.style.cursor = 'pointer';
|
70 |
+
|
71 |
+
audioIcon.addEventListener('click', () => {
|
72 |
+
if (audio.paused) {
|
73 |
+
audio.play();
|
74 |
+
audioIcon.classList.remove('fa-volume-xmark');
|
75 |
+
audioIcon.classList.add('fa-volume-high');
|
76 |
+
} else {
|
77 |
+
audio.pause();
|
78 |
+
audioIcon.classList.remove('fa-volume-high');
|
79 |
+
audioIcon.classList.add('fa-volume-xmark');
|
80 |
+
}
|
81 |
+
});
|
82 |
+
|
83 |
+
audioContainer.appendChild(audioIcon);
|
84 |
+
audioContainer.appendChild(document.createTextNode('Listen'));
|
85 |
+
message.appendChild(audioContainer);
|
86 |
+
}
|
87 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
|
89 |
+
bubble.appendChild(label);
|
90 |
+
bubble.appendChild(message);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
convo.appendChild(bubble);
|
92 |
convo.scrollTop = convo.scrollHeight;
|
93 |
+
return bubble;
|
94 |
}
|
95 |
|
96 |
+
async function handleSend() {
|
97 |
+
if (!selectedFile) {
|
98 |
+
alert("Please upload a file first");
|
99 |
+
return;
|
100 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
|
102 |
+
const isSummarizeMode = document.querySelector('input[name="option"]:checked').value === 'Summarize';
|
|
|
|
|
103 |
|
104 |
+
// User message
|
105 |
+
createMessageBubble(
|
106 |
+
isSummarizeMode ? "Please summarize this document" : "Please caption this image",
|
107 |
+
"You"
|
108 |
+
);
|
109 |
+
|
110 |
+
// Thinking message
|
111 |
+
const thinkingBubble = createMessageBubble(
|
112 |
+
isSummarizeMode ? "Summarizing your document..." : "Generating caption for your image...",
|
113 |
+
"Aidan"
|
114 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
|
116 |
+
try {
|
117 |
+
const formData = new FormData();
|
118 |
+
formData.append('file', selectedFile);
|
|
|
|
|
119 |
|
120 |
+
if (isSummarizeMode) {
|
121 |
+
const length = document.querySelector('input[name="optionS"]:checked').value;
|
122 |
+
formData.append('length', length);
|
123 |
+
}
|
124 |
+
|
125 |
+
const endpoint = isSummarizeMode ? '/summarize/' : '/imagecaption/';
|
126 |
+
const response = await fetch(endpoint, {
|
127 |
+
method: 'POST',
|
128 |
+
body: formData
|
|
|
129 |
});
|
|
|
|
|
130 |
|
131 |
+
const result = await response.json();
|
132 |
+
|
133 |
+
// Replace thinking message with actual result
|
134 |
+
thinkingBubble.remove();
|
135 |
+
|
136 |
+
if (isSummarizeMode) {
|
137 |
+
createMessageBubble(
|
138 |
+
`<strong>Summary:</strong><br><br>${result.summary.replace(/\n/g, '<br>')}` +
|
139 |
+
(result.pdf_url ? `<br><br><a href="${result.pdf_url}" download target="_blank">Download PDF</a>` : ''),
|
140 |
+
"Aidan",
|
141 |
+
result.audio_url
|
142 |
+
);
|
143 |
+
} else {
|
144 |
+
createMessageBubble(
|
145 |
+
`<strong>Caption:</strong><br><br>${result.answer}`,
|
146 |
+
"Aidan",
|
147 |
+
result.audio
|
148 |
+
);
|
149 |
+
}
|
150 |
+
} catch (error) {
|
151 |
+
thinkingBubble.remove();
|
152 |
+
createMessageBubble(
|
153 |
+
"⚠️ Sorry, I encountered an error processing your request",
|
154 |
+
"Aidan"
|
155 |
+
);
|
156 |
+
console.error(error);
|
157 |
+
} finally {
|
158 |
+
selectedFile = null;
|
159 |
+
fileUpload.value = '';
|
160 |
+
imageUpload.value = '';
|
161 |
+
if (filePreviewBubble) {
|
162 |
+
filePreviewBubble.remove();
|
163 |
+
filePreviewBubble = null;
|
164 |
+
}
|
165 |
+
}
|
166 |
}
|
167 |
|
168 |
+
// Attach send handlers
|
169 |
+
sendButtons.forEach(button => {
|
170 |
+
button.addEventListener('click', handleSend);
|
171 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
});
|