Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- frontend/app.js +75 -56
- frontend/index.html +1 -1
frontend/app.js
CHANGED
|
@@ -96,14 +96,12 @@ async function submitQuery() {
|
|
| 96 |
if (query.length < 10) { showToast("Query too short — minimum 10 characters."); return; }
|
| 97 |
if (query.length > 1000) { showToast("Query too long — maximum 1000 characters."); return; }
|
| 98 |
|
| 99 |
-
// First message in this chat — create session and switch screen
|
| 100 |
if (!activeSessionId) {
|
| 101 |
createSession(query);
|
| 102 |
showScreen("chat");
|
| 103 |
document.getElementById("topbar-title").textContent = getActiveSession().title;
|
| 104 |
}
|
| 105 |
|
| 106 |
-
// Save user message to session
|
| 107 |
getActiveSession().messages.push({ role: "user", text: query });
|
| 108 |
|
| 109 |
textarea.value = "";
|
|
@@ -224,9 +222,9 @@ function removeLoader(id) {
|
|
| 224 |
|
| 225 |
// ── Sources panel ────────────────────────────────────────────────
|
| 226 |
function openSources(sources) {
|
| 227 |
-
const panel
|
| 228 |
const overlay = document.getElementById("sources-overlay");
|
| 229 |
-
const body
|
| 230 |
|
| 231 |
body.innerHTML = sources.map((s, i) => {
|
| 232 |
const meta = s.meta || {};
|
|
@@ -244,7 +242,6 @@ function openSources(sources) {
|
|
| 244 |
|
| 245 |
panel.classList.add("open");
|
| 246 |
overlay.classList.add("open");
|
| 247 |
-
// Trigger CSS transition
|
| 248 |
requestAnimationFrame(() => { panel.style.transform = "translateX(0)"; });
|
| 249 |
}
|
| 250 |
|
|
@@ -287,66 +284,88 @@ function escAttr(str) {
|
|
| 287 |
return String(str || "").replace(/'/g, "'").replace(/"/g, """);
|
| 288 |
}
|
| 289 |
|
|
|
|
| 290 |
function formatAnswer(text) {
|
| 291 |
if (!text) return "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
|
| 293 |
-
let html = text
|
| 294 |
// Headers
|
| 295 |
-
.
|
| 296 |
-
.
|
| 297 |
-
.
|
| 298 |
|
| 299 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
| 301 |
.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
| 302 |
-
|
| 303 |
-
// Numbered lists
|
| 304 |
-
.replace(/^\d+\.\s+(.+)$/gm, '<li class="numbered">$1</li>')
|
| 305 |
-
|
| 306 |
-
// Bullet lists
|
| 307 |
-
.replace(/^[-•]\s+(.+)$/gm, '<li class="bullet">$1</li>')
|
| 308 |
-
|
| 309 |
-
// Tables — | col | col |
|
| 310 |
-
.replace(/\|(.+)\|/g, (match) => {
|
| 311 |
-
const cells = match.split('|').filter(c => c.trim());
|
| 312 |
-
return '<tr>' + cells.map(c =>
|
| 313 |
-
c.trim().match(/^[-]+$/)
|
| 314 |
-
? ''
|
| 315 |
-
: `<td>${c.trim()}</td>`
|
| 316 |
-
).join('') + '</tr>';
|
| 317 |
-
})
|
| 318 |
-
|
| 319 |
-
// Inline code
|
| 320 |
-
.replace(/`(.+?)`/g, '<code>$1</code>')
|
| 321 |
-
|
| 322 |
-
// Double newline = paragraph break
|
| 323 |
-
.replace(/\n\n/g, '</p><p>')
|
| 324 |
-
|
| 325 |
-
// Single newline = line break
|
| 326 |
-
.replace(/\n/g, '<br>');
|
| 327 |
-
|
| 328 |
-
// Wrap consecutive <li class="numbered"> in <ol>
|
| 329 |
-
html = html.replace(
|
| 330 |
-
/(<li class="numbered">.*?<\/li>)+/gs,
|
| 331 |
-
(match) => `<ol>${match.replace(/class="numbered"/g, '')}</ol>`
|
| 332 |
-
);
|
| 333 |
-
|
| 334 |
-
// Wrap consecutive <li class="bullet"> in <ul>
|
| 335 |
-
html = html.replace(
|
| 336 |
-
/(<li class="bullet">.*?<\/li>)+/gs,
|
| 337 |
-
(match) => `<ul>${match.replace(/class="bullet"/g, '')}</ul>`
|
| 338 |
-
);
|
| 339 |
-
|
| 340 |
-
// Wrap table rows in <table>
|
| 341 |
-
html = html.replace(
|
| 342 |
-
/(<tr>.*?<\/tr>)+/gs,
|
| 343 |
-
(match) => `<table class="answer-table">${match}</table>`
|
| 344 |
-
);
|
| 345 |
-
|
| 346 |
-
return `<p>${html}</p>`;
|
| 347 |
}
|
| 348 |
|
| 349 |
function showToast(msg) {
|
| 350 |
-
// Simple alert fallback — can be styled later
|
| 351 |
alert(msg);
|
| 352 |
}
|
|
|
|
| 96 |
if (query.length < 10) { showToast("Query too short — minimum 10 characters."); return; }
|
| 97 |
if (query.length > 1000) { showToast("Query too long — maximum 1000 characters."); return; }
|
| 98 |
|
|
|
|
| 99 |
if (!activeSessionId) {
|
| 100 |
createSession(query);
|
| 101 |
showScreen("chat");
|
| 102 |
document.getElementById("topbar-title").textContent = getActiveSession().title;
|
| 103 |
}
|
| 104 |
|
|
|
|
| 105 |
getActiveSession().messages.push({ role: "user", text: query });
|
| 106 |
|
| 107 |
textarea.value = "";
|
|
|
|
| 222 |
|
| 223 |
// ── Sources panel ────────────────────────────────────────────────
|
| 224 |
function openSources(sources) {
|
| 225 |
+
const panel = document.getElementById("sources-panel");
|
| 226 |
const overlay = document.getElementById("sources-overlay");
|
| 227 |
+
const body = document.getElementById("sources-panel-body");
|
| 228 |
|
| 229 |
body.innerHTML = sources.map((s, i) => {
|
| 230 |
const meta = s.meta || {};
|
|
|
|
| 242 |
|
| 243 |
panel.classList.add("open");
|
| 244 |
overlay.classList.add("open");
|
|
|
|
| 245 |
requestAnimationFrame(() => { panel.style.transform = "translateX(0)"; });
|
| 246 |
}
|
| 247 |
|
|
|
|
| 284 |
return String(str || "").replace(/'/g, "'").replace(/"/g, """);
|
| 285 |
}
|
| 286 |
|
| 287 |
+
// ── Answer formatter ─────────────────────────────────────────────
|
| 288 |
function formatAnswer(text) {
|
| 289 |
if (!text) return "";
|
| 290 |
+
text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
| 291 |
+
|
| 292 |
+
const lines = text.split('\n');
|
| 293 |
+
let html = '';
|
| 294 |
+
let inTable = false;
|
| 295 |
+
let tableHtml = '';
|
| 296 |
+
let inList = false;
|
| 297 |
+
let listType = '';
|
| 298 |
+
|
| 299 |
+
for (let i = 0; i < lines.length; i++) {
|
| 300 |
+
const line = lines[i];
|
| 301 |
+
|
| 302 |
+
// Table row
|
| 303 |
+
if (line.trim().startsWith('|')) {
|
| 304 |
+
if (line.match(/^\|[\s\-|]+\|$/)) continue; // skip separator rows
|
| 305 |
+
if (!inTable) { tableHtml = '<table class="answer-table">'; inTable = true; }
|
| 306 |
+
const cells = line.split('|').filter((c, idx, a) => idx > 0 && idx < a.length - 1);
|
| 307 |
+
tableHtml += '<tr>' + cells.map(c => `<td>${inline(c.trim())}</td>`).join('') + '</tr>';
|
| 308 |
+
continue;
|
| 309 |
+
} else if (inTable) {
|
| 310 |
+
html += tableHtml + '</table>';
|
| 311 |
+
tableHtml = ''; inTable = false;
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
// Numbered list
|
| 315 |
+
if (line.match(/^\d+\.\s+/)) {
|
| 316 |
+
if (!inList || listType !== 'ol') {
|
| 317 |
+
if (inList) html += `</${listType}>`;
|
| 318 |
+
html += '<ol>'; inList = true; listType = 'ol';
|
| 319 |
+
}
|
| 320 |
+
html += `<li>${inline(line.replace(/^\d+\.\s+/, ''))}</li>`;
|
| 321 |
+
continue;
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
// Bullet list
|
| 325 |
+
if (line.match(/^[\*\-]\s+/)) {
|
| 326 |
+
if (!inList || listType !== 'ul') {
|
| 327 |
+
if (inList) html += `</${listType}>`;
|
| 328 |
+
html += '<ul>'; inList = true; listType = 'ul';
|
| 329 |
+
}
|
| 330 |
+
html += `<li>${inline(line.replace(/^[\*\-]\s+/, ''))}</li>`;
|
| 331 |
+
continue;
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
// Close list on blank line
|
| 335 |
+
if (inList && line.trim() === '') {
|
| 336 |
+
html += `</${listType}>`;
|
| 337 |
+
inList = false; listType = '';
|
| 338 |
+
}
|
| 339 |
|
|
|
|
| 340 |
// Headers
|
| 341 |
+
if (line.startsWith('### ')) { html += `<h3>${inline(line.slice(4))}</h3>`; continue; }
|
| 342 |
+
if (line.startsWith('## ')) { html += `<h2>${inline(line.slice(3))}</h2>`; continue; }
|
| 343 |
+
if (line.startsWith('# ')) { html += `<h1>${inline(line.slice(2))}</h1>`; continue; }
|
| 344 |
|
| 345 |
+
// Blank line
|
| 346 |
+
if (line.trim() === '') { html += '<br>'; continue; }
|
| 347 |
+
|
| 348 |
+
// Normal paragraph line
|
| 349 |
+
html += `<p>${inline(line)}</p>`;
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
// Close any unclosed tags
|
| 353 |
+
if (inTable) html += tableHtml + '</table>';
|
| 354 |
+
if (inList) html += `</${listType}>`;
|
| 355 |
+
|
| 356 |
+
return html;
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
function inline(text) {
|
| 360 |
+
return String(text || '')
|
| 361 |
+
.replace(/&/g, '&')
|
| 362 |
+
.replace(/</g, '<')
|
| 363 |
+
.replace(/>/g, '>')
|
| 364 |
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
| 365 |
.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
| 366 |
+
.replace(/`(.+?)`/g, '<code>$1</code>');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 367 |
}
|
| 368 |
|
| 369 |
function showToast(msg) {
|
|
|
|
| 370 |
alert(msg);
|
| 371 |
}
|
frontend/index.html
CHANGED
|
@@ -120,6 +120,6 @@
|
|
| 120 |
</div>
|
| 121 |
</div>
|
| 122 |
|
| 123 |
-
<script src="/static/app.js"></script>
|
| 124 |
</body>
|
| 125 |
</html>
|
|
|
|
| 120 |
</div>
|
| 121 |
</div>
|
| 122 |
|
| 123 |
+
<script src="/static/app.js?v=2"></script>
|
| 124 |
</body>
|
| 125 |
</html>
|