mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-05-06 11:22:48 +08:00
fix:Add a new vendor page, API endpoint, and model name. (#1155)
* fix:Add a new vendor page, API endpoint, and model name. Fix the bug where, after entering characters, line breaks cannot be fully deleted. * fix: add missing i18n key codexConfig.modelNameHint for zh/en/ja --------- Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
@@ -117,9 +117,13 @@ export function CodexFormFields({
|
||||
className="w-full px-3 py-2 border border-border-default bg-background text-foreground rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:focus:ring-blue-400/20 transition-colors"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("codexConfig.modelNameHint", {
|
||||
defaultValue: "指定使用的模型,将自动更新到 config.toml 中",
|
||||
})}
|
||||
{modelName.trim()
|
||||
? t("codexConfig.modelNameHint", {
|
||||
defaultValue: "指定使用的模型,将自动更新到 config.toml 中",
|
||||
})
|
||||
: t("providerForm.modelHint", {
|
||||
defaultValue: "💡 留空将使用供应商的默认模型",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -60,10 +60,8 @@ export function useBaseUrlState({
|
||||
if (!codexConfig) return;
|
||||
|
||||
const extracted = extractCodexBaseUrl(codexConfig) || "";
|
||||
if (extracted !== codexBaseUrl) {
|
||||
setCodexBaseUrl(extracted);
|
||||
}
|
||||
}, [appType, category, codexConfig, codexBaseUrl]);
|
||||
setCodexBaseUrl((prev) => (prev === extracted ? prev : extracted));
|
||||
}, [appType, category, codexConfig]);
|
||||
|
||||
// 从Claude配置同步到 state(Gemini)
|
||||
useEffect(() => {
|
||||
@@ -116,7 +114,7 @@ export function useBaseUrlState({
|
||||
const sanitized = url.trim();
|
||||
setCodexBaseUrl(sanitized);
|
||||
|
||||
if (!sanitized || !onCodexConfigChange) {
|
||||
if (!onCodexConfigChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,10 +74,8 @@ export function useCodexConfigState({ initialData }: UseCodexConfigStateProps) {
|
||||
return;
|
||||
}
|
||||
const extracted = extractCodexBaseUrl(codexConfig) || "";
|
||||
if (extracted !== codexBaseUrl) {
|
||||
setCodexBaseUrl(extracted);
|
||||
}
|
||||
}, [codexConfig, codexBaseUrl]);
|
||||
setCodexBaseUrl((prev) => (prev === extracted ? prev : extracted));
|
||||
}, [codexConfig]);
|
||||
|
||||
// 与 TOML 配置保持模型名称同步
|
||||
useEffect(() => {
|
||||
@@ -85,10 +83,8 @@ export function useCodexConfigState({ initialData }: UseCodexConfigStateProps) {
|
||||
return;
|
||||
}
|
||||
const extracted = extractCodexModelName(codexConfig) || "";
|
||||
if (extracted !== codexModelName) {
|
||||
setCodexModelName(extracted);
|
||||
}
|
||||
}, [codexConfig, codexModelName]);
|
||||
setCodexModelName((prev) => (prev === extracted ? prev : extracted));
|
||||
}, [codexConfig]);
|
||||
|
||||
// 获取 API Key(从 auth JSON)
|
||||
const getCodexAuthApiKey = useCallback((authString: string): string => {
|
||||
@@ -165,10 +161,6 @@ export function useCodexConfigState({ initialData }: UseCodexConfigStateProps) {
|
||||
const sanitized = url.trim();
|
||||
setCodexBaseUrl(sanitized);
|
||||
|
||||
if (!sanitized) {
|
||||
return;
|
||||
}
|
||||
|
||||
isUpdatingCodexBaseUrlRef.current = true;
|
||||
setCodexConfig((prev) => setCodexBaseUrlInConfig(prev, sanitized));
|
||||
setTimeout(() => {
|
||||
@@ -184,10 +176,6 @@ export function useCodexConfigState({ initialData }: UseCodexConfigStateProps) {
|
||||
const trimmed = modelName.trim();
|
||||
setCodexModelName(trimmed);
|
||||
|
||||
if (!trimmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
isUpdatingCodexModelNameRef.current = true;
|
||||
setCodexConfig((prev) => setCodexModelNameInConfig(prev, trimmed));
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -775,7 +775,8 @@
|
||||
"extractFromCurrent": "Extract from Editor",
|
||||
"extractNoCommonConfig": "No common config available to extract from editor",
|
||||
"extractFailed": "Extract failed: {{error}}",
|
||||
"saveFailed": "Save failed: {{error}}"
|
||||
"saveFailed": "Save failed: {{error}}",
|
||||
"modelNameHint": "Specify the model to use, will be auto-updated in config.toml"
|
||||
},
|
||||
"geminiConfig": {
|
||||
"envFile": "Environment Variables (.env)",
|
||||
|
||||
@@ -775,7 +775,8 @@
|
||||
"extractFromCurrent": "編集内容から抽出",
|
||||
"extractNoCommonConfig": "編集内容から抽出できる共通設定がありません",
|
||||
"extractFailed": "抽出に失敗しました: {{error}}",
|
||||
"saveFailed": "保存に失敗しました: {{error}}"
|
||||
"saveFailed": "保存に失敗しました: {{error}}",
|
||||
"modelNameHint": "使用するモデルを指定します。config.toml に自動更新されます"
|
||||
},
|
||||
"geminiConfig": {
|
||||
"envFile": "環境変数 (.env)",
|
||||
|
||||
@@ -775,7 +775,8 @@
|
||||
"extractFromCurrent": "从编辑内容提取",
|
||||
"extractNoCommonConfig": "当前编辑内容没有可提取的通用配置",
|
||||
"extractFailed": "提取失败: {{error}}",
|
||||
"saveFailed": "保存失败: {{error}}"
|
||||
"saveFailed": "保存失败: {{error}}",
|
||||
"modelNameHint": "指定使用的模型,将自动更新到 config.toml 中"
|
||||
},
|
||||
"geminiConfig": {
|
||||
"envFile": "环境变量 (.env)",
|
||||
|
||||
@@ -447,12 +447,23 @@ export const setCodexBaseUrl = (
|
||||
baseUrl: string,
|
||||
): string => {
|
||||
const trimmed = baseUrl.trim();
|
||||
if (!trimmed) {
|
||||
return configText;
|
||||
}
|
||||
// 归一化原文本中的引号(既能匹配,也能输出稳定格式)
|
||||
const normalizedText = normalizeQuotes(configText);
|
||||
|
||||
// 允许清空:当 baseUrl 为空时,移除 base_url 行
|
||||
if (!trimmed) {
|
||||
if (!normalizedText) return normalizedText;
|
||||
const next = normalizedText
|
||||
.split("\n")
|
||||
.filter((line) => !/^\s*base_url\s*=/.test(line))
|
||||
.join("\n")
|
||||
// 避免移除后留下过多空行
|
||||
.replace(/\n{3,}/g, "\n\n")
|
||||
// 避免开头出现空行
|
||||
.replace(/^\n+/, "");
|
||||
return next;
|
||||
}
|
||||
|
||||
const normalizedUrl = trimmed.replace(/\s+/g, "");
|
||||
const replacementLine = `base_url = "${normalizedUrl}"`;
|
||||
const pattern = /base_url\s*=\s*(["'])([^"']+)\1/;
|
||||
@@ -494,13 +505,21 @@ export const setCodexModelName = (
|
||||
modelName: string,
|
||||
): string => {
|
||||
const trimmed = modelName.trim();
|
||||
if (!trimmed) {
|
||||
return configText;
|
||||
}
|
||||
|
||||
// 归一化原文本中的引号(既能匹配,也能输出稳定格式)
|
||||
const normalizedText = normalizeQuotes(configText);
|
||||
|
||||
// 允许清空:当 modelName 为空时,移除 model 行
|
||||
if (!trimmed) {
|
||||
if (!normalizedText) return normalizedText;
|
||||
const next = normalizedText
|
||||
.split("\n")
|
||||
.filter((line) => !/^\s*model\s*=/.test(line))
|
||||
.join("\n")
|
||||
.replace(/\n{3,}/g, "\n\n")
|
||||
.replace(/^\n+/, "");
|
||||
return next;
|
||||
}
|
||||
|
||||
const replacementLine = `model = "${trimmed}"`;
|
||||
const pattern = /^model\s*=\s*["']([^"']+)["']/m;
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
extractCodexBaseUrl,
|
||||
extractCodexModelName,
|
||||
setCodexBaseUrl,
|
||||
setCodexModelName,
|
||||
} from "@/utils/providerConfigUtils";
|
||||
|
||||
describe("Codex TOML utils", () => {
|
||||
it("removes base_url line when set to empty", () => {
|
||||
const input = [
|
||||
'model_provider = "openai"',
|
||||
'base_url = "https://api.example.com/v1"',
|
||||
'model = "gpt-5-codex"',
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
const output = setCodexBaseUrl(input, "");
|
||||
|
||||
expect(output).not.toMatch(/^\s*base_url\s*=/m);
|
||||
expect(extractCodexBaseUrl(output)).toBeUndefined();
|
||||
expect(extractCodexModelName(output)).toBe("gpt-5-codex");
|
||||
});
|
||||
|
||||
it("removes model line when set to empty", () => {
|
||||
const input = [
|
||||
'model_provider = "openai"',
|
||||
'base_url = "https://api.example.com/v1"',
|
||||
'model = "gpt-5-codex"',
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
const output = setCodexModelName(input, "");
|
||||
|
||||
expect(output).not.toMatch(/^\s*model\s*=/m);
|
||||
expect(extractCodexModelName(output)).toBeUndefined();
|
||||
expect(extractCodexBaseUrl(output)).toBe("https://api.example.com/v1");
|
||||
});
|
||||
|
||||
it("updates existing values when non-empty", () => {
|
||||
const input = [
|
||||
'model_provider = "openai"',
|
||||
"base_url = 'https://old.example/v1'",
|
||||
'model = "old-model"',
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
const output1 = setCodexBaseUrl(input, " https://new.example/v1 \n");
|
||||
expect(extractCodexBaseUrl(output1)).toBe("https://new.example/v1");
|
||||
|
||||
const output2 = setCodexModelName(output1, " new-model \n");
|
||||
expect(extractCodexModelName(output2)).toBe("new-model");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user