Spaces:
Sleeping
Sleeping
Commit ·
3992b0b
1
Parent(s): c102985
Upd UI with report link
Browse files- static/index.html +4 -4
- static/script.js +63 -35
- static/styles.css +18 -0
static/index.html
CHANGED
|
@@ -203,15 +203,15 @@
|
|
| 203 |
<div class="chat-input-wrapper">
|
| 204 |
<textarea id="question" placeholder="Ask something about your documents..." rows="2" disabled></textarea>
|
| 205 |
<button id="ask" class="btn-primary" disabled>
|
| 206 |
-
<span class="btn-text">
|
| 207 |
<span class="btn-loading" style="display:none;">
|
| 208 |
<div class="spinner"></div>
|
| 209 |
Thinking...
|
| 210 |
</span>
|
| 211 |
</button>
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
</
|
| 215 |
</div>
|
| 216 |
<div class="chat-hint" id="chat-hint">
|
| 217 |
Upload documents first to start chatting
|
|
|
|
| 203 |
<div class="chat-input-wrapper">
|
| 204 |
<textarea id="question" placeholder="Ask something about your documents..." rows="2" disabled></textarea>
|
| 205 |
<button id="ask" class="btn-primary" disabled>
|
| 206 |
+
<span class="btn-text">Submit</span>
|
| 207 |
<span class="btn-loading" style="display:none;">
|
| 208 |
<div class="spinner"></div>
|
| 209 |
Thinking...
|
| 210 |
</span>
|
| 211 |
</button>
|
| 212 |
+
</div>
|
| 213 |
+
<div class="chat-underbar">
|
| 214 |
+
<a href="#" id="report-link" class="report-link" title="Generate report from selected document">Report</a>
|
| 215 |
</div>
|
| 216 |
<div class="chat-hint" id="chat-hint">
|
| 217 |
Upload documents first to start chatting
|
static/script.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
| 14 |
const askBtn = document.getElementById('ask');
|
| 15 |
const chatHint = document.getElementById('chat-hint');
|
| 16 |
const messages = document.getElementById('messages');
|
| 17 |
-
const
|
| 18 |
const loadingOverlay = document.getElementById('loading-overlay');
|
| 19 |
const loadingMessage = document.getElementById('loading-message');
|
| 20 |
|
|
@@ -101,9 +101,12 @@
|
|
| 101 |
} catch {}
|
| 102 |
});
|
| 103 |
}
|
| 104 |
-
// Report
|
| 105 |
-
if (
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
| 107 |
}
|
| 108 |
}
|
| 109 |
|
|
@@ -175,10 +178,10 @@
|
|
| 175 |
const files = data.files || [];
|
| 176 |
renderStoredFiles(files);
|
| 177 |
// Enable Report button when at least one file exists
|
| 178 |
-
if (
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
}
|
| 183 |
window.__sb_current_filenames = new Set((data.filenames || []).map(f => (f || '').toLowerCase()));
|
| 184 |
} catch {}
|
|
@@ -496,31 +499,42 @@
|
|
| 496 |
showButtonLoading(askBtn, true);
|
| 497 |
|
| 498 |
try {
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
// Add sources if available
|
| 519 |
-
if (data.sources && data.sources.length > 0) {
|
| 520 |
-
appendSources(data.sources);
|
| 521 |
}
|
| 522 |
} else {
|
| 523 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 524 |
}
|
| 525 |
} catch (error) {
|
| 526 |
thinkingMsg.remove();
|
|
@@ -536,6 +550,22 @@
|
|
| 536 |
}
|
| 537 |
}
|
| 538 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 539 |
function autoGrowTextarea() {
|
| 540 |
if (!questionInput) return;
|
| 541 |
questionInput.style.height = 'auto';
|
|
@@ -590,10 +620,8 @@
|
|
| 590 |
if (tgt && tgt.classList && tgt.classList.contains('pill') && tgt.parentElement && tgt.parentElement.id === 'stored-file-items') {
|
| 591 |
document.querySelectorAll('#stored-file-items .pill').forEach(el => el.classList.remove('active'));
|
| 592 |
tgt.classList.add('active');
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
reportToggle.classList.add('enabled');
|
| 596 |
-
}
|
| 597 |
}
|
| 598 |
});
|
| 599 |
|
|
|
|
| 14 |
const askBtn = document.getElementById('ask');
|
| 15 |
const chatHint = document.getElementById('chat-hint');
|
| 16 |
const messages = document.getElementById('messages');
|
| 17 |
+
const reportLink = document.getElementById('report-link');
|
| 18 |
const loadingOverlay = document.getElementById('loading-overlay');
|
| 19 |
const loadingMessage = document.getElementById('loading-message');
|
| 20 |
|
|
|
|
| 101 |
} catch {}
|
| 102 |
});
|
| 103 |
}
|
| 104 |
+
// Report link toggle
|
| 105 |
+
if (reportLink) {
|
| 106 |
+
reportLink.addEventListener('click', (e) => {
|
| 107 |
+
e.preventDefault();
|
| 108 |
+
toggleReportMode();
|
| 109 |
+
});
|
| 110 |
}
|
| 111 |
}
|
| 112 |
|
|
|
|
| 178 |
const files = data.files || [];
|
| 179 |
renderStoredFiles(files);
|
| 180 |
// Enable Report button when at least one file exists
|
| 181 |
+
if (reportLink) {
|
| 182 |
+
// Disable visually by muted color when no files
|
| 183 |
+
reportLink.style.pointerEvents = (files.length === 0) ? 'none' : 'auto';
|
| 184 |
+
reportLink.title = 'Generate report from selected document';
|
| 185 |
}
|
| 186 |
window.__sb_current_filenames = new Set((data.filenames || []).map(f => (f || '').toLowerCase()));
|
| 187 |
} catch {}
|
|
|
|
| 499 |
showButtonLoading(askBtn, true);
|
| 500 |
|
| 501 |
try {
|
| 502 |
+
// Branch: if report mode is active → call /report with textarea as instructions
|
| 503 |
+
if (isReportModeActive()) {
|
| 504 |
+
const filename = pickActiveFilename();
|
| 505 |
+
if (!filename) throw new Error('Please select a document to generate a report');
|
| 506 |
+
const form = new FormData();
|
| 507 |
+
form.append('user_id', user.user_id);
|
| 508 |
+
form.append('project_id', currentProject.project_id);
|
| 509 |
+
form.append('filename', filename);
|
| 510 |
+
form.append('outline_words', '200');
|
| 511 |
+
form.append('report_words', '1200');
|
| 512 |
+
form.append('instructions', question);
|
| 513 |
+
const response = await fetch('/report', { method: 'POST', body: form });
|
| 514 |
+
const data = await response.json();
|
| 515 |
+
if (response.ok) {
|
| 516 |
+
thinkingMsg.remove();
|
| 517 |
+
appendMessage('assistant', data.report_markdown || 'No report');
|
| 518 |
+
if (data.sources && data.sources.length) appendSources(data.sources);
|
| 519 |
+
} else {
|
| 520 |
+
throw new Error(data.detail || 'Failed to generate report');
|
|
|
|
|
|
|
|
|
|
| 521 |
}
|
| 522 |
} else {
|
| 523 |
+
const formData = new FormData();
|
| 524 |
+
formData.append('user_id', user.user_id);
|
| 525 |
+
formData.append('project_id', currentProject.project_id);
|
| 526 |
+
formData.append('question', question);
|
| 527 |
+
formData.append('k', '6');
|
| 528 |
+
const response = await fetch('/chat', { method: 'POST', body: formData });
|
| 529 |
+
const data = await response.json();
|
| 530 |
+
if (response.ok) {
|
| 531 |
+
thinkingMsg.remove();
|
| 532 |
+
appendMessage('assistant', data.answer || 'No answer received');
|
| 533 |
+
await saveChatMessage(user.user_id, currentProject.project_id, 'assistant', data.answer || 'No answer received');
|
| 534 |
+
if (data.sources && data.sources.length > 0) appendSources(data.sources);
|
| 535 |
+
} else {
|
| 536 |
+
throw new Error(data.detail || 'Failed to get answer');
|
| 537 |
+
}
|
| 538 |
}
|
| 539 |
} catch (error) {
|
| 540 |
thinkingMsg.remove();
|
|
|
|
| 550 |
}
|
| 551 |
}
|
| 552 |
|
| 553 |
+
function toggleReportMode() {
|
| 554 |
+
if (!reportLink) return;
|
| 555 |
+
reportLink.classList.toggle('active');
|
| 556 |
+
}
|
| 557 |
+
|
| 558 |
+
function isReportModeActive() {
|
| 559 |
+
return reportLink && reportLink.classList.contains('active');
|
| 560 |
+
}
|
| 561 |
+
|
| 562 |
+
function pickActiveFilename() {
|
| 563 |
+
const candidates = Array.from(document.querySelectorAll('#stored-file-items .pill'));
|
| 564 |
+
let active = candidates.find(el => el.classList.contains('active'));
|
| 565 |
+
if (!active && candidates.length) active = candidates[0];
|
| 566 |
+
return active ? active.textContent.trim() : '';
|
| 567 |
+
}
|
| 568 |
+
|
| 569 |
function autoGrowTextarea() {
|
| 570 |
if (!questionInput) return;
|
| 571 |
questionInput.style.height = 'auto';
|
|
|
|
| 620 |
if (tgt && tgt.classList && tgt.classList.contains('pill') && tgt.parentElement && tgt.parentElement.id === 'stored-file-items') {
|
| 621 |
document.querySelectorAll('#stored-file-items .pill').forEach(el => el.classList.remove('active'));
|
| 622 |
tgt.classList.add('active');
|
| 623 |
+
// Enable link visually
|
| 624 |
+
if (reportLink) reportLink.classList.add('active');
|
|
|
|
|
|
|
| 625 |
}
|
| 626 |
});
|
| 627 |
|
static/styles.css
CHANGED
|
@@ -833,6 +833,24 @@
|
|
| 833 |
resize: none;
|
| 834 |
}
|
| 835 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 836 |
#question:focus {
|
| 837 |
outline: none;
|
| 838 |
border-color: var(--accent);
|
|
|
|
| 833 |
resize: none;
|
| 834 |
}
|
| 835 |
|
| 836 |
+
.chat-underbar {
|
| 837 |
+
display: flex;
|
| 838 |
+
justify-content: flex-end;
|
| 839 |
+
margin-top: 6px;
|
| 840 |
+
}
|
| 841 |
+
|
| 842 |
+
.report-link {
|
| 843 |
+
color: var(--muted);
|
| 844 |
+
text-decoration: underline;
|
| 845 |
+
cursor: pointer;
|
| 846 |
+
user-select: none;
|
| 847 |
+
}
|
| 848 |
+
|
| 849 |
+
.report-link.active {
|
| 850 |
+
color: var(--accent);
|
| 851 |
+
font-weight: 600;
|
| 852 |
+
}
|
| 853 |
+
|
| 854 |
#question:focus {
|
| 855 |
outline: none;
|
| 856 |
border-color: var(--accent);
|