diff --git a/src/dom.js b/src/dom.js index cd5d3a9..78490e4 100644 --- a/src/dom.js +++ b/src/dom.js @@ -13,7 +13,7 @@ export const el = window.el = Object.fromEntries( 'attachBtn','attachBadge','attachPopover','fileInput','htmlEditor','extensionHtmlEditor', '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_api_key_or','set_api_key_oai','set_api_key_g','set_api_key_ant','set_api_key_cf', 'set_api_key_custom1','set_title_model','copySystemPrompt','pasteSystemPrompt','copyHTML','pasteHTML', 'accountTabGeneral','accountTabAPI','accountPanelGeneral','accountPanelAPI','set_gh_token', 'importAccountSettings','exportAccountSettings', diff --git a/src/main.js b/src/main.js index 32b7cf0..063c265 100644 --- a/src/main.js +++ b/src/main.js @@ -18,7 +18,7 @@ const DEFAULT_MODEL='openrouter/free' const icons=()=>window.lucide&&lucide.createIcons() const haptic=()=>/android/i.test(navigator.userAgent)&&navigator.vibrate?.(1) -const SYSTEM_KEYS=new Set(['sunes_v1','active_sune_id','thread_repo_url','sune_repo_url','sunes_updated_at','user_name','user_avatar','provider','openrouter_api_key','openai_api_key','google_api_key','claude_api_key','cloudflare_api_key','master_prompt','title_model','gh_token','custom_key_1']) +const SYSTEM_KEYS=new Set(['sunes_v1','active_sune_id','thread_repo_url','sune_repo_url','sunes_updated_at','user_name','user_avatar','provider','openrouter_api_key','openai_api_key','google_api_key','anthropic_api_key','cloudflare_api_key','master_prompt','title_model','gh_token','custom_key_1']) const gcStorage=()=>{try{const alive=new Set(sunes.map(s=>s.id));Object.keys(localStorage).forEach(k=>{if(SYSTEM_KEYS.has(k)||/^(t_|rem_t_|rem_index_|localforage|threads_)/.test(k))return;const m=k.match(/^sune_([^_]+)_/);if(m&&alive.has(m[1]))return;localStorage.removeItem(k)})}catch{}} const cleanSuneStorage=id=>{if(!id)return;const p=`sune_${id}_`;Object.keys(localStorage).forEach(k=>{if(k.startsWith(p))localStorage.removeItem(k)})} @@ -366,7 +366,7 @@ $(el.sidebarBtnLeft).on('click',()=>{ }); init() 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_api_key_custom1.value=USER.customKey1||'';el.set_api_key_custom1.value=USER.customKey1||'';el.set_master_prompt.value=USER.masterPrompt||'';el.set_title_model.value=USER.titleModel;el.set_gh_token.value=USER.githubToken||'';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);showAccountTab('General');el.accountSettingsModal.classList.remove('hidden')} +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_ant.value=USER.apiKeyAnthropic||'';el.set_api_key_cf.value=USER.apiKeyCloudflare||'';el.set_api_key_custom1.value=USER.customKey1||'';el.set_api_key_custom1.value=USER.customKey1||'';el.set_master_prompt.value=USER.masterPrompt||'';el.set_title_model.value=USER.titleModel;el.set_gh_token.value=USER.githubToken||'';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);showAccountTab('General');el.accountSettingsModal.classList.remove('hidden')} function closeAccountSettings(){el.accountSettingsModal.classList.add('hidden')} $(el.accountSettingsOption).on('click',()=>{el.userMenu.classList.add('hidden');openAccountSettings()}) $(el.closeAccountSettings).on('click',closeAccountSettings) @@ -374,12 +374,12 @@ $(el.cancelAccountSettings).on('click',closeAccountSettings) $(el.accountSettingsModal).on('click',e=>{if(e.target===el.accountSettingsModal||e.target.classList.contains('bg-black/30'))closeAccountSettings()}) $(el.setUserAvatarBtn).on('click',()=>el.userAvatarInput.click()); $(el.userAvatarInput).on('change',async()=>{const f=el.userAvatarInput.files?.[0];if(!f)return;try{const v=await imgToWebp(f);USER.avatar=v;el.userAvatarPreview.src=v;el.userAvatarPreview.classList.remove('bg-gray-200');renderUserUI()}catch{}}); -$(el.accountSettingsForm).on('submit',e=>{e.preventDefault();USER.provider=el.set_provider.value||'openrouter';USER.apiKeyOpenRouter=String(el.set_api_key_or.value||'').trim();USER.apiKeyOpenAI=String(el.set_api_key_oai.value||'').trim();USER.apiKeyGoogle=String(el.set_api_key_g.value||'').trim();USER.apiKeyClaude=String(el.set_api_key_claude.value||'').trim();USER.apiKeyCloudflare=String(el.set_api_key_cf.value||'').trim();USER.customKey1=String(el.set_api_key_custom1.value||'').trim();USER.masterPrompt=String(el.set_master_prompt.value||'').trim();USER.titleModel=String(el.set_title_model.value||'').trim();USER.githubToken=String(el.set_gh_token.value||'').trim();USER.name=String(el.set_user_name.value||'').trim();renderUserUI();closeAccountSettings()}) +$(el.accountSettingsForm).on('submit',e=>{e.preventDefault();USER.provider=el.set_provider.value||'openrouter';USER.apiKeyOpenRouter=String(el.set_api_key_or.value||'').trim();USER.apiKeyOpenAI=String(el.set_api_key_oai.value||'').trim();USER.apiKeyGoogle=String(el.set_api_key_g.value||'').trim();USER.apiKeyAnthropic=String(el.set_api_key_ant.value||'').trim();USER.apiKeyCloudflare=String(el.set_api_key_cf.value||'').trim();USER.customKey1=String(el.set_api_key_custom1.value||'').trim();USER.masterPrompt=String(el.set_master_prompt.value||'').trim();USER.titleModel=String(el.set_title_model.value||'').trim();USER.githubToken=String(el.set_gh_token.value||'').trim();USER.name=String(el.set_user_name.value||'').trim();renderUserUI();closeAccountSettings()}) $(el.accountPanelAPI).on('click',e=>{const b=e.target.closest('[data-reveal-for]');if(!b)return;const i=document.getElementById(b.dataset.revealFor);if(!i)return;const p=i.type==='password';i.type=p?'text':'password';b.querySelector('i').setAttribute('data-lucide',p?'eye-off':'eye');lucide.createIcons()}); el.accountTabGeneral.onclick=()=>showAccountTab('General');el.accountTabAPI.onclick=()=>showAccountTab('API');el.accountTabUser.onclick=()=>showAccountTab('User') -el.exportAccountSettings.onclick=()=>dl(`sune-account-${ts()}.json`,{v:1,provider:USER.provider,apiKeyOpenRouter:USER.apiKeyOpenRouter,apiKeyOpenAI:USER.apiKeyOpenAI,apiKeyGoogle:USER.apiKeyGoogle,apiKeyClaude:USER.apiKeyClaude,apiKeyCloudflare:USER.apiKeyCloudflare,customKey1:USER.customKey1,masterPrompt:USER.masterPrompt,titleModel:USER.titleModel,githubToken:USER.githubToken,userName:USER.name,userAvatar:USER.avatar}); +el.exportAccountSettings.onclick=()=>dl(`sune-account-${ts()}.json`,{v:1,provider:USER.provider,apiKeyOpenRouter:USER.apiKeyOpenRouter,apiKeyOpenAI:USER.apiKeyOpenAI,apiKeyGoogle:USER.apiKeyGoogle,apiKeyAnthropic:USER.apiKeyAnthropic,apiKeyCloudflare:USER.apiKeyCloudflare,customKey1:USER.customKey1,masterPrompt:USER.masterPrompt,titleModel:USER.titleModel,githubToken:USER.githubToken,userName:USER.name,userAvatar:USER.avatar}); el.importAccountSettings.onclick=()=>{el.importAccountSettingsInput.value='';el.importAccountSettingsInput.click()}; -el.importAccountSettingsInput.onchange=async e=>{const f=e.target.files?.[0];if(!f)return;try{const d=JSON.parse(await f.text());if(!d||typeof d!=='object')throw new Error('Invalid');const m={provider:'provider',apiKeyOpenRouter:'apiKeyOR',apiKeyOpenAI:'apiKeyOAI',apiKeyGoogle:'apiKeyG',apiKeyClaude:'apiKeyClaude',apiKeyCloudflare:'apiKeyCloudflare',customKey1:'customKey1',masterPrompt:'masterPrompt',titleModel:'titleModel',githubToken:'ghToken',name:'userName',avatar:'userAvatar'};Object.entries(m).forEach(([p,k])=>{const v=d[p]??d[k];if(typeof v==='string')USER[p]=v});renderUserUI();openAccountSettings();alert('Imported.')}catch{alert('Import failed')}}; +el.importAccountSettingsInput.onchange=async e=>{const f=e.target.files?.[0];if(!f)return;try{const d=JSON.parse(await f.text());if(!d||typeof d!=='object')throw new Error('Invalid');const m={provider:'provider',apiKeyOpenRouter:'apiKeyOR',apiKeyOpenAI:'apiKeyOAI',apiKeyGoogle:'apiKeyG',apiKeyAnthropic:'apiKeyAnthropic',apiKeyCloudflare:'apiKeyCloudflare',customKey1:'customKey1',masterPrompt:'masterPrompt',titleModel:'titleModel',githubToken:'ghToken',name:'userName',avatar:'userAvatar'};Object.entries(m).forEach(([p,k])=>{const v=d[p]??d[k];if(typeof v==='string')USER[p]=v});renderUserUI();openAccountSettings();alert('Imported.')}catch{alert('Import failed')}}; const getBubbleById=id=>el.messages.querySelector(`.msg-bubble[data-mid="${CSS.escape(id)}"]`) $(el.copySystemPrompt).on('click',async()=>await copyToClipboard(el.set_system_prompt.value||'')) $(el.pasteSystemPrompt).on('click',async()=>{try{el.set_system_prompt.value=await navigator.clipboard.readText()}catch{}}) diff --git a/src/parts/modals.html b/src/parts/modals.html index dd70800..62948d9 100644 --- a/src/parts/modals.html +++ b/src/parts/modals.html @@ -99,13 +99,13 @@
Model name

The Provider selected in Account Settings → General is used, unless the model name starts with one of these prefixes:

-
or:OpenRouteroai:OpenAIg:Googlecla:Claude
+
or:OpenRouteroai:OpenAIg:Googleant:Anthropic

After the prefix, write the model slug exactly the way that provider's API expects it:

OpenRouter · vendor/model
or:anthropic/claude-opus-5.5
Browse slugs at openrouter.ai/models
OpenAI
oai:gpt-6-sol
Google
g:gemini-3.8-flash
-
Claude
cla:claude-opus-5-5
+
Anthropic
ant:claude-opus-5-5

Tip: add :online to the end to enable web search, e.g. g:gemini-3.8-flash:online.

@@ -118,11 +118,11 @@
-

Or you can prefix model names with or:, oai:, g:, or cla: to override.

+

Or you can prefix model names with or:, oai:, g:, or ant: to override.

Stored locally.

Used for auto-generating thread titles.

- +