diff --git a/index.html b/index.html index 441b3d6..34bee11 100644 --- a/index.html +++ b/index.html @@ -120,13 +120,13 @@ const getActiveSune=()=>sunes.find(a=>a.id===su.getActiveId())||sunes[0],createD const store=new Proxy({},{get(_,p){if(p==='apiKey')return globalStore.apiKey;const a=getActiveSune();if(p==='model')return a.settings.model;if(p in a.settings)return a.settings[p];if(p==='system_prompt')return a.settings.system_prompt},set(_,p,v){if(p==='apiKey'){globalStore.apiKey=v;return true}const i=sunes.findIndex(a=>a.id===getActiveSune().id);if(i>=0){if(p==='model')sunes[i].settings.model=v||DEFAULT_MODEL;else if(p==='system_prompt')sunes[i].settings.system_prompt=v||'';else sunes[i].settings[p]=v;su.save(sunes);return true}return false}}) const state={messages:[],busy:false,controller:null,currentThreadId:null,abortRequested:false} const getModelShort=m=>{const mm=m||store.model||'';return mm.includes('/')?mm.split('/').pop():mm} -const reflectActiveSune=()=>{const a=getActiveSune();el.settingsBtnTop.title=`Settings โ€” ${a.name}`;el.settingsBtnTop.innerHTML=a.avatar?``:'๐Ÿค–';if(window.lucide)lucide.createIcons()} -const suneRow=a=>`
` +const reflectActiveSune=()=>{const a=getActiveSune();el.settingsBtnTop.title=`Settings โ€” ${a.name}`;el.settingsBtnTop.innerHTML=a.avatar?``:'โœบ';if(window.lucide)lucide.createIcons()} +const suneRow=a=>`
` const renderSidebar=()=>{const list=[...sunes].sort((a,b)=>(b.pinned-a.pinned));el.suneList.innerHTML=list.map(suneRow).join('');if(window.lucide)lucide.createIcons()} 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)})} const md=window.markdownit({html:false,linkify:true,typographer:true,breaks:true}) const getSuneLabel=m=>{const name=(m&&m.sune_name)||getActiveSune().name,modelShort=getModelShort(m&&m.model);return `${name} ยท ${modelShort}`} -function msgRow(m){const role=typeof m==='string'?m:(m&&m.role)||'assistant';const meta=typeof m==='string'?{}:m||{};const row=document.createElement('div');row.className='flex flex-col gap-2';const head=document.createElement('div');head.className='flex items-center gap-2 px-4';const avatar=document.createElement('div');if(role==='user'){avatar.className='bg-gray-900 text-white msg-avatar shrink-0 h-7 w-7 rounded-full flex items-center justify-center';avatar.textContent='๐Ÿง‘'}else{if(meta&&meta.avatar){avatar.className='msg-avatar shrink-0 h-7 w-7 rounded-full overflow-hidden';const img=document.createElement('img');img.src=meta.avatar;img.className='h-full w-full object-cover';avatar.appendChild(img)}else{avatar.className='bg-gray-200 text-gray-900 msg-avatar shrink-0 h-7 w-7 rounded-full flex items-center justify-center';avatar.textContent='๐Ÿค–'}}const name=document.createElement('div');name.className='text-xs font-medium text-gray-500';name.textContent=role==='user'?'You':getSuneLabel(meta);head.appendChild(avatar);head.appendChild(name);const bubble=document.createElement('div');bubble.className=(role==='user'?'bg-gray-50 border border-gray-200':'bg-gray-100')+' msg-bubble markdown-body rounded-none px-4 py-3 w-full';row.appendChild(head);row.appendChild(bubble);el.messages.appendChild(row);queueMicrotask(()=>el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'}));return bubble} +function msgRow(m){const role=typeof m==='string'?m:(m&&m.role)||'assistant';const meta=typeof m==='string'?{}:m||{};const row=document.createElement('div');row.className='flex flex-col gap-2';const head=document.createElement('div');head.className='flex items-center gap-2 px-4';const avatar=document.createElement('div');if(role==='user'){avatar.className='bg-gray-900 text-white msg-avatar shrink-0 h-7 w-7 rounded-full flex items-center justify-center';avatar.textContent='๐Ÿง‘'}else{if(meta&&meta.avatar){avatar.className='msg-avatar shrink-0 h-7 w-7 rounded-full overflow-hidden';const img=document.createElement('img');img.src=meta.avatar;img.className='h-full w-full object-cover';avatar.appendChild(img)}else{avatar.className='bg-gray-200 text-gray-900 msg-avatar shrink-0 h-7 w-7 rounded-full flex items-center justify-center';avatar.textContent='โœบ'}}const name=document.createElement('div');name.className='text-xs font-medium text-gray-500';name.textContent=role==='user'?'You':getSuneLabel(meta);head.appendChild(avatar);head.appendChild(name);const bubble=document.createElement('div');bubble.className=(role==='user'?'bg-gray-50 border border-gray-200':'bg-gray-100')+' msg-bubble markdown-body rounded-none px-4 py-3 w-full';row.appendChild(head);row.appendChild(bubble);el.messages.appendChild(row);queueMicrotask(()=>el.chat.scrollTo({top:el.chat.scrollHeight,behavior:'smooth'}));return bubble} function renderMarkdown(node,text,opt={enhance:true,highlight:true}){node.innerHTML=md.render(text);if(opt.enhance)enhanceCodeBlocks(node,opt.highlight)} function addMessage(m,track=true){const bubble=msgRow(m);renderMarkdown(bubble,m.content);if(track)state.messages.push(m);return bubble} const addSuneBubbleStreaming=meta=>msgRow(Object.assign({role:'assistant'},meta))