mirror of
https://github.com/multipleof4/devsune.git
synced 2026-01-14 00:17:56 +00:00
Update index.html
This commit is contained in:
21
index.html
21
index.html
@@ -30,7 +30,8 @@
|
||||
<div class="mx-auto w-full max-w-none px-0">
|
||||
<form id="composer" class="group relative flex items-start gap-2 px-3">
|
||||
<textarea id="input" rows="1" placeholder="Send a message" spellcheck="false" autocapitalize="none" autocomplete="off" autocorrect="off" inputmode="text" enterkeyhint="enter" class="flex-1 resize-none rounded-2xl border-none bg-white px-3 py-2 text-[14px] leading-6 placeholder:text-gray-400 focus:outline-none focus:ring-0 max-h-52 overflow-y-auto min-h-[96px]"></textarea>
|
||||
-5 w-5"></i></button>
|
||||
<div class="flex flex-col gap-2 self-stretch justify-center">
|
||||
<button id="sendBtn" type="submit" aria-label="Send" class="shrink-0 rounded-2xl bg-black text-white h-10 w-10 inline-flex items-center justify-center shadow-sm hover:bg-black/90 active:scale-[.98] transition"><i data-lucide="sparkles" class="h-5 w-5"></i></button>
|
||||
<button id="attachBtn" type="button" aria-label="Attach" class="relative shrink-0 rounded-2xl bg-gray-100 text-gray-900 h-10 w-10 inline-flex items-center justify-center shadow-sm hover:bg-gray-200 active:scale-[.98] transition"><i data-lucide="paperclip" class="h-5 w-5"></i><span id="attachBadge" class="hidden absolute -top-1 -right-1 h-4 min-w-4 px-1 rounded-full bg-black text-white text-[10px] leading-4 text-center"></span></button>
|
||||
</div>
|
||||
<input id="fileInput" type="file" class="hidden" multiple accept="image/png,image/jpeg,image/webp,image/gif,application/pdf,audio/wav,audio/x-wav,audio/mpeg,audio/mp3"/>
|
||||
@@ -163,7 +164,9 @@ function addMessage(m,track=true){m.id=m.id||gid();if(!Array.isArray(m.content)&
|
||||
const addSuneBubbleStreaming=meta=>msgRow(Object.assign({role:'assistant'},meta))
|
||||
const clearChat=()=>{state.messages=[];el.messages.innerHTML='';state.attachments=[];updateAttachBadge();el.fileInput.value=''}
|
||||
const payloadWithSampling=b=>{const o=Object.assign({},b);o.temperature=store.temperature;o.top_p=store.top_p;o.top_k=store.top_k;o.frequency_penalty=store.frequency_penalty;o.presence_penalty=store.presence_penalty;o.repetition_penalty=store.repetition_penalty;o.min_p=store.min_p;o.top_a=store.top_a;const mt=Math.max(0,int(store.max_tokens||0,0));if(mt)o.max_tokens=mt;return o}
|
||||
function setBtnStop(){const b=el.sendBtn;b.dataset.mode='stop';b.type='button';b.setAttribute('aria-label','Stop');b.innerHTML='<i data-lucide="square" class="h-5 w-5"></i>';icons();b.onclick=()=>{state.abortRequested=true;stateDemoReply(){return 'Tip: open the sidebar → Account & Backup to set your API key.'}
|
||||
function setBtnStop(){const b=el.sendBtn;b.dataset.mode='stop';b.type='button';b.setAttribute('aria-label','Stop');b.innerHTML='<i data-lucide="square" class="h-5 w-5"></i>';icons();b.onclick=()=>{state.abortRequested=true;state.controller?.abort?.()}}
|
||||
function setBtnSend(){const b=el.sendBtn;b.dataset.mode='send';b.type='submit';b.setAttribute('aria-label','Send');b.innerHTML='<i data-lucide="sparkles" class="h-5 w-5"></i>';icons();b.onclick=null}
|
||||
function localDemoReply(){return 'Tip: open the sidebar → Account & Backup to set your API key.'}
|
||||
let threads=[];const titleFrom=t=>(t||'').replace(/\s+/g,' ').trim().slice(0,60)||'Untitled'
|
||||
const TKEY='threads_v1',tload=()=>localforage.getItem(TKEY).then(v=>Array.isArray(v)?v:[]),tsave=v=>localforage.setItem(TKEY,v)
|
||||
async function ensureThreadOnFirstUser(text){let needNew=!state.currentThreadId;if(state.messages.length===0)state.currentThreadId=null;if(state.currentThreadId&&!threads.some(x=>x.id===state.currentThreadId))needNew=true;if(!needNew)return;const id=gid(),now=Date.now(),th={id,title:titleFrom(text),pinned:false,updatedAt:now,messages:[]};state.currentThreadId=id;threads.unshift(th);await tsave(threads);await renderHistory()}
|
||||
@@ -175,7 +178,7 @@ function showHistoryMenu(btn,id){menuThreadId=id;const r=btn.getBoundingClientRe
|
||||
let menuSuneId=null;const hideSuneMenu=()=>{el.suneMenu.classList.add('hidden');menuSuneId=null}
|
||||
function showSuneMenu(btn,id){menuSuneId=id;const r=btn.getBoundingClientRect();el.suneMenu.style.top=(r.bottom+4)+'px';el.suneMenu.style.left=Math.min(window.innerWidth-220,r.right-200)+'px';el.suneMenu.classList.remove('hidden');icons()}
|
||||
el.historyList.addEventListener('click',async e=>{const openBtn=e.target.closest('[data-open-thread]'),menuBtn=e.target.closest('[data-thread-menu]');if(openBtn){const id=openBtn.getAttribute('data-open-thread'),th=threads.find(t=>t.id===id);if(!th)return;state.currentThreadId=id;renderSuneHTML();clearChat();state.messages=Array.isArray(th.messages)?[...th.messages]:[];for(const m of state.messages){const b=msgRow(m);b.dataset.mid=m.id||'';renderMarkdown(b,partsToText(m.content))}queueMicrotask(()=>el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'}));el.historyPanel.classList.add('translate-x-full');el.historyOverlay.classList.add('hidden');hideHistoryMenu();return}if(menuBtn){e.stopPropagation();showHistoryMenu(menuBtn,menuBtn.getAttribute('[data-thread-menu]')?menuBtn.getAttribute('[data-thread-menu]'):menuBtn.getAttribute('data-thread-menu'))}})
|
||||
el.historyMenu.addEventListener('click',async e=>{const act=e.target.closest('[data-action]')?.getAttribute('data-action');if(!act||!menuThreadId)return;const th=threads.find(t=>t.id===menuThreadId);if(!th)return;if(act==='pin'){th.pinned=!th.pinned}else if(act==='rename'){const nv=prompt('Rename to:',th.title);0;for(const m of msgs){if(!m||!m.role||m.role==='system')continue;totalChars+=String(partsToText(m.content||'')||'').length}const tokens=Math.max(0,Math.ceil(totalChars/4));const k=tokens>=1000?Math.round(tokens/1000)+'k':String(tokens);alert(tokens+' tokens ('+k+')')}hideHistoryMenu();await tsave(threads);renderHistory()})
|
||||
el.historyMenu.addEventListener('click',async e=>{const act=e.target.closest('[data-action]')?.getAttribute('data-action');if(!act||!menuThreadId)return;const th=threads.find(t=>t.id===menuThreadId);if(!th)return;if(act==='pin'){th.pinned=!th.pinned}else if(act==='rename'){const nv=prompt('Rename to:',th.title);if(nv!=null){th.title=titleFrom(nv);th.updatedAt=Date.now()}}else if(act==='delete'){if(confirm('Delete this chat?')){threads=threads.filter(x=>x.id!==th.id);if(state.currentThreadId===th.id){state.currentThreadId=null;clearChat()}}}else if(act==='count_tokens'){const msgs=Array.isArray(th.messages)?th.messages:[];let totalChars=0;for(const m of msgs){if(!m||!m.role||m.role==='system')continue;totalChars+=String(partsToText(m.content||'')||'').length}const tokens=Math.max(0,Math.ceil(totalChars/4));const k=tokens>=1000?Math.round(tokens/1000)+'k':String(tokens);alert(tokens+' tokens ('+k+')')}hideHistoryMenu();await tsave(threads);renderHistory()})
|
||||
el.suneList.addEventListener('click',e=>{const menuBtn=e.target.closest('[data-sune-menu]');if(menuBtn){e.stopPropagation();showSuneMenu(menuBtn,menuBtn.getAttribute('[data-sune-menu]')?menuBtn.getAttribute('[data-sune-menu]'):menuBtn.getAttribute('data-sune-menu'));return}const btn=e.target.closest('[data-sune-id]');if(!btn)return;const id=btn.getAttribute('data-sune-id');if(id){su.setActiveId(id);renderSidebar();reflectActiveSune();state.currentThreadId=null;clearChat();document.getElementById('sidebar').classList.add('-translate-x-full');document.getElementById('sidebarOverlay').classList.add('hidden')}})
|
||||
el.suneMenu.addEventListener('click',e=>{const act=e.target.closest('[data-action]')?.getAttribute('data-action');if(!act||!menuSuneId)return;const s=sunes.find(x=>x.id===menuSuneId);if(!s)return;if(act==='pin')s.pinned=!s.pinned;else if(act==='rename'){const nv=prompt('Rename sune to:',s.name);if(nv!=null)s.name=nv.trim()}else if(act==='pfp'){const url=prompt('Image URL:',s.avatar||'');if(url!==null)s.avatar=url.trim()}s.updatedAt=Date.now();su.save(sunes);hideSuneMenu();renderSidebar();reflectActiveSune()})
|
||||
function updateAttachBadge(){const n=state.attachments.length;el.attachBadge.textContent=String(n);el.attachBadge.classList.toggle('hidden',n===0)}
|
||||
@@ -185,7 +188,7 @@ function addAttachmentTree(role,arr){if(!arr?.length)return;const id=gid(),text=
|
||||
el.attachBtn.addEventListener('click',()=>{if(state.busy)return;if(state.attachments.length){state.attachments=[];updateAttachBadge();el.fileInput.value=''};el.fileInput.click()})
|
||||
el.fileInput.addEventListener('change',async()=>{const files=[...(el.fileInput.files||[])];if(!files.length)return;for(const f of files){const at=await toAttach(f).catch(()=>null);if(at)state.attachments.push(at)}updateAttachBadge()})
|
||||
el.messages.addEventListener('click',async e=>{const a=e.target.closest('a[href^="#dl-"]');if(!a)return; e.preventDefault();const m=a.getAttribute('href').match(/^#dl-([^-]+)-(\d+)$/);if(!m)return;const id=m[1],i=+m[2];const msg=state.messages.find(x=>x.id===id),meta=msg?.attachmentsMeta?.[i];if(!meta)return;let blob;if(meta.mode==='dataURL'){blob=await (await fetch(meta.data)).blob()}else{const bin=Uint8Array.from(atob(meta.data),c=>c.charCodeAt(0));blob=new Blob([bin],{type:meta.mime||'application/octet-stream'})}const url=URL.createObjectURL(blob),dl=document.createElement('a');dl.href=url;dl.download=meta.name||'download';document.body.appendChild(dl);dl.click();dl.remove();URL.revokeObjectURL(url)})
|
||||
el.composer.addEventListener('submit',async e=>{e.preventDefault();if(state.busy)return;const text=el.input.value.trim();if(!text&&!state.attachments.length)return;if(state.messages.length===0)state.currentThreadId=null;await ensureThreadOnFirstUser(text||'(attachments)');el.input.value='';const parts=[];if(text)parts.push({type:'text',text});state.attachments.forEach(a=>parts.push(a.part));addMessage({role:'user',content:parts.length?parts:[{type:'text',text:text||'(sent attachments)'}]});if(state.attachments.length)addAttachmentTree('user',state.attachments);state.busy=true;setBtnStop();const a=getActiveSune();const suneMeta={sune_name:a.name,model:store.model,avatar:a.avatar||''};const suneBubble=addSuneBubbleStreaming(suneMeta);const streamId=sid();suneBubble.dataset.mid=streamId;suneBubble.dataset.meta=JSON.stringify(suneMeta);state.stream={rid:streamId,bubble:suneBubble,meta:suneMeta,text:'',done:false};let buf='',completed=false;const onDelta=(delta,done)=>{buf+=delta;state.stream.text=buf;renderMarkdown(suneBubble,buf,{enhance:false});if(done&&!completed){completed=true;setBtnSend();state.busy=false;enhanceCodeBlocks(suneBubble,true);state.messages.push(Object.assign({id:streamId,role:'assistant',content:[{type:'text',text:buf}]},suneMeta));persistThread();state.stream={rid:null,bubble:null,meta:null,text:'',done:false};queueMicrotask(()=>el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'}))}};await askOpenRouterStreaming(onDelta,streamId);state.attachments=[];updateAttachBadge()})
|
||||
el.composer.addEventListener('submit',async e=>{e.preventDefault();if(state.busy)return;const text=el.input.value.trim();if(!text&&!state.attachments.length)return;if(state.messages.length===0)state.currentThreadId=null;await ensureThreadOnFirstUser(text||'(attachments)');el.input.value='';const parts=[];if(text)parts.push({type:'text',text});state.attachments.forEach(a=>parts.push(a.part));addMessage({role:'user',content:parts.length?parts:[{type:'text',text:text||'(sent attachments)'}]});if(state.attachments.length)addAttachmentTree('user',state.attachments);state.busy=true;setBtnStop();const a=getActiveSune();const suneMeta={sune_name:a.name,model:store.model,avatar:a.avatar||''};const suneBubble=addSuneBubbleStreaming(suneMeta);const streamId=sid();suneBubble.dataset.mid=streamId;state.stream={rid:streamId,bubble:suneBubble,meta:suneMeta,text:'',done:false};let buf='',completed=false;const onDelta=(delta,done)=>{buf+=delta;state.stream.text=buf;renderMarkdown(suneBubble,buf,{enhance:false});if(done&&!completed){completed=true;setBtnSend();state.busy=false;enhanceCodeBlocks(suneBubble,true);state.messages.push(Object.assign({id:streamId,role:'assistant',content:[{type:'text',text:buf}]},suneMeta));persistThread();state.stream={rid:null,bubble:null,meta:null,text:'',done:false};queueMicrotask(()=>el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'}))}};await askOpenRouterStreaming(onDelta,streamId);state.attachments=[];updateAttachBadge()})
|
||||
let jars={js:null,html:null};const ensureJars=async()=>{if(jars.js&&jars.html)return jars;const mod=await import('https://medv.io/codejar/codejar.js');const CodeJar=mod.CodeJar||mod.default;const mk=(elx,lang)=>CodeJar(elx,ed=>{ed.innerHTML=hljs.highlight(ed.textContent,{language:lang}).value},{tab:' '});if(!jars.js)jars.js=mk(el.scriptEditor,'javascript');if(!jars.html)jars.html=mk(el.htmlEditor,'xml');return jars}
|
||||
let openedJS=false,openedHTML=false
|
||||
function openSettings(){const a=getActiveSune(),s=a.settings;openedJS=false;openedHTML=false;el.set_model.value=s.model;el.set_temperature.value=s.temperature;el.set_top_p.value=s.top_p;el.set_top_k.value=s.top_k;el.set_frequency_penalty.value=s.frequency_penalty;el.set_presence_penalty.value=s.presence_penalty;el.set_repetition_penalty.value=s.repetition_penalty;el.set_min_p.value=s.min_p;el.set_top_a.value=s.top_a;el.set_max_tokens.value=s.max_tokens||'';el.set_verbosity.value=s.verbosity||'';el.set_reasoning_effort.value=s.reasoning_effort||'default';el.set_system_prompt.value=s.system_prompt;showTab('Model');el.settingsModal.classList.remove('hidden')}
|
||||
@@ -223,7 +226,7 @@ window.addEventListener('resize',()=>{hideHistoryMenu();hideSuneMenu()})
|
||||
init()
|
||||
const WS_BASE='wss://orp.awww.workers.dev/ws'
|
||||
const buildBody=()=>{const msgs=[];if(store.masterPrompt)msgs.push({role:'system',content:[{type:'text',text:store.masterPrompt}]});if(store.system_prompt)msgs.push({role:'system',content:[{type:'text',text:store.system_prompt}]});msgs.push(...state.messages.filter(m=>m.role!=='system').map(m=>({role:m.role,content:m.content})));const b=payloadWithSampling({model:store.model,messages:msgs,stream:true});if(store.reasoning_effort&&store.reasoning_effort!=='default')b.reasoning={effort:store.reasoning_effort};if(store.verbosity)b.verbosity=store.verbosity;return b}
|
||||
async function askOpenRouterStreaming(onDelta,streamId){if(!store.apiKey){const t=localDemoReply();onDelta(t,true);return {ok:true,rid:streamId||null}}const r={rid:streamId||gid(),seq:-1,done:false,manual:false,signaled:false,backoff:300,ws:null};const signal=t=>{if(!r.signaled){r.signaled=true;onDelta(t||'',true)}};const open=mode=>{if(r.done)return;r.ws=new WebSocket(WS_BASE+'?uid='+encodeURIComponent(r.rid));r.ws.onopen=()=>r.ws.send(JSON.stringify(mode==='begin'?{type:'begin',rid:r.rid,provider:store.provider,apiKey:store.apiKey,or_body:buildBody()}:{type:'resume',rid:r.rid,after:r.seq}));r.ws.onmessage=e=>{let m;try{m=JSON.parse(e.data)}catch{return}if(m.type==='delta'&&typeof m.seq==='number'&&m.seq>r.seq){r.seq=m.seq;onDelta(m.text||'',false)}else if(m.type==='done'){r.done=true;signal('');r.ws.close()}else if(m.type==='err'){r.done=true;signal('\n\n'+(m.message||'error'));r.ws.close()}};r.ws.onclose=()=>{if(!r.done&&!r.manual=setTimeout(()=>open('resume'),r.backoff=Math.min(r.backoff*1.5,5000)))};r.ws.onerror=()=>{}};state.controller={abort:()=>{r.manual=true;r.done=true;try{r.ws?.send(JSON.stringify({type:'stop',rid:r.rid}))}catch{}try{r.ws?.close()}catch{}signal('')}};open('begin');return {ok:true,rid:r.rid}}
|
||||
async function askOpenRouterStreaming(onDelta,streamId){if(!store.apiKey){const t=localDemoReply();onDelta(t,true);return {ok:true,rid:streamId||null}}const r={rid:streamId||gid(),seq:-1,done:false,manual:false,signaled:false,backoff:300,ws:null};const signal=t=>{if(!r.signaled){r.signaled=true;onDelta(t||'',true)}};const open=mode=>{if(r.done)return;r.ws=new WebSocket(WS_BASE+'?uid='+encodeURIComponent(r.rid));r.ws.onopen=()=>r.ws.send(JSON.stringify(mode==='begin'?{type:'begin',rid:r.rid,provider:store.provider,apiKey:store.apiKey,or_body:buildBody()}:{type:'resume',rid:r.rid,after:r.seq}));r.ws.onmessage=e=>{let m;try{m=JSON.parse(e.data)}catch{return}if(m.type==='delta'&&typeof m.seq==='number'&&m.seq>r.seq){r.seq=m.seq;onDelta(m.text||'',false)}else if(m.type==='done'){r.done=true;signal('');r.ws.close()}else if(m.type==='err'){r.done=true;signal('\n\n'+(m.message||'error'));r.ws.close()}};r.ws.onclose=()=>{if(!r.done&&!r.manual)setTimeout(()=>open('resume'),r.backoff=Math.min(r.backoff*1.5,5000))};r.ws.onerror=()=>{}};state.controller={abort:()=>{r.manual=true;r.done=true;try{r.ws?.send(JSON.stringify({type:'stop',rid:r.rid}))}catch{}try{r.ws?.close()}catch{}signal('')}};open('begin');return {ok:true,rid:r.rid}}
|
||||
function openAccountSettings(){el.set_provider.value=store.provider||'openrouter';el.set_api_key_or.value=store.apiKeyOR||'';el.set_api_key_oai.value=store.apiKeyOAI||'';el.set_master_prompt.value=store.masterPrompt||'';el.accountSettingsModal.classList.remove('hidden')}
|
||||
function closeAccountSettings(){el.accountSettingsModal.classList.add('hidden')}
|
||||
el.accountSettingsOption.addEventListener('click',()=>{el.userMenu.classList.add('hidden');openAccountSettings()})
|
||||
@@ -232,10 +235,12 @@ el.cancelAccountSettings.addEventListener('click',closeAccountSettings)
|
||||
el.accountSettingsModal.addEventListener('click',e=>{if(e.target===el.accountSettingsModal||e.target.classList.contains('bg-black/30'))closeAccountSettings()})
|
||||
el.accountSettingsForm.addEventListener('submit',e=>{e.preventDefault();store.provider=el.set_provider.value||'openrouter';store.apiKeyOR=String(el.set_api_key_or.value||'').trim();store.apiKeyOAI=String(el.set_api_key_oai.value||'').trim();store.masterPrompt=String(el.set_master_prompt.value||'').trim();closeAccountSettings()})
|
||||
async function fetchTranscript(id){try{const r=await fetch(WS_BASE+'?uid='+encodeURIComponent(id));if(!r.ok)return null;return await r.json()}catch{return null}}
|
||||
let syncLoopRunning=false,lastSyncRid=null
|
||||
async function syncLoop(ridOpt){if(syncLoopRunning)return;const rid=ridOpt||state.stream?.rid||lastSyncRid;if(!rid)return;syncLoopRunning=true;lastSyncRid=rid;try{while(true){const j=await fetchTranscript(rid);if(!j)break;const bubble=document.querySelector(`[data-mid="${rid}"]`);if(!bubble)break;const prev=bubble.__lastText||'';const text=j.text||'';if(text&&text!==prev){bubble.__lastText=text;renderMarkdown(bubble,text,{enhance:false})}if(j.done||j.phase==='done'||j.error){let final=text;if(j.error)final=(final?final+'\n\n':'')+String(j.error||'');setBtnSend();state.busy=false;renderMarkdown(bubble,final||text,{enhance:false});enhanceCodeBlocks(bubble,true);const meta=JSON.parse(bubble.dataset.meta||'{}');if(!state.messages.some(m=>m.id===rid))state.messages.push(Object.assign({id:rid,role:'assistant',content:[{type:'text',text:final}]},meta));await persistThread();state.stream={rid:null,bubble:null,meta:null,text:'',done:false};break}await new Promise(r=>setTimeout(r,900))}}finally{syncLoopRunning=false}}
|
||||
async function syncTranscript(){const s=state.stream;if(!s||!s.rid)return false;const j=await fetchTranscript(s.rid);if(!j||!j.rid||j.rid!==s.rid){if(j&&j.error&&!s.done){s.done=true;setBtnSend();state.busy=false;s.text=(s.text||'')+'\n\n'+String(j.error||'');renderMarkdown(s.bubble,s.text,{enhance:false});enhanceCodeBlocks(s.bubble,true);state.messages.push(Object.assign({id:s.rid,role:'assistant',content:[{type:'text',text:s.text}]},s.meta));persistThread();state.stream={rid:null,bubble:null,meta:null,text:'',done:false};queueMicrotask(()=>el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'}))}return false}const t=j.text||'';if(t&&t!==s.text){s.text=t;renderMarkdown(s.bubble,t,{enhance:false})}if(j.error&&!s.done){s.done=true;setBtnSend();state.busy=false;s.text=(s.text||'')+'\n\n'+String(j.error||'');renderMarkdown(s.bubble,s.text,{enhance:false});enhanceCodeBlocks(s.bubble,true);state.messages.push(Object.assign({id:s.rid,role:'assistant',content:[{type:'text',text:s.text}]},s.meta));persistThread();state.stream={rid:null,bubble:null,meta:null,text:'',done:false};queueMicrotask(()=>el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'}));return false}if((j.done||j.phase==='done')&&!s.done){s.done=true;setBtnSend();state.busy=false;enhanceCodeBlocks(s.bubble,true);state.messages.push(Object.assign({id:s.rid,role:'assistant',content:[{type:'text',text:s.text||''}]},s.meta));persistThread();state.stream={rid:null,bubble:null,meta:null,text:s.text||'',done:false};queueMicrotask(()=>el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'}));return false}return true}
|
||||
let syncLoopRunning=false
|
||||
async function syncLoop(){if(syncLoopRunning)return;const s=state.stream;if(!s||!s.rid)return;syncLoopRunning=true;try{while(await syncTranscript())await new Promise(r=>setTimeout(r,900))}finally{syncLoopRunning=false}}
|
||||
window.addEventListener('focus',()=>syncLoop())
|
||||
document.addEventListener('visibilitychange',()=>{if(document.hidden&&state.stream?.rid&&!state.stream.done)syncLoop(state.stream.rid);else if(!document.hidden)syncLoop(state.stream?.rid||lastSyncRid)})
|
||||
window.addEventListener('blur',()=>syncLoop())
|
||||
document.addEventListener('visibilitychange',()=>syncLoop())
|
||||
el.copySystemPrompt.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(el.set_system_prompt.value||'')}catch{}})
|
||||
el.pasteSystemPrompt.addEventListener('click',async()=>{try{el.set_system_prompt.value=await navigator.clipboard.readText()}catch{}})
|
||||
el.copyHTML.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(el.htmlEditor.textContent||'')}catch{}})
|
||||
|
||||
Reference in New Issue
Block a user