/*
Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
import React, { useState, useEffect, useMemo } from 'react';
import {
Modal,
RadioGroup,
Radio,
Select,
Input,
Toast,
Typography,
} from '@douyinfe/semi-ui';
import { useTranslation } from 'react-i18next';
import { selectFilter } from '../../../../helpers';
const APP_CONFIGS = {
claude: {
label: 'Claude',
defaultName: 'My Claude',
modelFields: [
{ key: 'model', label: '主模型' },
{ key: 'haikuModel', label: 'Haiku 模型' },
{ key: 'sonnetModel', label: 'Sonnet 模型' },
{ key: 'opusModel', label: 'Opus 模型' },
],
},
codex: {
label: 'Codex',
defaultName: 'My Codex',
modelFields: [{ key: 'model', label: '主模型' }],
},
gemini: {
label: 'Gemini',
defaultName: 'My Gemini',
modelFields: [{ key: 'model', label: '主模型' }],
},
};
function getServerAddress() {
try {
const raw = localStorage.getItem('status');
if (raw) {
const status = JSON.parse(raw);
if (status.server_address) return status.server_address;
}
} catch (_) {}
return window.location.origin;
}
function buildCCSwitchURL(app, name, models, apiKey) {
const serverAddress = getServerAddress();
const endpoint = app === 'codex' ? serverAddress + '/v1' : serverAddress;
const params = new URLSearchParams();
params.set('resource', 'provider');
params.set('app', app);
params.set('name', name);
params.set('endpoint', endpoint);
params.set('apiKey', apiKey);
for (const [k, v] of Object.entries(models)) {
if (v) params.set(k, v);
}
params.set('homepage', serverAddress);
params.set('enabled', 'true');
return `ccswitch://v1/import?${params.toString()}`;
}
export default function CCSwitchModal({
visible,
onClose,
tokenKey,
modelOptions,
}) {
const { t } = useTranslation();
const [app, setApp] = useState('claude');
const [name, setName] = useState(APP_CONFIGS.claude.defaultName);
const [models, setModels] = useState({});
const currentConfig = APP_CONFIGS[app];
useEffect(() => {
if (visible) {
setModels({});
setApp('claude');
setName(APP_CONFIGS.claude.defaultName);
}
}, [visible]);
const handleAppChange = (val) => {
setApp(val);
setName(APP_CONFIGS[val].defaultName);
setModels({});
};
const handleModelChange = (field, value) => {
setModels((prev) => ({ ...prev, [field]: value }));
};
const handleSubmit = () => {
if (!models.model) {
Toast.warning(t('请选择主模型'));
return;
}
const url = buildCCSwitchURL(app, name, models, 'sk-' + tokenKey);
window.open(url, '_blank');
onClose();
};
const fieldLabelStyle = useMemo(
() => ({
marginBottom: 4,
fontSize: 13,
color: 'var(--semi-color-text-1)',
}),
[],
);
return (