b014638f39
- POST /api/save-to-corpus.php — saves tool output text to user's default CaveauAI corpus via ClientRagPipeline
- api/case/upload.php — dual-writes uploaded PDFs to CaveauAI client_documents (best-effort)
- assets/js/corpus-save.js — shared <dialog> handler for .js-save-corpus buttons on all tool pages
- includes/layout_footer.php — injects corpus-save.js + shared save dialog markup
- korrespond/deep-research/barnevernet/discrepancy JS — save-to-corpus buttons on output sections
- api/search.php + LegalTools::search() — corpus_scope param ('shared'|'private'|'both'), merges personal CaveauAI corpus with shared legal library when 'both'
- includes/tool_form.php + assets/js/tools.js — corpus scope radio toggle shown on search tab
- api/user-docs.php — add POST upload method for non-SSO authenticated users
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
107 lines
3.1 KiB
JavaScript
107 lines
3.1 KiB
JavaScript
/**
|
|
* corpus-save.js — "Save to corpus" shared handler for all DBN tool pages.
|
|
*
|
|
* Buttons that trigger a save must have:
|
|
* class="js-save-corpus"
|
|
* data-content-id="<id of element containing the text to save>"
|
|
* data-tool="<source_tool slug, e.g. korrespond>"
|
|
* data-suggested-title="<pre-filled title string>" (optional)
|
|
*/
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
const dlg = document.getElementById('save-corpus-dialog');
|
|
const form = document.getElementById('save-corpus-form');
|
|
const titleIn = document.getElementById('save-corpus-title');
|
|
const tagsIn = document.getElementById('save-corpus-tags');
|
|
const cancelBtn = document.getElementById('save-corpus-cancel');
|
|
|
|
if (!dlg || !form) return; // dialog not present (e.g. not logged in)
|
|
|
|
cancelBtn?.addEventListener('click', () => dlg.close());
|
|
|
|
let _pendingBtn = null;
|
|
let _pendingContent = '';
|
|
let _pendingTool = '';
|
|
|
|
// Delegated click — catches buttons added dynamically by tool JS
|
|
document.addEventListener('click', (e) => {
|
|
const btn = e.target.closest('.js-save-corpus');
|
|
if (!btn) return;
|
|
|
|
const contentId = btn.dataset.contentId;
|
|
const el = contentId ? document.getElementById(contentId) : null;
|
|
const content = (el ? (el.value ?? el.textContent) : '').trim();
|
|
|
|
if (!content || content.length < 30) {
|
|
btn.textContent = 'Nothing to save';
|
|
setTimeout(() => { btn.textContent = 'Save to corpus'; }, 2000);
|
|
return;
|
|
}
|
|
|
|
_pendingBtn = btn;
|
|
_pendingContent = content;
|
|
_pendingTool = btn.dataset.tool ?? '';
|
|
|
|
titleIn.value = btn.dataset.suggestedTitle ?? '';
|
|
tagsIn.value = '';
|
|
dlg.showModal();
|
|
titleIn.focus();
|
|
titleIn.select();
|
|
});
|
|
|
|
// Form submit inside dialog
|
|
form.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
dlg.close();
|
|
|
|
const btn = _pendingBtn;
|
|
const content = _pendingContent;
|
|
const title = titleIn.value.trim();
|
|
const tags = tagsIn.value.trim();
|
|
const tool = _pendingTool;
|
|
|
|
if (!title || !content) return;
|
|
|
|
if (btn) {
|
|
btn.disabled = true;
|
|
btn.textContent = 'Saving…';
|
|
}
|
|
|
|
try {
|
|
const resp = await fetch('api/save-to-corpus.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ title, content, source_tool: tool, tags }),
|
|
});
|
|
|
|
const data = await resp.json().catch(() => ({}));
|
|
|
|
if (resp.ok && data.ok) {
|
|
if (btn) {
|
|
btn.textContent = '✓ Saved to corpus';
|
|
btn.classList.add('js-save-corpus--saved');
|
|
}
|
|
} else {
|
|
const msg = data.error ?? `Error ${resp.status}`;
|
|
if (btn) {
|
|
btn.textContent = 'Save failed';
|
|
btn.disabled = false;
|
|
btn.title = msg;
|
|
}
|
|
console.error('[corpus-save] Save failed:', msg);
|
|
}
|
|
} catch (err) {
|
|
if (btn) {
|
|
btn.textContent = 'Network error';
|
|
btn.disabled = false;
|
|
}
|
|
console.error('[corpus-save] Network error:', err);
|
|
}
|
|
|
|
_pendingBtn = null;
|
|
_pendingContent = '';
|
|
});
|
|
}());
|