Files
CC-Switch/src/components/providers/forms/hooks/useBaseUrlState.ts
T
Jason 3bf37cf0ff refactor(url): remove automatic trailing slash stripping from base URL inputs
- Remove `.replace(/\/+$/, "")` from all base URL handlers in useBaseUrlState.ts
- Remove trailing slash stripping in useCodexConfigState.ts
- Remove trailing slash normalization in providerConfigUtils.ts setCodexBaseUrl()
- Update i18n hints (en/ja/zh) to instruct users to avoid trailing slashes

This gives users explicit control over URL format rather than silently modifying input.
2025-11-29 20:52:29 +08:00

173 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useCallback, useRef, useEffect } from "react";
import {
extractCodexBaseUrl,
setCodexBaseUrl as setCodexBaseUrlInConfig,
} from "@/utils/providerConfigUtils";
import type { ProviderCategory } from "@/types";
interface UseBaseUrlStateProps {
appType: "claude" | "codex" | "gemini";
category: ProviderCategory | undefined;
settingsConfig: string;
codexConfig?: string;
onSettingsConfigChange: (config: string) => void;
onCodexConfigChange?: (config: string) => void;
}
/**
* 管理 Base URL 状态
* 支持 Claude (JSON) 和 Codex (TOML) 两种格式
*/
export function useBaseUrlState({
appType,
category,
settingsConfig,
codexConfig,
onSettingsConfigChange,
onCodexConfigChange,
}: UseBaseUrlStateProps) {
const [baseUrl, setBaseUrl] = useState("");
const [codexBaseUrl, setCodexBaseUrl] = useState("");
const [geminiBaseUrl, setGeminiBaseUrl] = useState("");
const isUpdatingRef = useRef(false);
// 从配置同步到 stateClaude
useEffect(() => {
if (appType !== "claude") return;
// 只有 official 类别不显示 Base URL 输入框,其他类别都需要回填
if (category === "official") return;
if (isUpdatingRef.current) return;
try {
const config = JSON.parse(settingsConfig || "{}");
const envUrl: unknown = config?.env?.ANTHROPIC_BASE_URL;
if (typeof envUrl === "string" && envUrl && envUrl.trim() !== baseUrl) {
setBaseUrl(envUrl.trim());
}
} catch {
// ignore
}
}, [appType, category, settingsConfig, baseUrl]);
// 从配置同步到 stateCodex
useEffect(() => {
if (appType !== "codex") return;
// 只有 official 类别不显示 Base URL 输入框,其他类别都需要回填
if (category === "official") return;
if (isUpdatingRef.current) return;
if (!codexConfig) return;
const extracted = extractCodexBaseUrl(codexConfig) || "";
if (extracted !== codexBaseUrl) {
setCodexBaseUrl(extracted);
}
}, [appType, category, codexConfig, codexBaseUrl]);
// 从Claude配置同步到 stateGemini
useEffect(() => {
if (appType !== "gemini") return;
// 只有 official 类别不显示 Base URL 输入框,其他类别都需要回填
if (category === "official") return;
if (isUpdatingRef.current) return;
try {
const config = JSON.parse(settingsConfig || "{}");
const envUrl: unknown = config?.env?.GOOGLE_GEMINI_BASE_URL;
const nextUrl = typeof envUrl === "string" ? envUrl.trim() : "";
if (nextUrl !== geminiBaseUrl) {
setGeminiBaseUrl(nextUrl);
setBaseUrl(nextUrl); // 也更新 baseUrl 用于 UI
}
} catch {
// ignore
}
}, [appType, category, settingsConfig, geminiBaseUrl]);
// 处理 Claude Base URL 变化
const handleClaudeBaseUrlChange = useCallback(
(url: string) => {
const sanitized = url.trim();
setBaseUrl(sanitized);
isUpdatingRef.current = true;
try {
const config = JSON.parse(settingsConfig || "{}");
if (!config.env) {
config.env = {};
}
config.env.ANTHROPIC_BASE_URL = sanitized;
onSettingsConfigChange(JSON.stringify(config, null, 2));
} catch {
// ignore
} finally {
setTimeout(() => {
isUpdatingRef.current = false;
}, 0);
}
},
[settingsConfig, onSettingsConfigChange],
);
// 处理 Codex Base URL 变化
const handleCodexBaseUrlChange = useCallback(
(url: string) => {
const sanitized = url.trim();
setCodexBaseUrl(sanitized);
if (!sanitized || !onCodexConfigChange) {
return;
}
isUpdatingRef.current = true;
const updatedConfig = setCodexBaseUrlInConfig(
codexConfig || "",
sanitized,
);
onCodexConfigChange(updatedConfig);
setTimeout(() => {
isUpdatingRef.current = false;
}, 0);
},
[codexConfig, onCodexConfigChange],
);
// 处理 Gemini Base URL 变化
const handleGeminiBaseUrlChange = useCallback(
(url: string) => {
const sanitized = url.trim();
setGeminiBaseUrl(sanitized);
setBaseUrl(sanitized); // 也更新 baseUrl 用于 UI
isUpdatingRef.current = true;
try {
const config = JSON.parse(settingsConfig || "{}");
if (!config.env) {
config.env = {};
}
config.env.GOOGLE_GEMINI_BASE_URL = sanitized;
onSettingsConfigChange(JSON.stringify(config, null, 2));
} catch {
// ignore
} finally {
setTimeout(() => {
isUpdatingRef.current = false;
}, 0);
}
},
[settingsConfig, onSettingsConfigChange],
);
return {
baseUrl,
setBaseUrl,
codexBaseUrl,
setCodexBaseUrl,
geminiBaseUrl,
setGeminiBaseUrl,
handleClaudeBaseUrlChange,
handleCodexBaseUrlChange,
handleGeminiBaseUrlChange,
};
}