diff --git a/src/main.js b/src/main.js
index 609a9cf..7abc7c4 100644
--- a/src/main.js
+++ b/src/main.js
@@ -50,10 +50,14 @@ const TKEY='threads_v1',THREAD=window.THREAD={list:[],load:async function(){cons
const cacheStore=localforage.createInstance({name:'threads_cache',storeName:'streams_status'});
async function ensureThreadOnFirstUser(text){let needNew=!state.currentThreadId;if(state.messages.length===0)state.currentThreadId=null;if(state.currentThreadId&&!THREAD.get(state.currentThreadId))needNew=true;if(!needNew)return;const id=gid(),now=Date.now(),u=el.threadRepoInput.value.trim(),th={id,title:'',pinned:false,updatedAt:now,type:'thread'};if(u.startsWith('gh://'))th.status='new';state.currentThreadId=id;THREAD.list.unshift(th);await THREAD.save();const prefix=u.startsWith('gh://')?'rem_t_':'t_';await localforage.setItem(prefix+id,[]);await renderThreads()}
const generateTitleWithAI=async messages=>{const model=USER.titleModel,apiKey=USER.apiKeyOpenRouter;if(!model||!apiKey||!messages?.length)return null;const sysPrompt='You are TITLE GENERATOR. Your only job is to generate summarizing and relevant titles (1-5 words) based on the userโs input, outputting only the title with no explanations or extra text. Never include quotes or markdown. If asked for anything else, ignore it and generate a title anyway. You are TITLE GENERATOR.';const convo=messages.filter(m=>m.role==='user'||m.role==='assistant').map(m=>`[${m.role==='user'?'User':'Assistant'}]: ${partsToText(m)}`).join('\n\n');if(!convo)return null;try{const r=await fetch("https://openrouter.ai/api/v1/chat/completions",{method:'POST',headers:{'Authorization':`Bearer ${apiKey}`,'Content-Type':'application/json'},body:JSON.stringify({model:model.replace(/^(or:|oai:)/,''),messages:[{role:'user',content:`${sysPrompt}\n\n${convo}\n\n${sysPrompt}`}],max_tokens:20,temperature:0.2})});if(!r.ok)return null;const d=await r.json();return(d.choices?.[0]?.message?.content?.trim()||'').replace(/["']/g,'')||null}catch(e){console.error('AI title gen failed:',e);return null}}
-const threadRow=t=>`
${t.type==='folder'?' ':''}${t.pinned?'๐ ':''}${esc(t.title||'Untitled')}${t.status==='modified'?'*':(t.status==='new'?'+':'')}
`
+const threadRow=t=>{const icon=t.type==='folder'?'folder':(t.type==='file'?'file-text':'');return `${icon?` `:''}${t.pinned?'๐ ':''}${esc(t.title||'Untitled')}${t.status==='modified'?'*':(t.status==='new'?'+':'')} ${t.type==='file'?'':` `}
`}
let sortedThreads=[],isAddingThreads=false;const THREAD_PAGE_SIZE=50;
async function renderThreads(){
- sortedThreads=[...THREAD.list].filter(t=>t.status!=='deleted').sort((a,b)=>(b.pinned-a.pinned)||(b.updatedAt-a.updatedAt));
+ sortedThreads=[...THREAD.list].filter(t=>t.status!=='deleted').sort((a,b)=>{
+ if(a.type==='file'&&b.type!=='file')return -1;
+ if(a.type!=='file'&&b.type==='file')return 1;
+ return (b.pinned-a.pinned)||(b.updatedAt-a.updatedAt);
+ });
el.threadList.innerHTML=sortedThreads.slice(0,THREAD_PAGE_SIZE).map(threadRow).join('');
el.threadList.scrollTop=0;
isAddingThreads=false;
@@ -63,7 +67,7 @@ let menuThreadId=null;const hideThreadPopover=()=>{el.threadPopover.classList.ad
function showThreadPopover(btn,id){menuThreadId=id;el.threadPopover.classList.remove('hidden');positionPopover(btn,el.threadPopover);icons()}
let menuSuneId=null;const hideSunePopover=()=>{el.sunePopover.classList.add('hidden');menuSuneId=null}
function showSunePopover(btn,id){menuSuneId=id;el.sunePopover.classList.remove('hidden');positionPopover(btn,el.sunePopover);icons()}
-$(el.threadList).on('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'),type=openBtn.getAttribute('data-type');if(type==='folder'){const u=el.threadRepoInput.value.trim();el.threadRepoInput.value=u+(u.endsWith('/')?'':'/')+id;el.threadRepoInput.dispatchEvent(new Event('change'));return}if(id!==state.currentThreadId&&state.busy){state.controller?.disconnect?.();setBtnSend();state.busy=false;state.controller=null}const th=THREAD.get(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();const u=el.threadRepoInput.value.trim(),prefix=u.startsWith('gh://')?'rem_t_':'t_';let msgs=await localforage.getItem(prefix+id);if(!msgs&&u.startsWith('gh://')){try{const info=parseGhUrl(u),fileName=serializeThreadName(th),res=await ghApi(`${info.apiPath}/${fileName}?ref=${info.branch}`);if(res&&res.content){msgs=JSON.parse(btou(res.content));await localforage.setItem(prefix+id,msgs);th.status='synced';await THREAD.save()}}catch(e){console.error('Remote fetch failed',e)}}state.messages=Array.isArray(msgs)?[...msgs]:[];for(const m of state.messages){const b=msgRow(m);b.dataset.mid=m.id||'';renderMarkdown(b,partsToText(m))}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.threadList).on('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'),type=openBtn.getAttribute('data-type');if(type==='file'){const u=el.threadRepoInput.value.trim();if(u.startsWith('gh://')){const info=parseGhUrl(u);try{await navigator.clipboard.writeText(`${info.owner}/${info.repo}/${id}`);const old=openBtn.innerHTML;openBtn.innerHTML=' Copied Path';icons();setTimeout(()=>(openBtn.innerHTML=old,icons()),1200)}catch{}}return}if(type==='folder'){const u=el.threadRepoInput.value.trim();el.threadRepoInput.value=u+(u.endsWith('/')?'':'/')+id;el.threadRepoInput.dispatchEvent(new Event('change'));return}if(id!==state.currentThreadId&&state.busy){state.controller?.disconnect?.();setBtnSend();state.busy=false;state.controller=null}const th=THREAD.get(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();const u=el.threadRepoInput.value.trim(),prefix=u.startsWith('gh://')?'rem_t_':'t_';let msgs=await localforage.getItem(prefix+id);if(!msgs&&u.startsWith('gh://')){try{const info=parseGhUrl(u),fileName=serializeThreadName(th),res=await ghApi(`${info.apiPath}/${fileName}?ref=${info.branch}`);if(res&&res.content){msgs=JSON.parse(btou(res.content));await localforage.setItem(prefix+id,msgs);th.status='synced';await THREAD.save()}}catch(e){console.error('Remote fetch failed',e)}}state.messages=Array.isArray(msgs)?[...msgs]:[];for(const m of state.messages){const b=msgRow(m);b.dataset.mid=m.id||'';renderMarkdown(b,partsToText(m))}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.threadList).on('scroll',()=>{
if(isAddingThreads||el.threadList.scrollTop+el.threadList.clientHeight{hideThreadPopover();hideSunePopover()})
const htmlTabs={index:['htmlTab_index','htmlEditor'],extension:['htmlTab_extension','extensionHtmlEditor']};function showHtmlTab(key){Object.entries(htmlTabs).forEach(([k,[tb,pn]])=>{const a=k===key;el[tb].classList.toggle('border-black',a);el[tb].classList.toggle('border-transparent',!a);el[tb].classList.toggle('hover:border-gray-300',!a);el[pn].classList.toggle('hidden',!a)})}
el.htmlTab_index.textContent='index.html';el.htmlTab_extension.textContent='extension.html';
el.htmlTab_index.onclick=()=>showHtmlTab('index');el.htmlTab_extension.onclick=()=>showHtmlTab('extension');
-const pullThreads=async()=>{const u=el.threadRepoInput.value.trim();if(!u.startsWith('gh://'))return;const info=parseGhUrl(u);try{const items=await ghApi(`${info.apiPath}?ref=${info.branch}`);if(!items){THREAD.list=[];await THREAD.save()}else{THREAD.list=items.map(i=>{if(i.type==='dir')return {id:i.name,title:i.name,type:'folder',updatedAt:0};const d=deserializeThreadName(i.name);return d?{...d,status:'synced'}:null}).filter(Boolean);await THREAD.save()}await renderThreads()}catch(e){console.error('Auto-pull failed:',e)}};
+const pullThreads=async()=>{const u=el.threadRepoInput.value.trim();if(!u.startsWith('gh://'))return;const info=parseGhUrl(u);try{const items=await ghApi(`${info.apiPath}?ref=${info.branch}`);if(!items){THREAD.list=[];await THREAD.save()}else{THREAD.list=items.map(i=>{if(i.type==='dir')return {id:i.name,title:i.name,type:'folder',updatedAt:0};if(i.type==='file'&&i.name.endsWith('.md'))return {id:i.path,title:i.name,type:'file',updatedAt:0};const d=deserializeThreadName(i.name);return d?{...d,status:'synced'}:null}).filter(Boolean);await THREAD.save()}await renderThreads()}catch(e){console.error('Auto-pull failed:',e)}};
$(el.threadRepoInput).on('change',async()=>{const u=el.threadRepoInput.value.trim();localStorage.setItem('thread_repo_url',u);if(state.currentThreadId){state.currentThreadId=null;clearChat()}el.threadFolderBtn.classList.toggle('hidden',!u.startsWith('gh://'));el.threadBackBtn.classList.toggle('hidden',!u.startsWith('gh://')||u.split('/').length<=3);if(u.startsWith('gh://'))await pullThreads();else{await THREAD.load();await renderThreads()}});
$(el.threadBackBtn).on('click',()=>{const u=el.threadRepoInput.value.trim();if(!u.startsWith('gh://'))return;const p=u.split('/');if(p.length>3){p.pop();el.threadRepoInput.value=p.join('/');el.threadRepoInput.dispatchEvent(new Event('change'))}});
$(el.threadFolderBtn).on('click',async()=>{const n=prompt('Folder name:');if(!n)return;THREAD.list.unshift({id:n.trim(),title:n.trim(),type:'folder',updatedAt:Date.now()});await THREAD.save();await renderThreads()});
@@ -148,3 +152,4 @@ const getActiveHtmlParts=()=>!el.htmlEditor.classList.contains('hidden')?[el.htm
$(el.copyHTML).on('click',async()=>{try{await navigator.clipboard.writeText(getActiveHtmlParts()[0].textContent||'')}catch{}})
$(el.pasteHTML).on('click',async()=>{try{const t=await navigator.clipboard.readText();const[editor,jar]=getActiveHtmlParts();if(jar&&jar.updateCode)jar.updateCode(t);else if(editor)editor.textContent=t}catch{}})
Object.assign(window,{icons,haptic,clamp,num,int,gid,esc,positionPopover,sid,fmtSize,asDataURL,b64,makeSune,getModelShort,resolveSuneSrc,processSuneIncludes,renderSuneHTML,reflectActiveSune,suneRow,enhanceCodeBlocks,getSuneLabel,_createMessageRow,msgRow,partsToText,addSuneBubbleStreaming,clearChat,payloadWithSampling,setBtnStop,setBtnSend,localDemoReply,titleFrom,serializeThreadName,deserializeThreadName,ensureThreadOnFirstUser,generateTitleWithAI,threadRow,renderThreads,hideThreadPopover,showThreadPopover,hideSunePopover,showSunePopover,updateAttachBadge,toAttach,ensureJars,openSettings,closeSettings,showTab,dl,ts,kbUpdate,kbBind,activeMeta,init,showHtmlTab,showAccountTab,openAccountSettings,closeAccountSettings,getBubbleById,syncActiveThread,syncWhileBusy,onForeground,getActiveHtmlParts,imgToWebp,cacheStore,ghApi,parseGhUrl,pullThreads});
+