Spaces:
Running on Zero
Running on Zero
File size: 3,892 Bytes
1b2da16 589dcd3 1b2da16 9684bdc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | const host=document.getElementById('script-input');
const draft=element.querySelector('textarea'), viewport=element.querySelector('.reading-window');
const track=element.querySelector('.reading-track'), edit=element.querySelector('[data-edit]');
const hint=element.querySelector('#script-hint'), pages=element.querySelector('[data-pages]');
let take=null, reading=false, clock=0, page=0, pageCount=1, spans=[], lineHeight=35;
const listeners=new AbortController();
const on=(node,event,fn)=>node.addEventListener(event,fn,{signal:listeners.signal});
draft.value=props.value || '';
function publish() { props.value=draft.value; host.dataset.draft=draft.value; }
function showMode() {
draft.hidden=reading; viewport.hidden=!reading;
edit.hidden=!take; edit.textContent=reading?'Edit text':'View take';
pages.hidden=!reading || take?.alignment==='ready' || pageCount<2;
hint.textContent=reading?(take.alignment==='ready'?'': 'Word timing unavailable'):'Write your text · '+draft.value.length+'/600';
if(reading && take.text!==draft.value) hint.textContent='Previous take';
}
function layout() {
if(!reading || !spans.length) return;
track.style.transform='none';
lineHeight=parseFloat(getComputedStyle(viewport).lineHeight);
spans.forEach(s=>s.line=Math.max(0,Math.round(s.node.offsetTop/lineHeight)));
pageCount=Math.max(1,Math.ceil((Math.max(...spans.map(s=>s.line))+1)/2));
renderClock(); showMode();
}
function renderClock() {
if(!reading || !take) return;
if(take.alignment==='ready') {
let index=-1;
for(let i=0;i<spans.length;i++) {
const spoken=clock>=spans[i].start;
spans[i].node.classList.toggle('spoken',spoken);
if(spoken) index=i;
}
page=index<0?0:Math.floor(spans[index].line/2);
} else spans.forEach(s=>s.node.classList.add('spoken'));
page=Math.max(0,Math.min(pageCount-1,page));
track.style.transform=`translateY(${-page*2*lineHeight}px)`;
element.querySelector('[data-previous]').disabled=page===0;
element.querySelector('[data-next]').disabled=page===pageCount-1;
}
function receive(data) {
if(!data?.text) return;
take=data; clock=0; page=0; track.replaceChildren();
const words=data.alignment==='ready' && data.words?.length?data.words:
(data.text.match(/\S+\s*|\s+/gu)||[data.text]).map(text=>({text,start:0,end:0}));
spans=words.map(word=>{
const node=document.createElement('span'); node.className='word'; node.textContent=word.text;
track.append(node); return {...word,node,line:0};
});
reading=true; showMode(); requestAnimationFrame(layout);
}
on(draft,'input',()=>{publish();showMode();});
on(edit,'click',()=>{
reading=!reading;
if(!reading) document.getElementById('speech-player')?.dispatchEvent(new CustomEvent('zonos:pause'));
showMode(); if(reading) layout(); else draft.focus();
});
on(element.querySelector('[data-previous]'),'click',()=>{page--;renderClock();});
on(element.querySelector('[data-next]'),'click',()=>{page++;renderClock();});
on(host,'zonos:take',e=>receive(e.detail));
on(host,'zonos:clock',e=>{
clock=e.detail.time;
if(e.detail.playing && !reading){reading=true;showMode();layout();}
renderClock();
});
watch('value',()=>{if(props.value!==draft.value){draft.value=props.value||'';reading=false;showMode();}host.dataset.draft=draft.value;});
// Layout writes can resize Gradio's outer wrapper. Defer them outside the
// observer delivery cycle to avoid a ResizeObserver feedback warning in WebKit.
let resizeFrame=0;
const resize=new ResizeObserver(()=>{
if(resizeFrame)return;
resizeFrame=requestAnimationFrame(()=>{resizeFrame=0;if(element.isConnected)layout();});
});resize.observe(viewport);
const observer=new MutationObserver(()=>{if(!element.isConnected){listeners.abort();cancelAnimationFrame(resizeFrame);resize.disconnect();observer.disconnect();}});
observer.observe(document.documentElement,{childList:true,subtree:true});
publish();showMode();
host.dataset.ready='true';
|