Rename the claude provider to anthropic with an ant: prefix

The provider is Anthropic; Claude is its model family. Models now take
the ant: prefix (e.g. ant:claude-opus-5-5), the provider value sent to
the proxy is "anthropic", and the key lives in anthropic_api_key /
USER.apiKeyAnthropic. No fallback for the old names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 14:29:35 +00:00
co-authored by Claude Opus 5.5
parent 601336e9bd
commit 1ea7b9b5c7
5 changed files with 19 additions and 19 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ export const el = window.el = Object.fromEntries(
'attachBtn','attachBadge','attachPopover','fileInput','htmlEditor','extensionHtmlEditor', 'attachBtn','attachBadge','attachPopover','fileInput','htmlEditor','extensionHtmlEditor',
'htmlTab_index','htmlTab_extension','suneHtml','accountSettingsModal','accountSettingsForm', 'htmlTab_index','htmlTab_extension','suneHtml','accountSettingsModal','accountSettingsForm',
'closeAccountSettings','cancelAccountSettings','set_master_prompt','set_provider', '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', 'set_api_key_custom1','set_title_model','copySystemPrompt','pasteSystemPrompt','copyHTML','pasteHTML',
'accountTabGeneral','accountTabAPI','accountPanelGeneral','accountPanelAPI','set_gh_token', 'accountTabGeneral','accountTabAPI','accountPanelGeneral','accountPanelAPI','set_gh_token',
'importAccountSettings','exportAccountSettings', 'importAccountSettings','exportAccountSettings',
+5 -5
View File
@@ -18,7 +18,7 @@ const DEFAULT_MODEL='openrouter/free'
const icons=()=>window.lucide&&lucide.createIcons() const icons=()=>window.lucide&&lucide.createIcons()
const haptic=()=>/android/i.test(navigator.userAgent)&&navigator.vibrate?.(1) 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 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)})} 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() 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)})} 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')} function closeAccountSettings(){el.accountSettingsModal.classList.add('hidden')}
$(el.accountSettingsOption).on('click',()=>{el.userMenu.classList.add('hidden');openAccountSettings()}) $(el.accountSettingsOption).on('click',()=>{el.userMenu.classList.add('hidden');openAccountSettings()})
$(el.closeAccountSettings).on('click',closeAccountSettings) $(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.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.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.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.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.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.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)}"]`) 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.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{}}) $(el.pasteSystemPrompt).on('click',async()=>{try{el.set_system_prompt.value=await navigator.clipboard.readText()}catch{}})
+4 -4
View File
@@ -99,13 +99,13 @@
<div class="px-4 py-3 border-b text-sm font-semibold flex items-center justify-between"><span>Model name</span><button type="button" onclick="modelHelp.close()" class="p-1 rounded hover:bg-gray-100" aria-label="Close"><svg viewBox="0 0 24 24" class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg></button></div> <div class="px-4 py-3 border-b text-sm font-semibold flex items-center justify-between"><span>Model name</span><button type="button" onclick="modelHelp.close()" class="p-1 rounded hover:bg-gray-100" aria-label="Close"><svg viewBox="0 0 24 24" class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg></button></div>
<div class="p-4 space-y-3 text-sm text-gray-700 [&_code]:text-xs [&_code]:bg-gray-100 [&_code]:rounded [&_code]:px-1 [&_code]:py-0.5 [&_a]:underline [&_a]:text-gray-900"> <div class="p-4 space-y-3 text-sm text-gray-700 [&_code]:text-xs [&_code]:bg-gray-100 [&_code]:rounded [&_code]:px-1 [&_code]:py-0.5 [&_a]:underline [&_a]:text-gray-900">
<p>The <b>Provider</b> selected in <b>Account Settings → General</b> is used, unless the model name starts with one of these prefixes:</p> <p>The <b>Provider</b> selected in <b>Account Settings → General</b> is used, unless the model name starts with one of these prefixes:</p>
<div class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1"><code>or:</code><span>OpenRouter</span><code>oai:</code><span>OpenAI</span><code>g:</code><span>Google</span><code>cla:</code><span>Claude</span></div> <div class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1"><code>or:</code><span>OpenRouter</span><code>oai:</code><span>OpenAI</span><code>g:</code><span>Google</span><code>ant:</code><span>Anthropic</span></div>
<p>After the prefix, write the model slug exactly the way that provider's API expects it:</p> <p>After the prefix, write the model slug exactly the way that provider's API expects it:</p>
<div class="space-y-2"> <div class="space-y-2">
<div><div class="font-medium">OpenRouter <span class="font-normal text-gray-400">· vendor/model</span></div><code>or:anthropic/claude-opus-5.5</code><div class="text-xs text-gray-500 mt-0.5">Browse slugs at <a href="https://openrouter.ai/models" target="_blank" rel="noopener">openrouter.ai/models</a></div></div> <div><div class="font-medium">OpenRouter <span class="font-normal text-gray-400">· vendor/model</span></div><code>or:anthropic/claude-opus-5.5</code><div class="text-xs text-gray-500 mt-0.5">Browse slugs at <a href="https://openrouter.ai/models" target="_blank" rel="noopener">openrouter.ai/models</a></div></div>
<div><div class="font-medium">OpenAI</div><code>oai:gpt-6-sol</code><div class="text-xs text-gray-500 mt-0.5"><a href="https://platform.openai.com/docs/models" target="_blank" rel="noopener">OpenAI models</a></div></div> <div><div class="font-medium">OpenAI</div><code>oai:gpt-6-sol</code><div class="text-xs text-gray-500 mt-0.5"><a href="https://platform.openai.com/docs/models" target="_blank" rel="noopener">OpenAI models</a></div></div>
<div><div class="font-medium">Google</div><code>g:gemini-3.8-flash</code><div class="text-xs text-gray-500 mt-0.5"><a href="https://ai.google.dev/gemini-api/docs/models" target="_blank" rel="noopener">Gemini models</a></div></div> <div><div class="font-medium">Google</div><code>g:gemini-3.8-flash</code><div class="text-xs text-gray-500 mt-0.5"><a href="https://ai.google.dev/gemini-api/docs/models" target="_blank" rel="noopener">Gemini models</a></div></div>
<div><div class="font-medium">Claude</div><code>cla:claude-opus-5-5</code><div class="text-xs text-gray-500 mt-0.5"><a href="https://docs.anthropic.com/en/docs/about-claude/models" target="_blank" rel="noopener">Claude models</a></div></div> <div><div class="font-medium">Anthropic</div><code>ant:claude-opus-5-5</code><div class="text-xs text-gray-500 mt-0.5"><a href="https://docs.anthropic.com/en/docs/about-claude/models" target="_blank" rel="noopener">Claude models</a></div></div>
</div> </div>
<p class="text-xs text-gray-500">Tip: add <code>:online</code> to the end to enable web search, e.g. <code>g:gemini-3.8-flash:online</code>.</p> <p class="text-xs text-gray-500">Tip: add <code>:online</code> to the end to enable web search, e.g. <code>g:gemini-3.8-flash:online</code>.</p>
</div> </div>
@@ -118,11 +118,11 @@
<form id="accountSettingsForm" class="flex flex-col min-h-0 text-sm"> <form id="accountSettingsForm" class="flex flex-col min-h-0 text-sm">
<div class="border-b flex text-xs font-medium"><button type="button" id="accountTabGeneral" class="flex-1 py-2 px-3 text-center border-b-2 border-black">General</button><button type="button" id="accountTabAPI" class="flex-1 py-2 px-3 text-center border-b-2 border-transparent hover:border-gray-300">API</button><button type="button" id="accountTabUser" class="flex-1 py-2 px-3 text-center border-b-2 border-transparent hover:border-gray-300">User</button></div> <div class="border-b flex text-xs font-medium"><button type="button" id="accountTabGeneral" class="flex-1 py-2 px-3 text-center border-b-2 border-black">General</button><button type="button" id="accountTabAPI" class="flex-1 py-2 px-3 text-center border-b-2 border-transparent hover:border-gray-300">API</button><button type="button" id="accountTabUser" class="flex-1 py-2 px-3 text-center border-b-2 border-transparent hover:border-gray-300">User</button></div>
<div id="accountPanelGeneral" class="p-4 space-y-4 overflow-y-auto"> <div id="accountPanelGeneral" class="p-4 space-y-4 overflow-y-auto">
<div><label class="block text-gray-700 font-medium mb-1">Provider</label><select id="set_provider" class="w-full rounded-xl border border-gray-300 px-3 py-2"><option value="openrouter">OpenRouter</option><option value="openai">OpenAI</option><option value="google">Google</option><option value="claude">Claude</option></select><p class="mt-1 text-xs text-gray-500">Or you can prefix model names with or:, oai:, g:, or cla: to override.</p></div> <div><label class="block text-gray-700 font-medium mb-1">Provider</label><select id="set_provider" class="w-full rounded-xl border border-gray-300 px-3 py-2"><option value="openrouter">OpenRouter</option><option value="openai">OpenAI</option><option value="google">Google</option><option value="anthropic">Anthropic</option></select><p class="mt-1 text-xs text-gray-500">Or you can prefix model names with or:, oai:, g:, or ant: to override.</p></div>
<div><label class="block text-gray-700 font-medium mb-1">Master Prompt</label><textarea id="set_master_prompt" rows="6" class="w-full rounded-xl border border-gray-300 px-3 py-2" placeholder="Applies to all sunes on this device"></textarea><p class="mt-1 text-xs text-gray-500">Stored locally.</p></div> <div><label class="block text-gray-700 font-medium mb-1">Master Prompt</label><textarea id="set_master_prompt" rows="6" class="w-full rounded-xl border border-gray-300 px-3 py-2" placeholder="Applies to all sunes on this device"></textarea><p class="mt-1 text-xs text-gray-500">Stored locally.</p></div>
<div><label class="block text-gray-700 font-medium mb-1">Model preference for titles</label><input id="set_title_model" type="text" class="w-full rounded-xl border border-gray-300 px-3 py-2" placeholder="or:google/gemma-3-12b-it"/><p class="mt-1 text-xs text-gray-500">Used for auto-generating thread titles.</p></div> <div><label class="block text-gray-700 font-medium mb-1">Model preference for titles</label><input id="set_title_model" type="text" class="w-full rounded-xl border border-gray-300 px-3 py-2" placeholder="or:google/gemma-3-12b-it"/><p class="mt-1 text-xs text-gray-500">Used for auto-generating thread titles.</p></div>
</div> </div>
<div id="accountPanelAPI" class="p-4 overflow-y-auto hidden"><div class="grid grid-cols-2 gap-x-4 gap-y-4"><div><label class="block text-gray-700 font-medium mb-1">OpenRouter Key</label><div class="relative"><input id="set_api_key_or" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="sk-or-..."><button type="button" data-reveal-for="set_api_key_or" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Use: <code>USER.apiKeyOpenRouter</code></p></div><div><label class="block text-gray-700 font-medium mb-1">OpenAI Key</label><div class="relative"><input id="set_api_key_oai" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="sk-..."><button type="button" data-reveal-for="set_api_key_oai" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Use: <code>USER.apiKeyOpenAI</code></p></div><div><label class="block text-gray-700 font-medium mb-1">Google Key</label><div class="relative"><input id="set_api_key_g" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="AIza..."><button type="button" data-reveal-for="set_api_key_g" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Gemini/Studio. Use: <code>USER.apiKeyGoogle</code></p></div><div><label class="block text-gray-700 font-medium mb-1">Claude Key</label><div class="relative"><input id="set_api_key_claude" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="sk-ant-..."><button type="button" data-reveal-for="set_api_key_claude" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Use: <code>USER.apiKeyClaude</code></p></div><div><label class="block text-gray-700 font-medium mb-1">Cloudflare Token</label><div class="relative"><input id="set_api_key_cf" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="..."><button type="button" data-reveal-for="set_api_key_cf" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Not used. Use: <code>USER.apiKeyCloudflare</code></p></div><div><label class="block text-gray-700 font-medium mb-1">Custom Key 1</label><div class="relative"><input id="set_api_key_custom1" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="..."><button type="button" data-reveal-for="set_api_key_custom1" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Use: <code>USER.customKey1</code></p></div><div><label class="block text-gray-700 font-medium mb-1">Github Token</label><div class="relative"><input id="set_gh_token" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="ghp_..."><button type="button" data-reveal-for="set_gh_token" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Use: <code>USER.githubToken</code></p></div></div></div> <div id="accountPanelAPI" class="p-4 overflow-y-auto hidden"><div class="grid grid-cols-2 gap-x-4 gap-y-4"><div><label class="block text-gray-700 font-medium mb-1">OpenRouter Key</label><div class="relative"><input id="set_api_key_or" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="sk-or-..."><button type="button" data-reveal-for="set_api_key_or" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Use: <code>USER.apiKeyOpenRouter</code></p></div><div><label class="block text-gray-700 font-medium mb-1">OpenAI Key</label><div class="relative"><input id="set_api_key_oai" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="sk-..."><button type="button" data-reveal-for="set_api_key_oai" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Use: <code>USER.apiKeyOpenAI</code></p></div><div><label class="block text-gray-700 font-medium mb-1">Google Key</label><div class="relative"><input id="set_api_key_g" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="AIza..."><button type="button" data-reveal-for="set_api_key_g" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Gemini/Studio. Use: <code>USER.apiKeyGoogle</code></p></div><div><label class="block text-gray-700 font-medium mb-1">Anthropic Key</label><div class="relative"><input id="set_api_key_ant" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="sk-ant-..."><button type="button" data-reveal-for="set_api_key_ant" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Use: <code>USER.apiKeyAnthropic</code></p></div><div><label class="block text-gray-700 font-medium mb-1">Cloudflare Token</label><div class="relative"><input id="set_api_key_cf" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="..."><button type="button" data-reveal-for="set_api_key_cf" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Not used. Use: <code>USER.apiKeyCloudflare</code></p></div><div><label class="block text-gray-700 font-medium mb-1">Custom Key 1</label><div class="relative"><input id="set_api_key_custom1" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="..."><button type="button" data-reveal-for="set_api_key_custom1" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Use: <code>USER.customKey1</code></p></div><div><label class="block text-gray-700 font-medium mb-1">Github Token</label><div class="relative"><input id="set_gh_token" type="password" class="w-full rounded-xl border border-gray-300 px-3 py-2 pr-10" placeholder="ghp_..."><button type="button" data-reveal-for="set_gh_token" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-400 hover:text-gray-600"><i data-lucide="eye" class="h-4 w-4"></i></button></div><p class="mt-1 text-xs text-gray-500">Use: <code>USER.githubToken</code></p></div></div></div>
<div id="accountPanelUser" class="p-4 space-y-4 overflow-y-auto hidden"> <div id="accountPanelUser" class="p-4 space-y-4 overflow-y-auto hidden">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="relative"><img id="userAvatarPreview" class="h-16 w-16 rounded-full object-cover bg-gray-200"><button type="button" id="setUserAvatarBtn" class="absolute bottom-0 right-0 h-6 w-6 rounded-full bg-white border border-gray-300 flex items-center justify-center hover:bg-gray-100" aria-label="Edit photo"><i data-lucide="edit-3" class="h-3 w-3"></i></button></div> <div class="relative"><img id="userAvatarPreview" class="h-16 w-16 rounded-full object-cover bg-gray-200"><button type="button" id="setUserAvatarBtn" class="absolute bottom-0 right-0 h-6 w-6 rounded-full bg-white border border-gray-300 flex items-center justify-center hover:bg-gray-100" aria-label="Edit photo"><i data-lucide="edit-3" class="h-3 w-3"></i></button></div>
+4 -4
View File
@@ -15,7 +15,7 @@ export const buildBody=()=>{
msgs.push({role:'system', content: mPrompt}); msgs.push({role:'system', content: mPrompt});
} }
const modelName=SUNE.model.replace(/^(or:|oai:|g:|cla:)/,'').replace(/^[^/]*\//,'').replace(/(?::online|:free)+$/,''); const modelName=SUNE.model.replace(/^(or:|oai:|g:|ant:)/,'').replace(/^[^/]*\//,'').replace(/(?::online|:free)+$/,'');
const sPrompt = (SUNE.system_prompt || '').trim().replaceAll('{{model_name}}',()=>modelName); const sPrompt = (SUNE.system_prompt || '').trim().replaceAll('{{model_name}}',()=>modelName);
if(sPrompt) { if(sPrompt) {
msgs.push({role:'system', content: sPrompt}); msgs.push({role:'system', content: sPrompt});
@@ -43,7 +43,7 @@ export const buildBody=()=>{
} }
} }
const b=payloadWithSampling({model:SUNE.model.replace(/^(or:|oai:|g:|cla:)/,''),messages:msgs,stream:true}); const b=payloadWithSampling({model:SUNE.model.replace(/^(or:|oai:|g:|ant:)/,''),messages:msgs,stream:true});
b.reasoning={...(SUNE.reasoning_effort&&SUNE.reasoning_effort!=='default'?{effort:SUNE.reasoning_effort}:{}),exclude:!SUNE.include_thoughts}; b.reasoning={...(SUNE.reasoning_effort&&SUNE.reasoning_effort!=='default'?{effort:SUNE.reasoning_effort}:{}),exclude:!SUNE.include_thoughts};
if(SUNE.img_output){b.modalities=['image'];b.image_config={aspect_ratio:SUNE.aspect_ratio||'1:1',image_size:SUNE.image_size||'1K'}} if(SUNE.img_output){b.modalities=['image'];b.image_config={aspect_ratio:SUNE.aspect_ratio||'1:1',image_size:SUNE.image_size||'1K'}}
return b return b
@@ -161,8 +161,8 @@ function track(msg,tid,key){
export function startReply(msg){ export function startReply(msg){
const {USER,SUNE,state}=window; const {USER,SUNE,state}=window;
const model=SUNE.model,provider=model.startsWith('oai:')?'openai':model.startsWith('g:')?'google':model.startsWith('cla:')?'claude':model.startsWith('or:')?'openrouter':USER.provider; const model=SUNE.model,provider=model.startsWith('oai:')?'openai':model.startsWith('g:')?'google':model.startsWith('ant:')?'anthropic':model.startsWith('or:')?'openrouter':USER.provider;
const apiKey=provider==='openai'?USER.apiKeyOpenAI:provider==='google'?USER.apiKeyGoogle:provider==='claude'?USER.apiKeyClaude:USER.apiKeyOpenRouter; const apiKey=provider==='openai'?USER.apiKeyOpenAI:provider==='google'?USER.apiKeyGoogle:provider==='anthropic'?USER.apiKeyAnthropic:USER.apiKeyOpenRouter;
const s=track(msg,state.currentThreadId,threadKey(state.currentThreadId)); const s=track(msg,state.currentThreadId,threadKey(state.currentThreadId));
if(!apiKey){msg.content[0].text=window.localDemoReply();return finish(s)} if(!apiKey){msg.content[0].text=window.localDemoReply();return finish(s)}
s.req=JSON.stringify({type:'begin',rid:s.rid,provider,apiKey,body:buildBody()}); s.req=JSON.stringify({type:'begin',rid:s.rid,provider,apiKey,body:buildBody()});
+5 -5
View File
@@ -5,26 +5,26 @@ export const USER = {
get avatar() { return localStorage.getItem('user_avatar') || ''; }, get avatar() { return localStorage.getItem('user_avatar') || ''; },
set avatar(v) { localStorage.setItem('user_avatar', v || ''); }, set avatar(v) { localStorage.setItem('user_avatar', v || ''); },
get provider() { return localStorage.getItem('provider') || 'openrouter'; }, get provider() { return localStorage.getItem('provider') || 'openrouter'; },
set provider(v) { localStorage.setItem('provider', ['openai', 'google', 'claude'].includes(v) ? v : 'openrouter'); }, set provider(v) { localStorage.setItem('provider', ['openai', 'google', 'anthropic'].includes(v) ? v : 'openrouter'); },
get apiKeyOpenRouter() { return localStorage.getItem('openrouter_api_key') || ''; }, get apiKeyOpenRouter() { return localStorage.getItem('openrouter_api_key') || ''; },
set apiKeyOpenRouter(v) { localStorage.setItem('openrouter_api_key', v || ''); }, set apiKeyOpenRouter(v) { localStorage.setItem('openrouter_api_key', v || ''); },
get apiKeyOpenAI() { return localStorage.getItem('openai_api_key') || ''; }, get apiKeyOpenAI() { return localStorage.getItem('openai_api_key') || ''; },
set apiKeyOpenAI(v) { localStorage.setItem('openai_api_key', v || ''); }, set apiKeyOpenAI(v) { localStorage.setItem('openai_api_key', v || ''); },
get apiKeyGoogle() { return localStorage.getItem('google_api_key') || ''; }, get apiKeyGoogle() { return localStorage.getItem('google_api_key') || ''; },
set apiKeyGoogle(v) { localStorage.setItem('google_api_key', v || ''); }, set apiKeyGoogle(v) { localStorage.setItem('google_api_key', v || ''); },
get apiKeyClaude() { return localStorage.getItem('claude_api_key') || ''; }, get apiKeyAnthropic() { return localStorage.getItem('anthropic_api_key') || ''; },
set apiKeyClaude(v) { localStorage.setItem('claude_api_key', v || ''); }, set apiKeyAnthropic(v) { localStorage.setItem('anthropic_api_key', v || ''); },
get apiKeyCloudflare() { return localStorage.getItem('cloudflare_api_key') || ''; }, get apiKeyCloudflare() { return localStorage.getItem('cloudflare_api_key') || ''; },
set apiKeyCloudflare(v) { localStorage.setItem('cloudflare_api_key', v || ''); }, set apiKeyCloudflare(v) { localStorage.setItem('cloudflare_api_key', v || ''); },
get apiKey() { get apiKey() {
const p = this.provider; const p = this.provider;
return p === 'openai' ? this.apiKeyOpenAI : p === 'google' ? this.apiKeyGoogle : p === 'claude' ? this.apiKeyClaude : this.apiKeyOpenRouter; return p === 'openai' ? this.apiKeyOpenAI : p === 'google' ? this.apiKeyGoogle : p === 'anthropic' ? this.apiKeyAnthropic : this.apiKeyOpenRouter;
}, },
set apiKey(v) { set apiKey(v) {
const p = this.provider; const p = this.provider;
if (p === 'openai') this.apiKeyOpenAI = v; if (p === 'openai') this.apiKeyOpenAI = v;
else if (p === 'google') this.apiKeyGoogle = v; else if (p === 'google') this.apiKeyGoogle = v;
else if (p === 'claude') this.apiKeyClaude = v; else if (p === 'anthropic') this.apiKeyAnthropic = v;
else this.apiKeyOpenRouter = v; else this.apiKeyOpenRouter = v;
}, },
get masterPrompt() { return localStorage.getItem('master_prompt') || 'Always respond using markdown.'; }, get masterPrompt() { return localStorage.getItem('master_prompt') || 'Always respond using markdown.'; },