researchrag / app.js
Hugdrigo's picture
Upload 5 files
f597e2d verified
Raw History Blame Contribute Delete
6.97 kB
/* Browser UI for ResearchRAG Static */
(function(){
'use strict';
const $=id=>document.getElementById(id);
const state={documents:[],index:null,last:null,question:''};
const demo=ResearchRAG.DEMO;
function setStatus(msg,type='info'){const el=$('status');el.textContent=msg;el.dataset.type=type;}
function setText(id,text){$(id).textContent=String(text);}
function updateIndex(){state.index=ResearchRAG.buildIndex(state.documents);setText('metric-docs',state.index.docs.length);setText('metric-chunks',state.index.chunks.length);setText('metric-characters',state.index.docs.reduce((n,d)=>n+d.characters,0).toLocaleString());renderDocList();$('run').disabled=!state.index.N;}
function renderDocList(){const root=$('documents');root.replaceChildren();if(!state.documents.length){const p=document.createElement('p');p.className='muted empty';p.textContent='No documents loaded. Add text, upload .txt/.md/.csv or try the examples.';root.append(p);return;}
state.documents.forEach((d,i)=>{const row=document.createElement('div');row.className='doc-row';const name=document.createElement('span');name.className='doc-name';name.textContent=d.name;name.title=d.name;const len=document.createElement('span');len.className='doc-len';len.textContent=`${d.text.length.toLocaleString()} chars`;const btn=document.createElement('button');btn.type='button';btn.className='delete';btn.textContent='Remove';btn.setAttribute('aria-label',`Remove ${d.name}`);btn.onclick=()=>{state.documents.splice(i,1);updateIndex();clearResults();setStatus('Document removed. Index rebuilt.');};row.append(name,len,btn);root.append(row);});}
function clearResults(){state.last=null;$('results').hidden=true;$('results-empty').hidden=false;$('download').disabled=true;}
function addDoc(name,text){let s=String(text||'').trim();if(s.length<30)throw new Error(`${name}: add at least 30 characters of text.`);if(s.length>350000)throw new Error(`${name}: file is too large; maximum 350,000 characters per document.`);if(state.documents.length>=15)throw new Error('Maximum 15 documents at a time. Remove one before adding more.');const n=String(name||'Untitled document').slice(0,130);let newname=n,number=2;while(state.documents.some(d=>d.name===newname)){newname=`${n} (${number++})`;}state.documents.push({name:newname,text:s});}
function loadDemo(){state.documents=demo.map(d=>({...d}));updateIndex();$('question').value='Why is source verification important in academic research?';clearResults();setStatus('Three example documents loaded. Press “Ask the documents”.','ok');$('question').focus();}
function pasteDoc(){try{const name=$('doc-title').value.trim()||'Pasted notes';const text=$('doc-text').value;addDoc(name,text);$('doc-text').value='';$('doc-title').value='';updateIndex();clearResults();setStatus('Document added. You can ask a question now.','ok');}catch(e){setStatus(e.message,'error');}}
async function readFiles(files){if(!files.length)return;let count=0;const failed=[];
for(const file of [...files]){try{if(!/\.(txt|md|csv)$/i.test(file.name)){throw new Error('Only .txt, .md and .csv files are supported in this browser edition.');}if(file.size>1048576){throw new Error('File is larger than 1 MB.');}const txt=await file.text();addDoc(file.name,txt);count++;}catch(e){failed.push(`${file.name}: ${e.message}`);}}
updateIndex();clearResults();setStatus(`${count} file(s) imported.`+(failed.length?' '+failed.join(' '):''),failed.length?'error':'ok');$('files').value='';}
function textEl(tag,cl,value){const e=document.createElement(tag);if(cl)e.className=cl;e.textContent=value;return e;}
function renderResults(output){const p=$('results');p.hidden=false;$('results-empty').hidden=true;$('evidence').replaceChildren();$('answer').replaceChildren();setText('hit-count',output.hits.length);setText('result-status',output.status==='ok'?'Evidence found':'No supported answer');
if(output.status!=='ok'){$('answer').append(textEl('p','muted',output.message));$('download').disabled=true;return;}
$('answer').append(textEl('p','advisory','Extractive evidence, not a generated answer. Check the cited source passages.'));
for(const a of output.answer){const box=document.createElement('p');box.className='answer-line';box.append(document.createTextNode(a.text+' '));const cite=textEl('span','cite',`[${a.chunkId}]`);box.append(cite);$('answer').append(box);}
for(const h of output.hits){const card=document.createElement('article');card.className='evidence-card';const top=document.createElement('div');top.className='evidence-top';top.append(textEl('strong','',`[${h.id}] ${h.doc}`),textEl('span','score',`Relevance ${(h.score*100).toFixed(0)}%`));card.append(top,textEl('p','',h.text));const meta=textEl('p','footnote','Lexical overlap: '+h.matchCount+' query term(s); rank is not a truth score.');card.append(meta);$('evidence').append(card);}
$('download').disabled=false;
}
function ask(){const q=$('question').value.trim();if(!q){setStatus('Write a question before searching.','error');$('question').focus();return;}
const out=ResearchRAG.query(state.index,q,5);state.last=out;state.question=q;renderResults(out);setStatus(out.status==='ok'?`${out.hits.length} cited source passages found.`:out.message,out.status==='ok'?'ok':'error');$('results').scrollIntoView({behavior:'smooth',block:'start'});
}
function exportMd(){if(!state.last||state.last.status!=='ok')return;const q=state.question;const arr=['# ResearchRAG Evidence Report','','**Question:** '+q,'','**Important:** This is extractive, lexical evidence retrieval. It is not a generative answer and does not verify facts.','','## Evidence-based excerpts',''];for(const a of state.last.answer){arr.push('- '+a.text.replace(/\n/g,' ')+' ['+a.chunkId+']');}arr.push('','## Retrieved passages','');for(const h of state.last.hits){arr.push(`### [${h.id}] ${h.doc}`,'',h.text,'',`Lexical relevance: ${(h.score*100).toFixed(0)}%`,'');}
const blob=new Blob([arr.join('\n')],{type:'text/markdown;charset=utf-8'}),url=URL.createObjectURL(blob),link=document.createElement('a');link.href=url;link.download='researchrag-evidence-report.md';link.click();setTimeout(()=>URL.revokeObjectURL(url),2000);}
function init(){ $('demo').onclick=loadDemo;$('add-paste').onclick=pasteDoc;$('run').onclick=ask;$('download').onclick=exportMd;$('reset').onclick=()=>{state.documents=[];updateIndex();clearResults();setStatus('Documents cleared from this tab.');};$('files').onchange=e=>readFiles(e.target.files);
const drop=$('drop');drop.ondragover=e=>{e.preventDefault();drop.classList.add('over');};drop.ondragleave=()=>drop.classList.remove('over');drop.ondrop=e=>{e.preventDefault();drop.classList.remove('over');readFiles(e.dataTransfer.files);};$('question').onkeydown=e=>{if((e.ctrlKey||e.metaKey)&&e.key==='Enter')ask();};updateIndex();setStatus('Ready. Load the sample documents or add your own.');}
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();
})();