sourojitdotcom's picture
Show real model download progress, blurred editor loading states and retry
768032a verified
Raw History Blame Contribute Delete
5.84 kB
const $=s=>document.querySelector(s),input=$('#input'),lang=$('#language'),sentence=$('#sentence');
const names={hi:'Hindi',bn:'Bengali',te:'Telugu',kn:'Kannada',ta:'Tamil'},examples={hi:['main theek hoon','ghar main kaun hai','mujhe bhookh lagi hai'],bn:['tumi kemon acho','ami bhalo achi','nomoskar'],te:['meeru ela unnaru','nenu bagunnanu','namaskaram'],kn:['neevu hegiddira','nanu chennagiddene','namaskara'],ta:['neenga eppadi irukeenga','nandri','vanakkam']};
const shell=$('#editorShell'),content=$('#editorContent'),overlay=$('#loadingOverlay'),bar=$('#loadingProgress');
let seq=0,timer,overrides={},exampleN=0,metrics,busy=true;
const worker=new Worker('./js/worker.js',{type:'module'});
function status(text,state=''){ $('#status').textContent=text;$('#led').className=state; }
function setBusy(value){busy=value;shell.classList.toggle('is-loading',value);overlay.hidden=!value;content.setAttribute('aria-busy',String(value));content.inert=value;input.disabled=value;for(const b of content.querySelectorAll('button'))b.disabled=value;}
function showLoading(label='Preparing…'){
setBusy(true);overlay.classList.remove('is-error');$('#loadingSpinner').hidden=false;$('#retryLoad').hidden=true;bar.hidden=false;bar.removeAttribute('value');$('#loadingTitle').textContent=label;$('#loadingDetail').textContent='Preparing your on-device workspace.';$('#loadingHint').textContent='Your text stays here. You can still switch languages.';status('Downloading…');
}
function showError(message){setBusy(true);overlay.classList.add('is-error');$('#loadingSpinner').hidden=true;bar.hidden=true;$('#loadingTitle').textContent=`Could not load ${names[lang.value]}`;$('#loadingDetail').textContent=message;$('#loadingHint').textContent='Your text is safe. Check your connection and try again.';$('#retryLoad').hidden=false;status('Download failed','error');window.multiIndicState={ready:false,language:lang.value,error:message};}
function request({loading=false,retry=false}={}){
clearTimeout(timer);const id=++seq;
if(loading)showLoading(`Preparing ${names[lang.value]}…`);else status('Working on your device…');
window.multiIndicState={ready:false,language:lang.value,sentence:sentence.checked};$('#count').textContent=`${input.value.length} / 400`;
worker.postMessage({id,language:lang.value,text:input.value,sentence:sentence.checked,overrides,retry,loading});
}
function metricsLine(){const m=metrics?.languages?.[lang.value];if(!m)return;$('#measurement').textContent=`${m.ours.top1.toFixed(2)}% test top-1 · ${m.download_mb.toFixed(2)} MB word pack (gzip-9)`;}
function changed(){overrides={};$('#hindiMode').hidden=lang.value!=='hi';sentence.checked=false;$('#languageName').textContent=names[lang.value].toUpperCase();$('#modeNote').textContent=lang.value==='hi'?'Enable the optional LM to choose Hindi words jointly in sentence context.':'Words are transliterated independently. Select a suggestion to replace the last word.';metricsLine();request({loading:true});}
function progress(p){
if(!busy)setBusy(true);
if($('#loadingTitle').textContent!==p.label)$('#loadingTitle').textContent=p.label;
const mb=n=>(n/1e6).toFixed(2);
$('#loadingDetail').textContent=p.total>0?`${mb(Math.min(p.loaded,p.total))} / ${mb(p.total)} MB${p.source==='cache'?' · saved on this device':''}`:p.loaded>0?`${mb(p.loaded)} MB received · total size unavailable`:'Preparing your on-device workspace.';
if(p.total>0){bar.value=Math.min(1,p.loaded/p.total);bar.setAttribute('aria-valuetext',`${Math.round(bar.value*100)} percent`);}else{bar.removeAttribute('value');bar.removeAttribute('aria-valuetext');}
status(p.phase==='cache'?'Loading from cache…':p.phase==='preparing'?'Preparing…':p.phase==='dictionary'?'Loading dictionary…':'Downloading…',p.phase==='cache'?'cached':'');
window.multiIndicLoading={...p,id:seq,language:lang.value};
}
worker.onmessage=({data:m})=>{
if(m.id!==seq)return;
if(m.progress){progress(m.progress);return;}
if(m.error){showError(m.error);return;}
if(m.language!==lang.value||Boolean(m.sentence)!==Boolean(sentence.checked&&lang.value==='hi'))return;
const r=m.result;$('#output').textContent=r.output;setBusy(false);status(m.fromCache||!navigator.onLine?'Offline-ready':'Ready · on device','ready');$('#status').title='This language is loaded. Keep this tab open to type without a connection.';$('#timing').textContent=`${r.ms.toFixed(0)} ms · ${sentence.checked&&lang.value==='hi'?'Hindi sentence LM':'word-by-word'}`;$('#suggestions').replaceChildren();
const tok=[...r.tokens].reverse().find(t=>t.candidates?.length);$('#wordLabel').textContent=tok?`for “${tok.text}”`:'';
for(const c of tok?.candidates||[]){const b=document.createElement('button');b.textContent=c.text;b.title=`Use ${c.text} for ${tok.text}`;b.onclick=()=>{overrides[tok.start]=c.text;request();};$('#suggestions').append(b);}
window.multiIndicState={ready:true,language:lang.value,sentence:sentence.checked,result:r};
};
worker.onerror=e=>showError(`Runtime error: ${e.message}`);
input.oninput=()=>{overrides={};clearTimeout(timer);timer=setTimeout(()=>request(),220);};lang.onchange=changed;sentence.onchange=()=>{overrides={};request({loading:true});};$('#retryLoad').onclick=()=>request({loading:true,retry:true});$('#example').onclick=()=>{const a=examples[lang.value];input.value=a[(++exampleN)%a.length];overrides={};request();};$('#copy').onclick=async()=>{try{await navigator.clipboard.writeText($('#output').textContent);$('#copy').textContent='Copied ✓';setTimeout(()=>$('#copy').textContent='Copy output ⧉',1200);}catch{status('Select output text to copy');}};
window.addEventListener('offline',()=>{if(!busy&&window.multiIndicState?.ready)status('Offline-ready','ready');});
fetch('./benchmark.json').then(r=>r.json()).then(d=>{metrics=d;metricsLine();}).catch(()=>{});request({loading:true});