refactor: simplify TOML common config handling by removing markers

- Remove COMMON_CONFIG_MARKER_START/END constants
- Simplify config snippet addition/removal logic
- Use natural append/replace approach instead of markers
- Fix unused variable warning
- Improve user experience with cleaner config output
This commit is contained in:
Jason
2025-09-18 22:33:55 +08:00
parent c6e4f3599e
commit 04e81ebbe3
7 changed files with 116 additions and 112 deletions

View File

@@ -62,7 +62,7 @@ const JsonEditor: React.FC<JsonEditorProps> = ({
return diagnostics; return diagnostics;
}), }),
[showValidation] [showValidation],
); );
useEffect(() => { useEffect(() => {

View File

@@ -131,22 +131,23 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
// Codex 通用配置状态 // Codex 通用配置状态
const [useCodexCommonConfig, setUseCodexCommonConfig] = useState(false); const [useCodexCommonConfig, setUseCodexCommonConfig] = useState(false);
const [codexCommonConfigSnippet, setCodexCommonConfigSnippetState] = useState<string>(() => { const [codexCommonConfigSnippet, setCodexCommonConfigSnippetState] =
if (typeof window === "undefined") { useState<string>(() => {
return DEFAULT_CODEX_COMMON_CONFIG_SNIPPET; if (typeof window === "undefined") {
} return DEFAULT_CODEX_COMMON_CONFIG_SNIPPET;
try {
const stored = window.localStorage.getItem(
CODEX_COMMON_CONFIG_STORAGE_KEY,
);
if (stored && stored.trim()) {
return stored;
} }
} catch { try {
// ignore localStorage 读取失败 const stored = window.localStorage.getItem(
} CODEX_COMMON_CONFIG_STORAGE_KEY,
return DEFAULT_CODEX_COMMON_CONFIG_SNIPPET; );
}); if (stored && stored.trim()) {
return stored;
}
} catch {
// ignore localStorage 读取失败
}
return DEFAULT_CODEX_COMMON_CONFIG_SNIPPET;
});
const [codexCommonConfigError, setCodexCommonConfigError] = useState(""); const [codexCommonConfigError, setCodexCommonConfigError] = useState("");
const isUpdatingFromCodexCommonConfig = useRef(false); const isUpdatingFromCodexCommonConfig = useRef(false);
// -1 表示自定义null 表示未选择,>= 0 表示预设索引 // -1 表示自定义null 表示未选择,>= 0 表示预设索引
@@ -217,7 +218,11 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
useEffect(() => { useEffect(() => {
if (initialData) { if (initialData) {
if (!isCodex) { if (!isCodex) {
const configString = JSON.stringify(initialData.settingsConfig, null, 2); const configString = JSON.stringify(
initialData.settingsConfig,
null,
2,
);
const hasCommon = hasCommonConfigSnippet( const hasCommon = hasCommonConfigSnippet(
configString, configString,
commonConfigSnippet, commonConfigSnippet,
@@ -235,7 +240,9 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
}; };
if (config.env) { if (config.env) {
setClaudeModel(config.env.ANTHROPIC_MODEL || ""); setClaudeModel(config.env.ANTHROPIC_MODEL || "");
setClaudeSmallFastModel(config.env.ANTHROPIC_SMALL_FAST_MODEL || ""); setClaudeSmallFastModel(
config.env.ANTHROPIC_SMALL_FAST_MODEL || "",
);
setBaseUrl(config.env.ANTHROPIC_BASE_URL || ""); // 初始化基础 URL setBaseUrl(config.env.ANTHROPIC_BASE_URL || ""); // 初始化基础 URL
// 初始化 Kimi 模型选择 // 初始化 Kimi 模型选择
@@ -254,7 +261,13 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
setUseCodexCommonConfig(hasCommon); setUseCodexCommonConfig(hasCommon);
} }
} }
}, [initialData, commonConfigSnippet, codexCommonConfigSnippet, isCodex, codexConfig]); }, [
initialData,
commonConfigSnippet,
codexCommonConfigSnippet,
isCodex,
codexConfig,
]);
// 当选择预设变化时,同步类别 // 当选择预设变化时,同步类别
useEffect(() => { useEffect(() => {
@@ -529,10 +542,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
setBaseUrl(""); // 清空基础 URL setBaseUrl(""); // 清空基础 URL
// 同步通用配置状态 // 同步通用配置状态
const hasCommon = hasCommonConfigSnippet( const hasCommon = hasCommonConfigSnippet(configString, commonConfigSnippet);
configString,
commonConfigSnippet,
);
setUseCommonConfig(hasCommon); setUseCommonConfig(hasCommon);
setCommonConfigError(""); setCommonConfigError("");
@@ -643,10 +653,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
updateSettingsConfigValue(configString); updateSettingsConfigValue(configString);
// 同步通用配置开关 // 同步通用配置开关
const hasCommon = hasCommonConfigSnippet( const hasCommon = hasCommonConfigSnippet(configString, commonConfigSnippet);
configString,
commonConfigSnippet,
);
setUseCommonConfig(hasCommon); setUseCommonConfig(hasCommon);
}; };
@@ -681,11 +688,12 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
// Codex: 处理通用配置开关 // Codex: 处理通用配置开关
const handleCodexCommonConfigToggle = (checked: boolean) => { const handleCodexCommonConfigToggle = (checked: boolean) => {
const { updatedConfig, error: snippetError } = updateTomlCommonConfigSnippet( const { updatedConfig, error: snippetError } =
codexConfig, updateTomlCommonConfigSnippet(
codexCommonConfigSnippet, codexConfig,
checked, codexCommonConfigSnippet,
); checked,
);
if (snippetError) { if (snippetError) {
setCodexCommonConfigError(snippetError); setCodexCommonConfigError(snippetError);
@@ -753,10 +761,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
// 保存 Codex 通用配置到 localStorage // 保存 Codex 通用配置到 localStorage
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
try { try {
window.localStorage.setItem( window.localStorage.setItem(CODEX_COMMON_CONFIG_STORAGE_KEY, value);
CODEX_COMMON_CONFIG_STORAGE_KEY,
value,
);
} catch { } catch {
// ignore localStorage 写入失败 // ignore localStorage 写入失败
} }
@@ -1177,7 +1182,9 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
useCommonConfig={useCodexCommonConfig} useCommonConfig={useCodexCommonConfig}
onCommonConfigToggle={handleCodexCommonConfigToggle} onCommonConfigToggle={handleCodexCommonConfigToggle}
commonConfigSnippet={codexCommonConfigSnippet} commonConfigSnippet={codexCommonConfigSnippet}
onCommonConfigSnippetChange={handleCodexCommonConfigSnippetChange} onCommonConfigSnippetChange={
handleCodexCommonConfigSnippetChange
}
commonConfigError={codexCommonConfigError} commonConfigError={codexCommonConfigError}
authError={codexAuthError} authError={codexAuthError}
/> />

View File

@@ -120,9 +120,7 @@ const ClaudeConfigEditor: React.FC<ClaudeConfigEditorProps> = ({
rows={12} rows={12}
/> />
{configError && ( {configError && (
<p className="text-xs text-red-500 dark:text-red-400"> <p className="text-xs text-red-500 dark:text-red-400">{configError}</p>
{configError}
</p>
)} )}
<p className="text-xs text-gray-500 dark:text-gray-400"> <p className="text-xs text-gray-500 dark:text-gray-400">
Claude Code settings.json Claude Code settings.json

View File

@@ -97,9 +97,7 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
data-enable-grammarly="false" data-enable-grammarly="false"
/> />
{authError && ( {authError && (
<p className="text-xs text-red-500 dark:text-red-400"> <p className="text-xs text-red-500 dark:text-red-400">{authError}</p>
{authError}
</p>
)} )}
<p className="text-xs text-gray-500 dark:text-gray-400"> <p className="text-xs text-gray-500 dark:text-gray-400">
Codex auth.json Codex auth.json
@@ -194,7 +192,9 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
</p> </p>
<textarea <textarea
value={commonConfigSnippet} value={commonConfigSnippet}
onChange={(e) => handleCommonConfigSnippetChange(e.target.value)} onChange={(e) =>
handleCommonConfigSnippetChange(e.target.value)
}
placeholder={`# Common Codex config placeholder={`# Common Codex config
# Add your common TOML configuration here`} # Add your common TOML configuration here`}
rows={12} rows={12}

View File

@@ -22,7 +22,10 @@ const deepMerge = (
return target; return target;
}; };
const deepRemove = (target: Record<string, any>, source: Record<string, any>) => { const deepRemove = (
target: Record<string, any>,
source: Record<string, any>,
) => {
Object.entries(source).forEach(([key, value]) => { Object.entries(source).forEach(([key, value]) => {
if (!(key in target)) return; if (!(key in target)) return;
@@ -59,7 +62,7 @@ const isSubset = (target: any, source: any): boolean => {
const deepClone = <T>(obj: T): T => { const deepClone = <T>(obj: T): T => {
if (obj === null || typeof obj !== "object") return obj; if (obj === null || typeof obj !== "object") return obj;
if (obj instanceof Date) return new Date(obj.getTime()) as T; if (obj instanceof Date) return new Date(obj.getTime()) as T;
if (obj instanceof Array) return obj.map(item => deepClone(item)) as T; if (obj instanceof Array) return obj.map((item) => deepClone(item)) as T;
if (obj instanceof Object) { if (obj instanceof Object) {
const clonedObj = {} as T; const clonedObj = {} as T;
for (const key in obj) { for (const key in obj) {
@@ -78,7 +81,10 @@ export interface UpdateCommonConfigResult {
} }
// 验证JSON配置格式 // 验证JSON配置格式
export const validateJsonConfig = (value: string, fieldName: string = "配置"): string => { export const validateJsonConfig = (
value: string,
fieldName: string = "配置",
): string => {
if (!value.trim()) { if (!value.trim()) {
return ""; return "";
} }
@@ -205,14 +211,14 @@ export const setApiKeyInConfig = (
// ========== TOML Config Utilities ========== // ========== TOML Config Utilities ==========
const COMMON_CONFIG_MARKER_START = "# === COMMON CONFIG START ===";
const COMMON_CONFIG_MARKER_END = "# === COMMON CONFIG END ===";
export interface UpdateTomlCommonConfigResult { export interface UpdateTomlCommonConfigResult {
updatedConfig: string; updatedConfig: string;
error?: string; error?: string;
} }
// 保存之前的通用配置片段,用于替换操作
let previousCommonSnippet = "";
// 将通用配置片段写入/移除 TOML 配置 // 将通用配置片段写入/移除 TOML 配置
export const updateTomlCommonConfigSnippet = ( export const updateTomlCommonConfigSnippet = (
tomlString: string, tomlString: string,
@@ -220,53 +226,52 @@ export const updateTomlCommonConfigSnippet = (
enabled: boolean, enabled: boolean,
): UpdateTomlCommonConfigResult => { ): UpdateTomlCommonConfigResult => {
if (!snippetString.trim()) { if (!snippetString.trim()) {
// 如果片段为空,移除已存在的通用配置部分 // 如果片段为空,直接返回原始配置
const cleaned = removeTomlCommonConfig(tomlString);
return { return {
updatedConfig: cleaned, updatedConfig: tomlString,
}; };
} }
if (enabled) { if (enabled) {
// 添加通用配置 // 添加通用配置
const withoutOld = removeTomlCommonConfig(tomlString); // 先移除旧的通用配置(如果有)
const commonSection = `\n${COMMON_CONFIG_MARKER_START}\n${snippetString}\n${COMMON_CONFIG_MARKER_END}\n`; let updatedConfig = tomlString;
if (previousCommonSnippet && tomlString.includes(previousCommonSnippet)) {
updatedConfig = tomlString.replace(previousCommonSnippet, "");
}
// 在文件末尾添加新的通用配置
// 确保有适当的换行
const needsNewline = updatedConfig && !updatedConfig.endsWith("\n");
updatedConfig =
updatedConfig + (needsNewline ? "\n\n" : "\n") + snippetString;
// 保存当前通用配置片段
previousCommonSnippet = snippetString;
return { return {
updatedConfig: withoutOld + commonSection, updatedConfig: updatedConfig.trim() + "\n",
}; };
} else { } else {
// 移除通用配置 // 移除通用配置
const cleaned = removeTomlCommonConfig(tomlString); if (tomlString.includes(snippetString)) {
const updatedConfig = tomlString.replace(snippetString, "");
// 清理多余的空行
const cleaned = updatedConfig.replace(/\n{3,}/g, "\n\n").trim();
// 清空保存的状态
previousCommonSnippet = "";
return {
updatedConfig: cleaned ? cleaned + "\n" : "",
};
}
return { return {
updatedConfig: cleaned, updatedConfig: tomlString,
}; };
} }
}; };
// 从 TOML 中移除通用配置部分
const removeTomlCommonConfig = (tomlString: string): string => {
const startIdx = tomlString.indexOf(COMMON_CONFIG_MARKER_START);
const endIdx = tomlString.indexOf(COMMON_CONFIG_MARKER_END);
if (startIdx === -1 || endIdx === -1) {
return tomlString;
}
// 找到标记前的换行符(如果有)
let realStartIdx = startIdx;
if (startIdx > 0 && tomlString[startIdx - 1] === '\n') {
realStartIdx = startIdx - 1;
}
// 找到标记后的换行符(如果有)
let realEndIdx = endIdx + COMMON_CONFIG_MARKER_END.length;
if (realEndIdx < tomlString.length && tomlString[realEndIdx] === '\n') {
realEndIdx = realEndIdx + 1;
}
return tomlString.slice(0, realStartIdx) + tomlString.slice(realEndIdx);
};
// 检查 TOML 配置是否已包含通用配置片段 // 检查 TOML 配置是否已包含通用配置片段
export const hasTomlCommonConfigSnippet = ( export const hasTomlCommonConfigSnippet = (
tomlString: string, tomlString: string,
@@ -274,17 +279,11 @@ export const hasTomlCommonConfigSnippet = (
): boolean => { ): boolean => {
if (!snippetString.trim()) return false; if (!snippetString.trim()) return false;
const startIdx = tomlString.indexOf(COMMON_CONFIG_MARKER_START); // 简单检查配置是否包含片段内容
const endIdx = tomlString.indexOf(COMMON_CONFIG_MARKER_END); // 去除空白字符后比较,避免格式差异影响
const normalizeWhitespace = (str: string) => str.replace(/\s+/g, " ").trim();
if (startIdx === -1 || endIdx === -1 || startIdx >= endIdx) { return normalizeWhitespace(tomlString).includes(
return false; normalizeWhitespace(snippetString),
} );
// 提取标记之间的内容
const existingSnippet = tomlString
.slice(startIdx + COMMON_CONFIG_MARKER_START.length, endIdx)
.trim();
return existingSnippet === snippetString.trim();
}; };