diff --git a/src/components/ConfigExporter.jsx b/src/components/ConfigExporter.jsx index 6a588a2..329a9d3 100644 --- a/src/components/ConfigExporter.jsx +++ b/src/components/ConfigExporter.jsx @@ -79,7 +79,7 @@ function ThemedSelect({
{open && ( -
-
+
+
{options.length > 0 ? ( options.map((option) => { const selected = option.value === value; @@ -265,31 +265,36 @@ const ConfigExporter = ({ tokens = [] }) => { }; }, [selectedToken?.id]); - const getProviderInfo = (modelName) => { + const getModelConnectionPreset = (modelName = '') => { const lower = modelName.toLowerCase(); if (lower.includes('claude')) { - return { provider: 'anthropic', api: 'anthropic-messages' }; + return { + family: 'anthropic', + baseUrl: serverAddress, + openclawApi: 'anthropic-messages', + openclawProviderId: 'subrouter-anthropic', + opencodeProviderId: 'anthropic', + }; } - if ( - lower.includes('gpt') || - lower.includes('o1') || - lower.includes('o3') || - lower.includes('o4') - ) { - return { provider: 'openai', api: 'openai-chat' }; - } - if (lower.includes('gemini')) { - return { provider: 'google', api: 'google-chat' }; - } - return { provider: 'openai', api: 'openai-chat' }; + return { + family: 'openai', + baseUrl: `${serverAddress}/v1`, + openclawApi: 'openai-completions', + openclawProviderId: 'subrouter-openai', + opencodeProviderId: 'openai', + }; }; const getCCSwitchEndpoint = () => { const app = CCSWITCH_APPS.find( (item) => item.id === selectedCCSwitchApp, ); - if (app?.endpointType === 'anthropic' || app?.endpointType === 'openclaw') { - return `${serverAddress}/`; + const preset = getModelConnectionPreset(selectedModel); + if (app?.endpointType === 'anthropic') { + return serverAddress; + } + if (app?.id === 'openclaw' || app?.id === 'opencode') { + return preset.baseUrl; } return `${serverAddress}/v1`; }; @@ -320,31 +325,55 @@ const ConfigExporter = ({ tokens = [] }) => { return `{ "env": { "ANTHROPIC_API_KEY": "${apiKey}", - "ANTHROPIC_BASE_URL": "${serverAddress}/", + "ANTHROPIC_BASE_URL": "${serverAddress}", "ANTHROPIC_MODEL": "${selectedModel}" } }`; case 'ccswitch': return generateCCSwitchLink(); case 'openclaw': { - const info = getProviderInfo(selectedModel); + const preset = getModelConnectionPreset(selectedModel); + const modelRef = `${preset.openclawProviderId}/${selectedModel}`; return `{ - "provider": "${info.provider}", - "base_url": "${serverAddress}/", - "api": "${info.api}", - "api_key": "${apiKey}", - "model": { - "id": "${selectedModel}", - "name": "${selectedModel}" + "agents": { + "defaults": { + "models": { + "${modelRef}": { + "alias": "${selectedModel}" + } + }, + "model": { + "primary": "${modelRef}" + } + } + }, + "models": { + "mode": "merge", + "providers": { + "${preset.openclawProviderId}": { + "baseUrl": "${preset.baseUrl}", + "apiKey": "${apiKey}", + "api": "${preset.openclawApi}", + "models": [ + { + "id": "${selectedModel}", + "name": "${selectedModel}" + } + ] + } + } } }`; } - case 'opencode': + case 'opencode': { + const preset = getModelConnectionPreset(selectedModel); + const modelRef = `${preset.opencodeProviderId}/${selectedModel}`; return `{ + "$schema": "https://opencode.ai/config.json", "provider": { - "openai": { + "${preset.opencodeProviderId}": { "options": { - "baseURL": "${serverAddress}/v1", + "baseURL": "${preset.baseUrl}", "apiKey": "${apiKey}" }, "models": { @@ -357,8 +386,9 @@ const ConfigExporter = ({ tokens = [] }) => { } } }, - "$schema": "https://opencode.ai/config.json" + "model": "${modelRef}" }`; + } case 'cursor': return `API Key: ${apiKey} Base URL: ${serverAddress}/v1 @@ -394,7 +424,7 @@ print(response.choices[0].message.content)`; client = anthropic.Anthropic( api_key="${apiKey}", - base_url="${serverAddress}/" + base_url="${serverAddress}" ) message = client.messages.create( @@ -443,12 +473,12 @@ print(message.content[0].text)`; }; const getSelectedToolBaseUrl = () => { - if ( - selectedTool === 'claudecode' || - selectedTool === 'anthropic' || - selectedTool === 'openclaw' - ) { - return `${serverAddress}/`; + const preset = getModelConnectionPreset(selectedModel); + if (selectedTool === 'claudecode' || selectedTool === 'anthropic') { + return serverAddress; + } + if (selectedTool === 'openclaw' || selectedTool === 'opencode') { + return preset.baseUrl; } return `${serverAddress}/v1`; }; diff --git a/src/index.css b/src/index.css index 6fb5a15..3b8b83a 100644 --- a/src/index.css +++ b/src/index.css @@ -20,6 +20,8 @@ --page-card-border: rgba(255,255,255,0.06); --page-input-bg: rgba(255,255,255,0.04); --page-input-border: rgba(255,255,255,0.1); + --page-select-bg: rgba(17,24,39,0.96); + --page-select-panel-bg: rgba(17,24,39,0.98); --page-surface: rgba(255,255,255,0.04); --page-surface-hover: rgba(255,255,255,0.08); --page-divider: rgba(255,255,255,0.06); @@ -73,6 +75,8 @@ --page-card-border: rgb(229,231,235); --page-input-bg: #fff; --page-input-border: rgb(209,213,219); + --page-select-bg: #ffffff; + --page-select-panel-bg: #ffffff; --page-surface: rgba(0,0,0,0.03); --page-surface-hover: rgba(0,0,0,0.06); --page-divider: rgb(229,231,235); @@ -94,6 +98,8 @@ --page-link: 217 119 87; --page-card-bg: rgba(255,255,255,0.7); --page-card-border: #E8DDD0; + --page-select-bg: rgba(255,251,247,0.98); + --page-select-panel-bg: rgba(255,251,247,0.99); --page-surface: rgba(61,48,36,0.04); --page-surface-hover: rgba(61,48,36,0.08); --page-divider: #E8DDD0; @@ -159,6 +165,17 @@ color: rgb(var(--page-text-muted)); } + .input-solid { + background: var(--page-select-bg); + } + + .select-panel { + background: var(--page-select-panel-bg); + border: 1px solid var(--page-divider); + box-shadow: 0 18px 48px rgba(0, 0, 0, 0.28); + backdrop-filter: none; + } + .card-glow { @apply relative overflow-hidden; } @@ -196,11 +213,19 @@ background: rgba(255, 255, 255, 0.97); } +.theme-light .select-panel { + box-shadow: 0 18px 48px rgba(15, 23, 42, 0.12); +} + .theme-claude .modal-overlay .glass, .theme-claude .modal-overlay .glass-sm { background: rgba(250, 246, 241, 0.97); } +.theme-claude .select-panel { + box-shadow: 0 18px 48px rgba(107, 93, 79, 0.14); +} + /* Light theme input focus */ .theme-light .input:focus { border-color: rgb(99 102 241);