mirror of
https://github.com/multipleof4/sune.git
synced 2026-01-14 08:38:00 +00:00
Revert: Update main.js
This commit is contained in:
17
src/main.js
17
src/main.js
@@ -1,6 +1,6 @@
|
||||
(()=>{let k,v=visualViewport;const f=()=>{removeEventListener('popstate',f),document.activeElement?.blur()};v.onresize=()=>{let o=v.height<innerHeight;o!=k&&((k=o)?(history.pushState({k:1},''),addEventListener('popstate',f)):(removeEventListener('popstate',f),history.state?.k&&history.back()))}})()
|
||||
const DEFAULT_MODEL='google/gemini-2.5-pro',DEFAULT_API_KEY=''
|
||||
const el=window.el=Object.fromEntries(['topbar','chat','messages','composer','input','sendBtn','suneBtnTop','suneModal','suneURL','settingsForm','closeSettings','cancelSettings','tabModel','tabPrompt','tabScript','panelModel','panelPrompt','panelScript','set_model','set_temperature','set_top_p','set_top_k','set_frequency_penalty','set_repetition_penalty','set_min_p','set_top_a','set_verbosity','set_reasoning_effort','set_system_prompt','set_hide_composer','set_include_thoughts','set_json_output','set_image_output','set_ignore_master_prompt','deleteSuneBtn','sidebarLeft','sidebarOverlayLeft','sidebarBtnLeft','suneList','newSuneBtn','userMenuBtn','userMenu','accountSettingsOption','sunesImportOption','sunesExportOption','threadsImportOption','threadsExportOption','importInput','sidebarBtnRight','sidebarRight','sidebarOverlayRight','threadList','closeThreads','threadPopover','sunePopover','footer','attachBtn','attachBadge','fileInput','htmlEditor','extensionHtmlEditor','jsonSchemaEditor','htmlTab_index','htmlTab_extension','suneHtml','accountSettingsModal','accountSettingsForm','closeAccountSettings','cancelAccountSettings','set_master_prompt','set_provider','set_api_key_or','set_api_key_oai','set_api_key_g','set_api_key_claude','set_api_key_cf','set_title_model','copySystemPrompt','pasteSystemPrompt','copyHTML','pasteHTML','accountTabGeneral','accountTabAPI','accountPanelGeneral','accountPanelAPI','set_gh_token','gcpSAInput','gcpSAUploadBtn','importAccountSettings','exportAccountSettings','importAccountSettingsInput','accountTabUser','accountPanelUser','set_user_name','userAvatarPreview','setUserAvatarBtn','userAvatarInput','set_donor'].map(id=>[id,$('#'+id)[0]]))
|
||||
const el=window.el=Object.fromEntries(['topbar','chat','messages','composer','input','sendBtn','suneBtnTop','suneModal','suneURL','settingsForm','closeSettings','cancelSettings','tabModel','tabPrompt','tabScript','panelModel','panelPrompt','panelScript','set_model','set_temperature','set_top_p','set_top_k','set_frequency_penalty','set_repetition_penalty','set_min_p','set_top_a','set_verbosity','set_reasoning_effort','set_system_prompt','set_hide_composer','set_include_thoughts','set_json_output','set_ignore_master_prompt','deleteSuneBtn','sidebarLeft','sidebarOverlayLeft','sidebarBtnLeft','suneList','newSuneBtn','userMenuBtn','userMenu','accountSettingsOption','sunesImportOption','sunesExportOption','threadsImportOption','threadsExportOption','importInput','sidebarBtnRight','sidebarRight','sidebarOverlayRight','threadList','closeThreads','threadPopover','sunePopover','footer','attachBtn','attachBadge','fileInput','htmlEditor','extensionHtmlEditor','jsonSchemaEditor','htmlTab_index','htmlTab_extension','suneHtml','accountSettingsModal','accountSettingsForm','closeAccountSettings','cancelAccountSettings','set_master_prompt','set_provider','set_api_key_or','set_api_key_oai','set_api_key_g','set_api_key_claude','set_api_key_cf','set_title_model','copySystemPrompt','pasteSystemPrompt','copyHTML','pasteHTML','accountTabGeneral','accountTabAPI','accountPanelGeneral','accountPanelAPI','set_gh_token','gcpSAInput','gcpSAUploadBtn','importAccountSettings','exportAccountSettings','importAccountSettingsInput','accountTabUser','accountPanelUser','set_user_name','userAvatarPreview','setUserAvatarBtn','userAvatarInput','set_donor'].map(id=>[id,$('#'+id)[0]]))
|
||||
const icons=()=>window.lucide&&lucide.createIcons()
|
||||
const haptic=()=>/android/i.test(navigator.userAgent)&&navigator.vibrate?.(1)
|
||||
const clamp=(v,min,max)=>Math.max(min,Math.min(max,v)),num=(v,d)=>v==null||v===''||isNaN(+v)?d:+v,int=(v,d)=>v==null||v===''||isNaN(parseInt(v))?d:parseInt(v),gid=()=>Math.random().toString(36).slice(2,9),esc=s=>String(s).replace(/[&<>'"`]/g,c=>({"&":"&","<":"<",">":">","\"":""","'":"'","`":"`"}[c])),positionPopover=(a,p)=>{const r=a.getBoundingClientRect();p.style.top=`${r.bottom+p.offsetHeight+4>window.innerHeight?r.top-p.offsetHeight-4:r.bottom+4}px`;p.style.left=`${Math.max(8,Math.min(r.right-p.offsetWidth,window.innerWidth-p.offsetWidth-8))}px`}
|
||||
@@ -10,7 +10,7 @@ const asDataURL=f=>new Promise(r=>{const fr=new FileReader();fr.onload=()=>r(Str
|
||||
const imgToWebp=(f,D=128,q=80)=>new Promise((r,j)=>{if(!f)return j();const i=new Image;i.onload=()=>{const c=document.createElement('canvas'),x=c.getContext('2d');let w=i.width,h=i.height;if(D>0&&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;x.drawImage(i,0,0,w,h);r(c.toDataURL('image/webp',clamp(q,0,100)/100));URL.revokeObjectURL(i.src)};i.onerror=j;i.src=URL.createObjectURL(f)});
|
||||
const b64=x=>x.split(',')[1]||''
|
||||
const su={key:'sunes_v1',activeKey:'active_sune_id',load(){try{return JSON.parse(localStorage.getItem(this.key)||'[]')}catch{return[]}},save(list){localStorage.setItem(this.key,JSON.stringify(list||[]))},getActiveId(){return localStorage.getItem(this.activeKey)||null},setActiveId(id){localStorage.setItem(this.activeKey,id||'')}}
|
||||
const defaultSettings={model:DEFAULT_MODEL,temperature:'',top_p:'',top_k:'',frequency_penalty:'',repetition_penalty:'',min_p:'',top_a:'',verbosity:'',reasoning_effort:'default',system_prompt:'',html:'',extension_html:"<sune src='https://raw.githubusercontent.com/sune-org/store/refs/heads/main/sync.sune' private></sune>",hide_composer:false,include_thoughts:false,json_output:false,image_output:false,ignore_master_prompt:false,json_schema:''}
|
||||
const defaultSettings={model:DEFAULT_MODEL,temperature:'',top_p:'',top_k:'',frequency_penalty:'',repetition_penalty:'',min_p:'',top_a:'',verbosity:'',reasoning_effort:'default',system_prompt:'',html:'',extension_html:"<sune src='https://raw.githubusercontent.com/sune-org/store/refs/heads/main/sync.sune' private></sune>",hide_composer:false,include_thoughts:false,json_output:false,ignore_master_prompt:false,json_schema:''}
|
||||
const makeSune=(p={})=>({id:p.id||gid(),name:p.name?.trim()||'Default',pinned:!!p.pinned,avatar:p.avatar||'',url:p.url||'',updatedAt:p.updatedAt||Date.now(),settings:Object.assign({},defaultSettings,p.settings||{}),storage:p.storage||{}})
|
||||
let sunes=(su.load()||[]).map(makeSune)
|
||||
const SUNE=window.SUNE=new Proxy({get list(){return sunes},get id(){return su.getActiveId()},get active(){return sunes.find(a=>a.id===su.getActiveId())||sunes[0]},get:id=>sunes.find(s=>s.id===id),setActive:id=>su.setActiveId(id||''),create(p={}){const s=makeSune(p);sunes.unshift(s);su.save(sunes);return s},delete(id){const curId=this.id;sunes=sunes.filter(s=>s.id!==id);su.save(sunes);if(sunes.length===0){const def=this.create({name:'Default'});this.setActive(def.id)}else if(curId===id)this.setActive(sunes[0].id)},save:()=>su.save(sunes)},{get(t,p){if(p==='fetchDotSune')return async g=>{try{const u=g.startsWith('http')?g:(()=>{const[a,b]=g.split('@'),[c,d]=a.split('/'),[e,...f]=b.split('/');return`https://raw.githubusercontent.com/${c}/${d}/${e}/${f.join('/')}`})(),j=await(await fetch(u)).json(),l=sunes.length;sunes.unshift(...(Array.isArray(j)?j:j?.sunes||[]).filter(s=>s?.id&&!t.get(s.id)).map(s=>makeSune(s)));sunes.length>l&&t.save()}catch{}};if(p==='attach')return async files=>{const arr=[];for(const f of files||[])arr.push(await toAttach(f));const clean=arr.filter(Boolean);if(!clean.length)return;await ensureThreadOnFirstUser('(attachments)');addMessage({role:'assistant',content:clean,...activeMeta()});await THREAD.persist()};if(p==='log')return async s=>{const t=String(s??'').trim();if(!t)return;await ensureThreadOnFirstUser(t);addMessage({role:'assistant',content:[{type:'text',text:t}],...activeMeta()});await THREAD.persist()};if(p==='lastReply')return [...state.messages].reverse().find(m=>m.role==='assistant');if(p==='infer')return async()=>{if(state.busy||!SUNE.model||state.abortRequested){state.abortRequested=false;return};await ensureThreadOnFirstUser('Sune Inference');const th=THREAD.active;if(th&&!th.title)(async()=>THREAD.setTitle(th.id,await generateTitleWithAI(state.messages)||'Sune Inference'))();state.busy=true;setBtnStop();const a=SUNE.active,suneMeta={sune_name:a.name,model:SUNE.model,avatar:a.avatar||''},streamId=sid(),suneBubble=addSuneBubbleStreaming(suneMeta,streamId);suneBubble.dataset.mid=streamId;const assistantMsg=Object.assign({id:streamId,role:'assistant',content:[{type:'text',text:''}]},suneMeta);state.messages.push(assistantMsg);THREAD.persist(false);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});assistantMsg.content[0].text=buf;if(done&&!completed){completed=true;setBtnSend();state.busy=false;enhanceCodeBlocks(suneBubble,true);THREAD.persist(true);el.composer.dispatchEvent(new CustomEvent('sune:newSuneResponse',{detail:{message:assistantMsg}}));state.stream={rid:null,bubble:null,meta:null,text:'',done:false}}else if(!done)THREAD.persist(false)};await askOpenRouterStreaming(onDelta,streamId)};if(p==='getByName')return n=>sunes.find(s=>s.name.toLowerCase()===(n||'').trim().toLowerCase());if(p==='handoff')return async n=>{await new Promise(r=>setTimeout(r,4000));const s=sunes.find(s=>s.name.toLowerCase()===(n||'').trim().toLowerCase());if(!s)return;SUNE.setActive(s.id);renderSidebar();await reflectActiveSune();await SUNE.infer()};if(p in t)return t[p];const a=t.active;if(!a)return;if(p in a.settings)return a.settings[p];if(p in a)return a[p]},set(t,p,v){const a=t.active;if(!a)return false;const i=sunes.findIndex(s=>s.id===a.id);if(i<0)return false;const isTopLevel=/^(name|avatar|url|pinned|storage)$/.test(p),target=isTopLevel?sunes[i]:sunes[i].settings;let value=v;if(!isTopLevel){if(p==='system_prompt')value=v||''}if(target[p]!==value){target[p]=value;sunes[i].updatedAt=Date.now();su.save(sunes)}return true}})
|
||||
@@ -41,7 +41,7 @@ const titleFrom=t=>(t||'').replace(/\s+/g,' ').trim().slice(0,60)||'Untitled'
|
||||
const TKEY='threads_v1',THREAD=window.THREAD={list:[],load:async function(){this.list=await localforage.getItem(TKEY).then(v=>Array.isArray(v)?v:[])||[]},save:async function(){await localforage.setItem(TKEY,this.list)},get:function(id){return this.list.find(t=>t.id===id)},get active(){return this.get(state.currentThreadId)},persist:async function(full=true){if(!state.currentThreadId)return;const th=this.active;if(!th)return;th.messages=[...state.messages];if(full){th.updatedAt=Date.now()}await this.save();if(full)await renderThreads()},setTitle:async function(id,title){const th=this.get(id);if(!th||!title)return;th.title=titleFrom(title);th.updatedAt=Date.now();await this.save();await renderThreads()},getLastAssistantMessageId:()=>{const a=[...el.messages.querySelectorAll('.msg-bubble')];for(let i=a.length-1;i>=0;i--){const b=a[i],h=b.previousElementSibling;if(!h)continue;if(!/^\s*You\b/.test(h.textContent||''))return b.dataset.mid||null}return null}}
|
||||
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(),th={id,title:'',pinned:false,updatedAt:now,messages:[]};state.currentThreadId=id;THREAD.list.unshift(th);await THREAD.save();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.content)}`).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 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.content)}`).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=>`<div class=\"relative flex items-center gap-2 px-3 py-2 ${t.pinned?'bg-yellow-50':''}\"><button data-open-thread=\"${t.id}\" class=\"flex-1 text-left truncate\">${t.pinned?'📌 ':''}${esc(t.title)}</button><button data-thread-menu=\"${t.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>`
|
||||
let sortedThreads=[],isAddingThreads=false;const THREAD_PAGE_SIZE=50;
|
||||
async function renderThreads(){
|
||||
@@ -75,10 +75,10 @@ function updateAttachBadge(){const n=state.attachments.length;el.attachBadge.tex
|
||||
async function toAttach(file){if(!file)return null;if(file instanceof File){const name=file.name||'file',mime=(file.type||'application/octet-stream').toLowerCase();if(/^image\//.test(mime)||/\.(png|jpe?g|webp|gif)$/i.test(name)){const data=mime==='image/webp'||/\.webp$/i.test(name)?await asDataURL(file):await imgToWebp(file,2048,94);return{type:'image_url',image_url:{url:data}}}if(mime==='application/pdf'||/\.pdf$/i.test(name)){const data=await asDataURL(file),bin=b64(data);return{type:'file',file:{filename:name.endsWith('.pdf')?name:name+'.pdf',file_data:bin}}}if(/^audio\//.test(mime)||/\.(wav|mp3)$/i.test(name)){const data=await asDataURL(file),bin=b64(data),fmt=/mp3/.test(mime)||/\.mp3$/i.test(name)?'mp3':'wav';return{type:'input_audio',input_audio:{data:bin,format:fmt}}}const data=await asDataURL(file),bin=b64(data);return{type:'file',file:{filename:name,file_data:bin}}}if(file&&file.name==null&&file.data){const name=file.name||'file',mime=(file.mime||'application/octet-stream').toLowerCase();if(/^image\//.test(mime)){const url=`data:${mime};base64,${file.data}`;return{type:'image_url',image_url:{url}}}if(mime==='application/pdf'){return{type:'file',file:{filename:name,file_data:file.data}}}if(/^audio\//.test(mime)){const fmt=/mp3/.test(mime)?'mp3':'wav';return{type:'input_audio',input_audio:{data:file.data,format:fmt}}}return{type:'file',file:{filename:name,file_data:file.data}}}return null}
|
||||
$(el.attachBtn).on('click',()=>{if(state.busy)return;if(state.attachments.length){state.attachments=[];updateAttachBadge();el.fileInput.value=''};el.fileInput.click()})
|
||||
$(el.fileInput).on('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.composer).on('submit',async e=>{e.preventDefault();if(state.busy)return;const text=el.input.value.trim();if(!text&&!state.attachments.length)return SUNE.infer();await ensureThreadOnFirstUser(text||'(attachments)');const th=THREAD.active,shouldGenTitle=th&&!th.title;el.input.value='';const parts=[];if(text)parts.push({type:'text',text});parts.push(...state.attachments);const userMsg={role:'user',content:parts.length?parts:[{type:'text',text:text||'(sent attachments)'}]};addMessage(userMsg);el.composer.dispatchEvent(new CustomEvent('user:send',{detail:{message:userMsg}}));if(shouldGenTitle)(async()=>{const title=await generateTitleWithAI(state.messages)||partsToText(state.messages.find(m=>m.role==='user')?.content)||'Untitled';await THREAD.setTitle(th.id,title)})();if(!SUNE.model)return state.attachments=[],updateAttachBadge();state.busy=true;setBtnStop();const a=SUNE.active,suneMeta={sune_name:a.name,model:SUNE.model,avatar:a.avatar||''},streamId=sid(),suneBubble=addSuneBubbleStreaming(suneMeta, streamId);suneBubble.dataset.mid=streamId;const assistantMsg=Object.assign({id:streamId,role:'assistant',content:[{type:'text',text:''}]},suneMeta);state.messages.push(assistantMsg);THREAD.persist(false);state.stream={rid:streamId,bubble:suneBubble,meta:suneMeta,text:'',done:false};let buf='',completed=false;const onDelta=(delta,done,images)=>{buf+=delta;state.stream.text=buf;if(images&&images.length){const imgIdx=assistantMsg.content.findIndex(c=>c.type==='image_url');if(imgIdx>=0)assistantMsg.content.splice(imgIdx,1);assistantMsg.content.push(...images);const imgHTML=images.map(img=>`<img src="${esc(img.image_url.url)}" class="max-w-full rounded-xl border border-gray-200 my-2"/>`).join('');suneBubble.innerHTML=md.render(buf)+imgHTML}else{renderMarkdown(suneBubble,buf,{enhance:false})}assistantMsg.content[0].text=buf;if(done&&!completed){completed=true;setBtnSend();state.busy=false;enhanceCodeBlocks(suneBubble,true);THREAD.persist(true);el.composer.dispatchEvent(new CustomEvent('sune:newSuneResponse',{detail:{message:assistantMsg}}));state.stream={rid:null,bubble:null,meta:null,text:'',done:false}}else if(!done)THREAD.persist(false)};await askOpenRouterStreaming(onDelta,streamId);state.attachments=[];updateAttachBadge()})
|
||||
$(el.composer).on('submit',async e=>{e.preventDefault();if(state.busy)return;const text=el.input.value.trim();if(!text&&!state.attachments.length)return SUNE.infer();await ensureThreadOnFirstUser(text||'(attachments)');const th=THREAD.active,shouldGenTitle=th&&!th.title;el.input.value='';const parts=[];if(text)parts.push({type:'text',text});parts.push(...state.attachments);const userMsg={role:'user',content:parts.length?parts:[{type:'text',text:text||'(sent attachments)'}]};addMessage(userMsg);el.composer.dispatchEvent(new CustomEvent('user:send',{detail:{message:userMsg}}));if(shouldGenTitle)(async()=>{const title=await generateTitleWithAI(state.messages)||partsToText(state.messages.find(m=>m.role==='user')?.content)||'Untitled';await THREAD.setTitle(th.id,title)})();if(!SUNE.model)return state.attachments=[],updateAttachBadge();state.busy=true;setBtnStop();const a=SUNE.active,suneMeta={sune_name:a.name,model:SUNE.model,avatar:a.avatar||''},streamId=sid(),suneBubble=addSuneBubbleStreaming(suneMeta, streamId);suneBubble.dataset.mid=streamId;const assistantMsg=Object.assign({id:streamId,role:'assistant',content:[{type:'text',text:''}]},suneMeta);state.messages.push(assistantMsg);THREAD.persist(false);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});assistantMsg.content[0].text=buf;if(done&&!completed){completed=true;setBtnSend();state.busy=false;enhanceCodeBlocks(suneBubble,true);THREAD.persist(true);el.composer.dispatchEvent(new CustomEvent('sune:newSuneResponse',{detail:{message:assistantMsg}}));state.stream={rid:null,bubble:null,meta:null,text:'',done:false}}else if(!done)THREAD.persist(false)};await askOpenRouterStreaming(onDelta,streamId);state.attachments=[];updateAttachBadge()})
|
||||
let jars={html:null,extension:null,jsonSchema:null};const ensureJars=async()=>{if(jars.html&&jars.extension&&jars.jsonSchema)return jars;const mod=await import('https://medv.io/codejar/codejar.js'),CodeJar=mod.CodeJar||mod.default,hl=e=>e.innerHTML=hljs.highlight(e.textContent,{language:'xml'}).value,hl_json=e=>e.innerHTML=hljs.highlight(e.textContent,{language:'json'}).value;if(!jars.html)jars.html=CodeJar(el.htmlEditor,hl,{tab:' '});if(!jars.extension)jars.extension=CodeJar(el.extensionHtmlEditor,hl,{tab:' '});if(!jars.jsonSchema)jars.jsonSchema=CodeJar(el.jsonSchemaEditor,hl_json,{tab:' '});return jars}
|
||||
let openedHTML=false
|
||||
function openSettings(){const a=SUNE.active,s=a.settings;openedHTML=false;el.suneURL.value=a.url||'';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_repetition_penalty.value=s.repetition_penalty;el.set_min_p.value=s.min_p;el.set_top_a.value=s.top_a;el.set_verbosity.value=s.verbosity||'';el.set_reasoning_effort.value=s.reasoning_effort||'default';el.set_system_prompt.value=s.system_prompt;el.set_hide_composer.checked=!!s.hide_composer;el.set_json_output.checked=!!s.json_output;el.set_image_output.checked=!!s.image_output;el.set_include_thoughts.checked=!!s.include_thoughts;el.set_ignore_master_prompt.checked=!!s.ignore_master_prompt;showTab('Model');el.suneModal.classList.remove('hidden')}
|
||||
function openSettings(){const a=SUNE.active,s=a.settings;openedHTML=false;el.suneURL.value=a.url||'';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_repetition_penalty.value=s.repetition_penalty;el.set_min_p.value=s.min_p;el.set_top_a.value=s.top_a;el.set_verbosity.value=s.verbosity||'';el.set_reasoning_effort.value=s.reasoning_effort||'default';el.set_system_prompt.value=s.system_prompt;el.set_hide_composer.checked=!!s.hide_composer;el.set_json_output.checked=!!s.json_output;el.set_include_thoughts.checked=!!s.include_thoughts;el.set_ignore_master_prompt.checked=!!s.ignore_master_prompt;showTab('Model');el.suneModal.classList.remove('hidden')}
|
||||
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==='Prompt'){ensureJars().then(({jsonSchema})=>{const s=SUNE.settings;jsonSchema.updateCode(s.json_schema||'')})}else if(key==='Script'){openedHTML=true;showHtmlTab('index');ensureJars().then(({html,extension})=>{const s=SUNE.settings;html.updateCode(s.html||'');extension.updateCode(s.extension_html||'')})}}
|
||||
@@ -88,7 +88,7 @@ $(el.suneModal).on('click',e=>{if(e.target===el.suneModal||e.target.classList.co
|
||||
$(el.tabModel).on('click',()=>showTab('Model'))
|
||||
$(el.tabPrompt).on('click',()=>showTab('Prompt'))
|
||||
$(el.tabScript).on('click',()=>showTab('Script'))
|
||||
$(el.settingsForm).on('submit',async e=>{e.preventDefault();SUNE.url=(el.suneURL.value||'').trim();SUNE.model=(el.set_model.value||'').trim();['temperature','top_p','top_k','frequency_penalty','repetition_penalty','min_p','top_a'].forEach(k=>SUNE[k]=el[`set_${k}`].value.trim());SUNE.verbosity=(el.set_verbosity.value||'');SUNE.reasoning_effort=(el.set_reasoning_effort.value||'default');SUNE.system_prompt=el.set_system_prompt.value.trim();SUNE.hide_composer=el.set_hide_composer.checked;SUNE.json_output=el.set_json_output.checked;SUNE.image_output=el.set_image_output.checked;SUNE.include_thoughts=el.set_include_thoughts.checked;SUNE.ignore_master_prompt=el.set_ignore_master_prompt.checked;SUNE.json_schema=el.jsonSchemaEditor.textContent;if(openedHTML){SUNE.html=el.htmlEditor.textContent;SUNE.extension_html=el.extensionHtmlEditor.textContent}closeSettings();await reflectActiveSune()})
|
||||
$(el.settingsForm).on('submit',async e=>{e.preventDefault();SUNE.url=(el.suneURL.value||'').trim();SUNE.model=(el.set_model.value||'').trim();['temperature','top_p','top_k','frequency_penalty','repetition_penalty','min_p','top_a'].forEach(k=>SUNE[k]=el[`set_${k}`].value.trim());SUNE.verbosity=(el.set_verbosity.value||'');SUNE.reasoning_effort=(el.set_reasoning_effort.value||'default');SUNE.system_prompt=el.set_system_prompt.value.trim();SUNE.hide_composer=el.set_hide_composer.checked;SUNE.json_output=el.set_json_output.checked;SUNE.include_thoughts=el.set_include_thoughts.checked;SUNE.ignore_master_prompt=el.set_ignore_master_prompt.checked;SUNE.json_schema=el.jsonSchemaEditor.textContent;if(openedHTML){SUNE.html=el.htmlEditor.textContent;SUNE.extension_html=el.extensionHtmlEditor.textContent}closeSettings();await reflectActiveSune()})
|
||||
$(el.deleteSuneBtn).on('click',async()=>{const activeId=SUNE.id,name=SUNE.name||'this sune';if(!confirm(`Delete "${name}"?`))return;SUNE.delete(activeId);renderSidebar();await reflectActiveSune();state.currentThreadId=null;clearChat();closeSettings()})
|
||||
$(el.newSuneBtn).on('click',async()=>{const name=prompt('Name your sune:');if(!name)return;const sune=SUNE.create({name:name.trim()});SUNE.setActive(sune.id);renderSidebar();await reflectActiveSune();state.currentThreadId=null;clearChat();document.getElementById('sidebarLeft').classList.add('-translate-x-full');document.getElementById('sidebarOverlayLeft').classList.add('hidden')})
|
||||
function dl(name,obj){const blob=new Blob([JSON.stringify(obj,null,2)],{type:name.endsWith('.sune')?'application/octet-stream':'application/json'}),url=URL.createObjectURL(blob),a=$('<a>').prop({href:url,download:name}).appendTo('body');a.get(0).click();a.remove();URL.revokeObjectURL(url)}
|
||||
@@ -110,8 +110,8 @@ el.htmlTab_index.textContent='index.html';el.htmlTab_extension.textContent='exte
|
||||
el.htmlTab_index.onclick=()=>showHtmlTab('index');el.htmlTab_extension.onclick=()=>showHtmlTab('extension');
|
||||
init()
|
||||
const HTTP_BASE='https://orp.aww.4ev.link/ws'
|
||||
const buildBody=()=>{const msgs=[];if(USER.masterPrompt&&!SUNE.ignore_master_prompt)msgs.push({role:'system',content:[{type:'text',text:USER.masterPrompt}]});if(SUNE.system_prompt)msgs.push({role:'system',content:[{type:'text',text:SUNE.system_prompt}]});msgs.push(...state.messages.filter(m=>m.role!=='system').map(m=>({role:m.role,content:m.content})));const b=payloadWithSampling({model:SUNE.model.replace(/^(or:|oai:|g:|cla:|cf:)/,''),messages:msgs,stream:true});if(SUNE.image_output)b.modalities=['image','text'];if(SUNE.json_output){let s;try{s=JSON.parse(SUNE.json_schema||'null')}catch{s=null}if(s&&typeof s==='object'&&Object.keys(s).length>0){b.response_format={type:'json_schema',json_schema:s}}else{b.response_format={type:'json_object'}}}b.reasoning={...(SUNE.reasoning_effort&&SUNE.reasoning_effort!=='default'?{effort:SUNE.reasoning_effort}:{}),exclude:!SUNE.include_thoughts};if(SUNE.verbosity)b.verbosity=SUNE.verbosity;return b}
|
||||
async function askOpenRouterStreaming(onDelta,streamId){const model=SUNE.model,provider=model.startsWith('oai:')?'openai':model.startsWith('g:')?'google':model.startsWith('cla:')?'claude':model.startsWith('cf:')?'cloudflare':model.startsWith('or:')?'openrouter':USER.provider,apiKey=provider==='openai'?USER.apiKeyOpenAI:provider==='google'?USER.apiKeyGoogle:provider==='claude'?USER.apiKeyClaude:provider==='cloudflare'?USER.apiKeyCloudflare:USER.apiKeyOpenRouter;if(!apiKey){onDelta(localDemoReply(),true);return {ok:true,rid:streamId||null}}const r={rid:streamId||gid(),seq:-1,done:false,signaled:false,ws:null};await cacheStore.setItem(r.rid,'busy');const signal=(t,imgs)=>{if(!r.signaled){r.signaled=true;onDelta(t||'',true,imgs)}};const ws=new WebSocket(HTTP_BASE.replace('https','wss')+'?uid='+encodeURIComponent(r.rid));r.ws=ws;ws.onopen=()=>ws.send(JSON.stringify({type:'begin',rid:r.rid,provider,apiKey,or_body:buildBody()}));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,m.images)}else if(m.type==='done'||m.type==='err'){r.done=true;cacheStore.setItem(r.rid,'done');signal(m.type==='err'?'\n\n'+(m.message||'error'):'',m.images);ws.close()}};ws.onclose=()=>{};ws.onerror=()=>{};state.controller={abort:()=>{r.done=true;cacheStore.setItem(r.rid,'done');try{if(ws.readyState===1)ws.send(JSON.stringify({type:'stop',rid:r.rid}))}catch{};signal('')},disconnect:()=>ws.close()};return {ok:true,rid:r.rid}}
|
||||
const buildBody=()=>{const msgs=[];if(USER.masterPrompt&&!SUNE.ignore_master_prompt)msgs.push({role:'system',content:[{type:'text',text:USER.masterPrompt}]});if(SUNE.system_prompt)msgs.push({role:'system',content:[{type:'text',text:SUNE.system_prompt}]});msgs.push(...state.messages.filter(m=>m.role!=='system').map(m=>({role:m.role,content:m.content})));const b=payloadWithSampling({model:SUNE.model.replace(/^(or:|oai:|g:|cla:|cf:)/,''),messages:msgs,stream:true});if(SUNE.json_output){let s;try{s=JSON.parse(SUNE.json_schema||'null')}catch{s=null}if(s&&typeof s==='object'&&Object.keys(s).length>0){b.response_format={type:'json_schema',json_schema:s}}else{b.response_format={type:'json_object'}}}b.reasoning={...(SUNE.reasoning_effort&&SUNE.reasoning_effort!=='default'?{effort:SUNE.reasoning_effort}:{}),exclude:!SUNE.include_thoughts};if(SUNE.verbosity)b.verbosity=SUNE.verbosity;return b}
|
||||
async function askOpenRouterStreaming(onDelta,streamId){const model=SUNE.model,provider=model.startsWith('oai:')?'openai':model.startsWith('g:')?'google':model.startsWith('cla:')?'claude':model.startsWith('cf:')?'cloudflare':model.startsWith('or:')?'openrouter':USER.provider,apiKey=provider==='openai'?USER.apiKeyOpenAI:provider==='google'?USER.apiKeyGoogle:provider==='claude'?USER.apiKeyClaude:provider==='cloudflare'?USER.apiKeyCloudflare:USER.apiKeyOpenRouter;if(!apiKey){onDelta(localDemoReply(),true);return {ok:true,rid:streamId||null}}const r={rid:streamId||gid(),seq:-1,done:false,signaled:false,ws:null};await cacheStore.setItem(r.rid,'busy');const signal=t=>{if(!r.signaled){r.signaled=true;onDelta(t||'',true)}};const ws=new WebSocket(HTTP_BASE.replace('https','wss')+'?uid='+encodeURIComponent(r.rid));r.ws=ws;ws.onopen=()=>ws.send(JSON.stringify({type:'begin',rid:r.rid,provider,apiKey,or_body:buildBody()}));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'||m.type==='err'){r.done=true;cacheStore.setItem(r.rid,'done');signal(m.type==='err'?'\n\n'+(m.message||'error'):'');ws.close()}};ws.onclose=()=>{};ws.onerror=()=>{};state.controller={abort:()=>{r.done=true;cacheStore.setItem(r.rid,'done');try{if(ws.readyState===1)ws.send(JSON.stringify({type:'stop',rid:r.rid}))}catch{};signal('')},disconnect:()=>ws.close()};return {ok:true,rid:r.rid}}
|
||||
const accountTabs={General:['accountTabGeneral','accountPanelGeneral'],API:['accountTabAPI','accountPanelAPI'],User:['accountTabUser','accountPanelUser']};function showAccountTab(key){Object.entries(accountTabs).forEach(([k,[tb,pn]])=>{el[tb].classList.toggle('border-black',k===key);el[pn].classList.toggle('hidden',k!==key)})}
|
||||
function openAccountSettings(){el.set_provider.value=USER.provider||'openrouter';el.set_api_key_or.value=USER.apiKeyOpenRouter||'';el.set_api_key_oai.value=USER.apiKeyOpenAI||'';el.set_api_key_g.value=USER.apiKeyGoogle||'';el.set_api_key_claude.value=USER.apiKeyClaude||'';el.set_api_key_cf.value=USER.apiKeyCloudflare||'';el.set_master_prompt.value=USER.masterPrompt||'';el.set_title_model.value=USER.titleModel;el.set_gh_token.value=USER.githubToken||'';const sa=USER.gcpSA;el.gcpSAUploadBtn.textContent=sa&&sa.project_id?`Uploaded: ${sa.project_id}`:'Upload .json';el.set_user_name.value=USER.name;el.userAvatarPreview.src=USER.avatar||'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';el.userAvatarPreview.classList.toggle('bg-gray-200',!USER.avatar);el.set_donor.checked=USER.donor;el.set_donor.disabled=true;showAccountTab('General');el.accountSettingsModal.classList.remove('hidden')}
|
||||
function closeAccountSettings(){el.accountSettingsModal.classList.add('hidden')}
|
||||
@@ -138,3 +138,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,ensureThreadOnFirstUser,generateTitleWithAI,threadRow,renderThreads,hideThreadPopover,showThreadPopover,hideSunePopover,showSunePopover,updateAttachBadge,toAttach,ensureJars,openSettings,closeSettings,showTab,dl,ts,kbUpdate,kbBind,activeMeta,init,showHtmlTab,buildBody,askOpenRouterStreaming,showAccountTab,openAccountSettings,closeAccountSettings,getBubbleById,syncActiveThread,syncWhileBusy,onForeground,getActiveHtmlParts,imgToWebp});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user