CaffeinatedCoding commited on
Commit
8e2a32f
·
verified ·
1 Parent(s): 59360e5

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. frontend/app.js +75 -56
  2. 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 = document.getElementById("sources-panel");
228
  const overlay = document.getElementById("sources-overlay");
229
- const body = document.getElementById("sources-panel-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, "&#39;").replace(/"/g, "&quot;");
288
  }
289
 
 
290
  function formatAnswer(text) {
291
  if (!text) return "";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
 
293
- let html = text
294
  // Headers
295
- .replace(/^### (.+)$/gm, '<h3>$1</h3>')
296
- .replace(/^## (.+)$/gm, '<h2>$1</h2>')
297
- .replace(/^# (.+)$/gm, '<h1>$1</h1>')
298
 
299
- // Bold and italic
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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, "&#39;").replace(/"/g, "&quot;");
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, '&amp;')
362
+ .replace(/</g, '&lt;')
363
+ .replace(/>/g, '&gt;')
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>