Update index.html via Sune

This commit is contained in:
2025-09-03 01:41:18 -07:00
parent 409f30581c
commit 73428dce8b

View File

@@ -162,9 +162,10 @@ const SUNE=window.SUNE=new Proxy({get list(){return sunes},get id(){return su.ge
if(!sunes.length){const def=SUNE.create({name:'Default'});SUNE.setActive(def.id)}
const state={messages:[],busy:false,controller:null,currentThreadId:null,abortRequested:false,attachments:[],stream:{rid:null,bubble:null,meta:null,text:'',done:false}}
const getModelShort=m=>{const mm=m||SUNE.model||'';return mm.includes('/')?mm.split('/').pop():mm}
const processSuneIncludes=async(h,d=0)=>{if(d>5)return`<!--Sune depth limit-->`;let p=h||'';for(const[t,u]of[...(h||'').matchAll(/<sune\s+src=['"]([^'"]+)['"]\s*\/>/gi)]){const i=await fetch(u).then(r=>r.ok?r.json():Promise.reject()).then(d=>(Array.isArray(d)?d[0]:d)||{}).then(s=>[s.html||'',s.extension_html||''].join('\n')).catch(()=>`<!--Failed ${esc(u)}-->`);const n=await processSuneIncludes(i,d+1);p=p.replace(t,n)}return p}
const renderSuneHTML=async()=>{const i=[SUNE.html,SUNE.extension_html].map(x=>(x||'').trim()).join('\n');const c=await processSuneIncludes(i);const m=el.suneHtml;m.innerHTML='';m.classList.toggle('hidden',!c);if(!c)return;m.insertAdjacentHTML('afterbegin',c);m.querySelectorAll('script').forEach(s=>{const n=document.createElement('script');[...s.attributes].forEach(a=>n.setAttribute(a.name,a.value));n.text=s.text;s.replaceWith(n)})}
const reflectActiveSune=async()=>{const a=SUNE.active;el.suneBtnTop.title=`Settings — ${a.name}`;el.suneBtnTop.innerHTML=a.avatar?`<img src="${esc(a.avatar)}" alt="" class="h-8 w-8 rounded-full object-cover"/>`:'✺';icons();await renderSuneHTML()}
const resolveSuneSrc=src=>{if(!src)return null;if(src.startsWith('gh://')){const path=src.substring(5),parts=path.split('/');if(parts.length<3)return null;const[owner,repo,...filePathParts]=parts;return`https://raw.githubusercontent.com/${owner}/${repo}/main/${filePathParts.join('/')}`}return src}
const processSuneIncludes=async(html,depth=0)=>{if(depth>5)return'<!-- Sune include depth limit reached -->';if(!html)return'';const includeRegex=/<sune\s+src=['"]([^'"]+)['"]\s*\/>/gi;const matches=[...html.matchAll(includeRegex)];if(matches.length===0)return html;const replacements=await Promise.all(matches.map(async match=>{const[tag,rawSrc]=match;const fetchUrl=resolveSuneSrc(rawSrc);if(!fetchUrl)return{tag,content:`<!-- Sune Include Error: Invalid src "${esc(rawSrc)}" -->`};try{const response=await fetch(fetchUrl);if(!response.ok)throw new Error(`HTTP ${response.status}`);const suneData=await response.json();const suneObj=Array.isArray(suneData)?suneData[0]:suneData;const combinedHtml=[suneObj?.settings?.html||'',suneObj?.settings?.extension_html||''].join('\n');const processedContent=await processSuneIncludes(combinedHtml,depth+1);return{tag,content:processedContent}}catch(error){return{tag,content:`<!-- Sune Include Error: Failed to fetch/parse ${esc(fetchUrl)}. ${esc(error.message)} -->`}}}));let resultHtml=html;for(const{tag,content}of replacements){resultHtml=resultHtml.replace(tag,content)}return resultHtml}
const renderSuneHTML=async()=>{const baseHtml=[SUNE.html,SUNE.extension_html].map(x=>(x||'').trim()).join('\n');const finalHtml=await processSuneIncludes(baseHtml);const container=el.suneHtml;container.innerHTML='';container.classList.toggle('hidden',!finalHtml.trim());if(!finalHtml.trim())return;container.insertAdjacentHTML('afterbegin',finalHtml);container.querySelectorAll('script').forEach(s=>{if(s.type&&s.type!=='text/javascript')return;const newScript=document.createElement('script');[...s.attributes].forEach(a=>newScript.setAttribute(a.name,a.value));newScript.text=s.text;s.replaceWith(newScript)})}
const reflectActiveSune=async()=>{const a=SUNE.active;el.suneBtnTop.title=`Settings — ${a.name}`;el.suneBtnTop.innerHTML=a.avatar?`<img src="${esc(a.avatar)}" alt="" class="h-8 w-8 rounded-full object-cover"/>`:'✺';await renderSuneHTML();icons()}
const suneRow=a=>`<div class="relative flex items-center gap-2 px-3 py-2 ${a.pinned?'bg-yellow-50':''}"><button data-sune-id="${a.id}" class="flex-1 text-left flex items-center gap-2 ${a.id===SUNE.id?'font-medium':''}">${a.avatar?`<img src="${esc(a.avatar)}" alt="" class="h-6 w-6 rounded-full object-cover"/>`:`<span class="h-6 w-6 rounded-full bg-gray-200 flex items-center justify-center">✺</span>`}<span class="truncate">${a.pinned?'📌 ':''}${esc(a.name)}</span></button><button data-sune-menu="${a.id}" class="h-8 w-8 rounded hover:bg-gray-100 flex items-center justify-center" title="More"><i data-lucide="more-horizontal" class="h-4 w-4"></i></button></div>`
const renderSidebar=()=>{const list=[...SUNE.list].sort((a,b)=>(b.pinned-a.pinned));el.suneList.innerHTML=list.map(suneRow).join('');icons()}
function enhanceCodeBlocks(root,doHL=true){root.querySelectorAll('pre>code').forEach(code=>{if(code.textContent.length>200000)return;const pre=code.parentElement;pre.classList.add('relative','rounded-xl','border','border-gray-200');if(!pre.querySelector('.copy-btn')){const btn=document.createElement('button');btn.className='copy-btn';btn.textContent='Copy';btn.addEventListener('click',async e=>{e.stopPropagation();try{await navigator.clipboard.writeText(code.innerText);btn.textContent='Copied';setTimeout(()=>btn.textContent='Copy',1200)}catch{}});pre.appendChild(btn)}if(doHL&&window.hljs&&code.textContent.length<100000)hljs.highlightElement(code)})}
@@ -191,7 +192,7 @@ let menuThreadId=null;const hideThreadPopover=()=>{el.threadPopover.classList.ad
function showThreadPopover(btn,id){menuThreadId=id;const r=btn.getBoundingClientRect();el.threadPopover.style.top=(r.bottom+4)+'px';el.threadPopover.style.left=Math.min(window.innerWidth-220,r.right-200)+'px';el.threadPopover.classList.remove('hidden');icons()}
let menuSuneId=null;const hideSunePopover=()=>{el.sunePopover.classList.add('hidden');menuSuneId=null}
function showSunePopover(btn,id){menuSuneId=id;const r=btn.getBoundingClientRect();el.sunePopover.style.top=(r.bottom+4)+'px';el.sunePopover.style.left=Math.min(window.innerWidth-220,r.right-200)+'px';el.sunePopover.classList.remove('hidden');icons()}
el.threadList.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');if(id!==state.currentThreadId&&state.busy){state.controller?.disconnect?.();setBtnSend();state.busy=false;state.controller=null}const th=threads.find(t=>t.id===id);if(!th)return;if(id===state.currentThreadId){el.sidebarRight.classList.add('translate-x-full');el.sidebarOverlayRight.classList.add('hidden');hideThreadPopover();return}state.currentThreadId=id;await 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))}syncWhileBusy();queueMicrotask(()=>el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'}));el.sidebarRight.classList.add('translate-x-full');el.sidebarOverlayRight.classList.add('hidden');hideThreadPopover();return}if(menuBtn){e.stopPropagation();showThreadPopover(menuBtn,menuBtn.getAttribute('[data-thread-menu]')?menuBtn.getAttribute('[data-thread-menu]'):menuBtn.getAttribute('data-thread-menu'))}})
el.threadList.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');if(id!==state.currentThreadId&&state.busy){state.controller?.disconnect?.();setBtnSend();state.busy=false;state.controller=null}const th=threads.find(t=>t.id===id);if(!th)return;if(id===state.currentThreadId){el.sidebarRight.classList.add('translate-x-full');el.sidebarOverlayRight.classList.add('hidden');hideThreadPopover();return}state.currentThreadId=id;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))}await renderSuneHTML();syncWhileBusy();queueMicrotask(()=>el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'}));el.sidebarRight.classList.add('translate-x-full');el.sidebarOverlayRight.classList.add('hidden');hideThreadPopover();return}if(menuBtn){e.stopPropagation();showThreadPopover(menuBtn,menuBtn.getAttribute('[data-thread-menu]')?menuBtn.getAttribute('[data-thread-menu]'):menuBtn.getAttribute('data-thread-menu'))}})
el.threadPopover.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+')')}hideThreadPopover();await tsave(threads);renderThreads()})
el.suneList.addEventListener('click',async e=>{const menuBtn=e.target.closest('[data-sune-menu]');if(menuBtn){e.stopPropagation();showSunePopover(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){if(state.busy){state.controller?.disconnect?.();setBtnSend();state.busy=false;state.controller=null};SUNE.setActive(id);renderSidebar();await reflectActiveSune();state.currentThreadId=null;clearChat();document.getElementById('sidebarLeft').classList.add('-translate-x-full');document.getElementById('sidebarOverlayLeft').classList.add('hidden')}})
el.sunePopover.addEventListener('click',async e=>{const act=e.target.closest('[data-action]')?.getAttribute('data-action');if(!act||!menuSuneId)return;const s=SUNE.get(menuSuneId);if(!s)return;const updateAndRender=async()=>{s.updatedAt=Date.now();SUNE.save();renderSidebar();await reflectActiveSune()};if(act==='pin'){s.pinned=!s.pinned;await updateAndRender()}else if(act==='rename'){const n=prompt('Rename sune to:',s.name);if(n!=null){s.name=n.trim();await updateAndRender()}}else if(act==='pfp'){const i=document.createElement('input');i.type='file';i.accept='image/*';i.onchange=()=>{const f=i.files?.[0];if(!f)return;const img=new Image;img.onload=async()=>{const c=document.createElement('canvas'),ctx=c.getContext('2d'),D=144;let w=img.width,h=img.height;if(Math.max(w,h)>D)w>h?(h=D*h/w,w=D):(w=D*w/h,h=D);c.width=w;c.height=h;ctx.drawImage(img,0,0,w,h);s.avatar=c.toDataURL('image/webp',.84);await updateAndRender();URL.revokeObjectURL(img.src)};img.src=URL.createObjectURL(f)};i.click()}else if(act==='export')dl(`sune-${(s.name||'sune').replace(/\W/g,'_')}-${ts()}.sune`,[s]);hideSunePopover()})
@@ -210,7 +211,6 @@ const closeSettings=()=>{el.suneModal.classList.add('hidden')}
const tabs={Model:['tabModel','panelModel'],Prompt:['tabPrompt','panelPrompt'],Script:['tabScript','panelScript']}
function showTab(key){Object.entries(tabs).forEach(([k,[tb,pn]])=>{el[tb].classList.toggle('border-black',k===key);el[pn].classList.toggle('hidden',k!==key)});if(key==='Script'){openedHTML=true;showHtmlTab('index');ensureJars().then(({html,extension})=>{const s=SUNE.settings;html.updateCode(s.html||'');extension.updateCode(s.extension_html||'')})}}
el.suneBtnTop.addEventListener('click',openSettings)
el.closeSettings?.addEventListener('click',closeSettings)
el.cancelSettings.addEventListener('click',closeSettings)
el.suneModal.addEventListener('click',e=>{if(e.target===el.suneModal||e.target.classList.contains('bg-black/30'))closeSettings()})
el.tabModel.addEventListener('click',()=>showTab('Model'))