mirror of
https://github.com/multipleof4/devsune.git
synced 2026-01-14 08:27:55 +00:00
Update index.html
This commit is contained in:
@@ -65,6 +65,7 @@
|
||||
<button data-action="pin" class="menu-item"><i data-lucide="pin" class="h-4 w-4"></i><span>Pin to top</span></button>
|
||||
<button data-action="rename" class="menu-item"><i data-lucide="edit-3" class="h-4 w-4"></i><span>Rename</span></button>
|
||||
<button data-action="delete" class="menu-item text-red-600"><i data-lucide="trash-2" class="h-4 w-4"></i><span>Delete</span></button>
|
||||
<button data-action="count-tokens" class="menu-item"><i data-lucide="hash" class="h-4 w-4"></i><span>Count tokens (approx.)</span></button>
|
||||
</div>
|
||||
<div id="suneMenu" class="menu-card hidden">
|
||||
<button data-action="pin" class="menu-item"><i data-lucide="pin" class="h-4 w-4"></i><span>Pin to top</span></button>
|
||||
@@ -149,7 +150,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)||await idb.get(id);if(!th)return;state.currentThreadId=id;clearChat();state.messages=Array.isArray(th.messages)?[...th.messages]:[];for(const m of state.messages){const b=msgRow(m);renderMarkdown(b,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'))}})
|
||||
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)||await idb.get(menuThreadId);if(!th)return;if(act==='pin'){th.pinned=!th.pinned;await idb.put(th)}else if(act==='rename'){const nv=prompt('Rename to:',th.title);if(nv!=null){th.title=titleFrom(nv);th.updatedAt=Date.now();await idb.put(th)}}else if(act==='delete'){if(confirm('Delete this chat?')){await idb.del(th.id);if(state.currentThreadId===th.id){state.currentThreadId=null;clearChat()}}}hideHistoryMenu();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)||await idb.get(menuThreadId);if(!th)return;if(act==='pin'){th.pinned=!th.pinned;await idb.put(th)}else if(act==='rename'){const nv=prompt('Rename to:',th.title);if(nv!=null){th.title=titleFrom(nv);th.updatedAt=Date.now();await idb.put(th)}}else if(act==='delete'){if(confirm('Delete this chat?')){await idb.del(th.id);if(state.currentThreadId===th.id){state.currentThreadId=null;clearChat()}}}else if(act==='count-tokens'){const ENCODER_CDN='https://cdn.jsdelivr.net/npm/gpt-3-encoder@1.1.4/dist/encoder.min.js';const getEncodeFn=()=>{if(typeof window.encode==='function')return window.encode;const names=['gpt3Encoder','GPT3Encoder','encoder','gpt_3_encoder','gpt3_encoder','gpt3'];for(const n of names){const g=window[n];if(!g)continue;if(typeof g==='function')return g;if(g&&typeof g.encode==='function')return g.encode.bind(g)}return null};const loadEncoder=()=>new Promise((res,rej)=>{const e=getEncodeFn();if(e)return res(e);const s=document.createElement('script');s.src=ENCODER_CDN;s.onload=()=>{const fn=getEncodeFn();fn?res(fn):rej(new Error('no encoder'));};s.onerror=()=>rej(new Error('failed to load'));document.head.appendChild(s)});try{const encode=await loadEncoder();const msgs=(Array.isArray(th.messages)?th.messages:[]).filter(m=>m&&m.role&&m.content&&m.role!=='system'&&(m.role==='user'||m.role==='assistant'));let total=0;for(const m of msgs){try{const arr=encode(typeof m.content==='string'?m.content:String(m.content));total+=Array.isArray(arr)?arr.length:0}catch{total+=Math.ceil((m.content||'').length/4)}}alert('Approx. token count: '+total)}catch(err){alert('Token counter failed to load. (\n'+String(err.message||err)+')')}}hideHistoryMenu();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'));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()||s.name}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()})
|
||||
const raf=(fn=>{let id=null;return()=>{if(id)cancelAnimationFrame(id);id=requestAnimationFrame(()=>{id=null;fn()})}})
|
||||
|
||||
Reference in New Issue
Block a user