mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-03 02:51:17 +08:00
fix: prevent common config modal infinite reopen loop and add draft editing
The auto-open useEffect in CodexConfigEditor and GeminiConfigEditor created an inescapable loop: commonConfigError triggered modal open, closing the modal didn't clear the error, so the effect immediately reopened it — locking the entire UI. - Remove auto-open useEffect from both Codex and Gemini config editors - Convert common config modals to draft editing (edit locally, validate before save) instead of persisting on every keystroke - Add TOML/JSON pre-validation via parseCommonConfigSnippet before any merge operation to prevent invalid content from being persisted - Expose clearCommonConfigError so editors can clear stale errors on modal close
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { parse as parseToml } from "smol-toml";
|
||||
import {
|
||||
updateTomlCommonConfigSnippet,
|
||||
hasTomlCommonConfigSnippet,
|
||||
} from "@/utils/providerConfigUtils";
|
||||
import { configApi } from "@/lib/api";
|
||||
import { normalizeTomlText } from "@/utils/textNormalization";
|
||||
|
||||
const LEGACY_STORAGE_KEY = "cc-switch:codex-common-config-snippet";
|
||||
const DEFAULT_CODEX_COMMON_CONFIG_SNIPPET = `# Common Codex config
|
||||
@@ -53,6 +55,30 @@ export function useCodexCommonConfig({
|
||||
hasInitializedEditMode.current = false;
|
||||
}, [selectedPresetId, initialEnabled]);
|
||||
|
||||
const parseCommonConfigSnippet = useCallback((snippetString: string) => {
|
||||
const trimmed = snippetString.trim();
|
||||
if (!trimmed) {
|
||||
return {
|
||||
hasContent: false,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = parseToml(normalizeTomlText(snippetString)) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
return {
|
||||
hasContent: Object.keys(parsed).length > 0,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
hasContent: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 初始化:从 config.json 加载,支持从 localStorage 迁移
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
@@ -107,36 +133,59 @@ export function useCodexCommonConfig({
|
||||
|
||||
// 初始化时检查通用配置片段(编辑模式)
|
||||
useEffect(() => {
|
||||
if (initialData?.settingsConfig && !isLoading) {
|
||||
const config =
|
||||
typeof initialData.settingsConfig.config === "string"
|
||||
? initialData.settingsConfig.config
|
||||
: "";
|
||||
const inferredHasCommon = hasTomlCommonConfigSnippet(
|
||||
config,
|
||||
commonConfigSnippet,
|
||||
);
|
||||
const hasCommon = initialEnabled ?? inferredHasCommon;
|
||||
setUseCommonConfig(hasCommon);
|
||||
|
||||
if (hasCommon && !inferredHasCommon && !hasInitializedEditMode.current) {
|
||||
hasInitializedEditMode.current = true;
|
||||
const { updatedConfig, error } = updateTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
commonConfigSnippet,
|
||||
true,
|
||||
);
|
||||
if (!error) {
|
||||
isUpdatingFromCommonConfig.current = true;
|
||||
onConfigChange(updatedConfig);
|
||||
setTimeout(() => {
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
}
|
||||
} else {
|
||||
hasInitializedEditMode.current = true;
|
||||
}
|
||||
if (
|
||||
!initialData?.settingsConfig ||
|
||||
isLoading ||
|
||||
hasInitializedEditMode.current
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasInitializedEditMode.current = true;
|
||||
|
||||
const parsedSnippet = parseCommonConfigSnippet(commonConfigSnippet);
|
||||
if (parsedSnippet.error) {
|
||||
if (commonConfigSnippet.trim()) {
|
||||
setCommonConfigError(parsedSnippet.error);
|
||||
}
|
||||
setUseCommonConfig(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const config =
|
||||
typeof initialData.settingsConfig.config === "string"
|
||||
? initialData.settingsConfig.config
|
||||
: "";
|
||||
const inferredHasCommon = hasTomlCommonConfigSnippet(
|
||||
config,
|
||||
commonConfigSnippet,
|
||||
);
|
||||
const hasCommon = initialEnabled ?? inferredHasCommon;
|
||||
|
||||
if (hasCommon && !inferredHasCommon) {
|
||||
const { updatedConfig, error } = updateTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
commonConfigSnippet,
|
||||
true,
|
||||
);
|
||||
if (error) {
|
||||
setCommonConfigError(error);
|
||||
setUseCommonConfig(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setCommonConfigError("");
|
||||
setUseCommonConfig(true);
|
||||
isUpdatingFromCommonConfig.current = true;
|
||||
onConfigChange(updatedConfig);
|
||||
setTimeout(() => {
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
setCommonConfigError("");
|
||||
setUseCommonConfig(hasCommon);
|
||||
}, [
|
||||
codexConfig,
|
||||
commonConfigSnippet,
|
||||
@@ -144,49 +193,75 @@ export function useCodexCommonConfig({
|
||||
initialEnabled,
|
||||
isLoading,
|
||||
onConfigChange,
|
||||
parseCommonConfigSnippet,
|
||||
]);
|
||||
|
||||
// 新建模式:如果通用配置片段存在且有效,默认启用
|
||||
useEffect(() => {
|
||||
// 仅新建模式、加载完成、尚未初始化过
|
||||
if (!initialData && !isLoading && !hasInitializedNewMode.current) {
|
||||
hasInitializedNewMode.current = true;
|
||||
|
||||
// 检查 TOML 片段是否有实质内容(不只是注释和空行)
|
||||
const lines = commonConfigSnippet.split("\n");
|
||||
const hasContent = lines.some((line) => {
|
||||
const trimmed = line.trim();
|
||||
return trimmed && !trimmed.startsWith("#");
|
||||
});
|
||||
|
||||
if (hasContent) {
|
||||
setUseCommonConfig(true);
|
||||
// 合并通用配置到当前配置
|
||||
const { updatedConfig, error } = updateTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
commonConfigSnippet,
|
||||
true,
|
||||
);
|
||||
if (!error) {
|
||||
isUpdatingFromCommonConfig.current = true;
|
||||
onConfigChange(updatedConfig);
|
||||
setTimeout(() => {
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
if (initialData || isLoading || hasInitializedNewMode.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasInitializedNewMode.current = true;
|
||||
|
||||
const parsedSnippet = parseCommonConfigSnippet(commonConfigSnippet);
|
||||
if (parsedSnippet.error) {
|
||||
if (commonConfigSnippet.trim()) {
|
||||
setCommonConfigError(parsedSnippet.error);
|
||||
}
|
||||
setUseCommonConfig(false);
|
||||
return;
|
||||
}
|
||||
if (!parsedSnippet.hasContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { updatedConfig, error } = updateTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
commonConfigSnippet,
|
||||
true,
|
||||
);
|
||||
if (error) {
|
||||
setCommonConfigError(error);
|
||||
setUseCommonConfig(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setCommonConfigError("");
|
||||
setUseCommonConfig(true);
|
||||
isUpdatingFromCommonConfig.current = true;
|
||||
onConfigChange(updatedConfig);
|
||||
setTimeout(() => {
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
}, [
|
||||
initialData,
|
||||
commonConfigSnippet,
|
||||
isLoading,
|
||||
codexConfig,
|
||||
onConfigChange,
|
||||
parseCommonConfigSnippet,
|
||||
]);
|
||||
|
||||
// 处理通用配置开关
|
||||
const handleCommonConfigToggle = useCallback(
|
||||
(checked: boolean) => {
|
||||
const parsedSnippet = parseCommonConfigSnippet(commonConfigSnippet);
|
||||
if (parsedSnippet.error) {
|
||||
setCommonConfigError(parsedSnippet.error);
|
||||
setUseCommonConfig(false);
|
||||
return;
|
||||
}
|
||||
if (!parsedSnippet.hasContent) {
|
||||
setCommonConfigError(
|
||||
t("codexConfig.noCommonConfigToApply", {
|
||||
defaultValue: "通用配置片段为空或没有可写入的内容",
|
||||
}),
|
||||
);
|
||||
setUseCommonConfig(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const { updatedConfig, error: snippetError } =
|
||||
updateTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
@@ -210,18 +285,39 @@ export function useCodexCommonConfig({
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
},
|
||||
[codexConfig, commonConfigSnippet, onConfigChange],
|
||||
[codexConfig, commonConfigSnippet, onConfigChange, parseCommonConfigSnippet, t],
|
||||
);
|
||||
|
||||
// 处理通用配置片段变化
|
||||
const handleCommonConfigSnippetChange = useCallback(
|
||||
(value: string) => {
|
||||
(value: string): boolean => {
|
||||
const previousSnippet = commonConfigSnippet;
|
||||
setCommonConfigSnippetState(value);
|
||||
|
||||
if (!value.trim()) {
|
||||
setCommonConfigError("");
|
||||
// 保存到 config.json(清空)
|
||||
|
||||
if (useCommonConfig) {
|
||||
const previousParsed = parseCommonConfigSnippet(previousSnippet);
|
||||
let updatedConfig = codexConfig;
|
||||
|
||||
if (!previousParsed.error && previousParsed.hasContent) {
|
||||
const removeResult = updateTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
previousSnippet,
|
||||
false,
|
||||
);
|
||||
if (removeResult.error) {
|
||||
setCommonConfigError(removeResult.error);
|
||||
return false;
|
||||
}
|
||||
updatedConfig = removeResult.updatedConfig;
|
||||
}
|
||||
|
||||
onConfigChange(updatedConfig);
|
||||
setUseCommonConfig(false);
|
||||
}
|
||||
|
||||
setCommonConfigSnippetState("");
|
||||
configApi
|
||||
.setCommonConfigSnippet("codex", "")
|
||||
.catch((error: unknown) => {
|
||||
@@ -230,51 +326,42 @@ export function useCodexCommonConfig({
|
||||
t("codexConfig.saveFailed", { error: String(error) }),
|
||||
);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (useCommonConfig) {
|
||||
const { updatedConfig } = updateTomlCommonConfigSnippet(
|
||||
const parsedNextSnippet = parseCommonConfigSnippet(value);
|
||||
if (parsedNextSnippet.error) {
|
||||
setCommonConfigError(parsedNextSnippet.error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 若当前启用通用配置,需要替换为最新片段
|
||||
if (useCommonConfig) {
|
||||
let nextConfig = codexConfig;
|
||||
const previousParsed = parseCommonConfigSnippet(previousSnippet);
|
||||
|
||||
if (!previousParsed.error && previousParsed.hasContent) {
|
||||
const removeResult = updateTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
previousSnippet,
|
||||
false,
|
||||
);
|
||||
onConfigChange(updatedConfig);
|
||||
setUseCommonConfig(false);
|
||||
if (removeResult.error) {
|
||||
setCommonConfigError(removeResult.error);
|
||||
return false;
|
||||
}
|
||||
nextConfig = removeResult.updatedConfig;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// TOML 格式校验较为复杂,暂时不做校验,直接清空错误
|
||||
setCommonConfigError("");
|
||||
// 保存到 config.json
|
||||
configApi
|
||||
.setCommonConfigSnippet("codex", value)
|
||||
.catch((error: unknown) => {
|
||||
console.error("保存 Codex 通用配置失败:", error);
|
||||
setCommonConfigError(
|
||||
t("codexConfig.saveFailed", { error: String(error) }),
|
||||
);
|
||||
});
|
||||
|
||||
// 若当前启用通用配置,需要替换为最新片段
|
||||
if (useCommonConfig) {
|
||||
const removeResult = updateTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
previousSnippet,
|
||||
false,
|
||||
);
|
||||
if (removeResult.error) {
|
||||
setCommonConfigError(removeResult.error);
|
||||
return;
|
||||
}
|
||||
const addResult = updateTomlCommonConfigSnippet(
|
||||
removeResult.updatedConfig,
|
||||
nextConfig,
|
||||
value,
|
||||
true,
|
||||
);
|
||||
|
||||
if (addResult.error) {
|
||||
setCommonConfigError(addResult.error);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 标记正在通过通用配置更新,避免触发状态检查
|
||||
@@ -285,8 +372,28 @@ export function useCodexCommonConfig({
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
setCommonConfigError("");
|
||||
setCommonConfigSnippetState(value);
|
||||
configApi
|
||||
.setCommonConfigSnippet("codex", value)
|
||||
.catch((error: unknown) => {
|
||||
console.error("保存 Codex 通用配置失败:", error);
|
||||
setCommonConfigError(
|
||||
t("codexConfig.saveFailed", { error: String(error) }),
|
||||
);
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
[commonConfigSnippet, codexConfig, useCommonConfig, onConfigChange],
|
||||
[
|
||||
commonConfigSnippet,
|
||||
codexConfig,
|
||||
onConfigChange,
|
||||
parseCommonConfigSnippet,
|
||||
t,
|
||||
useCommonConfig,
|
||||
],
|
||||
);
|
||||
|
||||
// 当配置变化时检查是否包含通用配置(但避免在通过通用配置更新时检查)
|
||||
@@ -294,12 +401,17 @@ export function useCodexCommonConfig({
|
||||
if (isUpdatingFromCommonConfig.current || isLoading) {
|
||||
return;
|
||||
}
|
||||
const parsedSnippet = parseCommonConfigSnippet(commonConfigSnippet);
|
||||
if (parsedSnippet.error) {
|
||||
setUseCommonConfig(false);
|
||||
return;
|
||||
}
|
||||
const hasCommon = hasTomlCommonConfigSnippet(
|
||||
codexConfig,
|
||||
commonConfigSnippet,
|
||||
);
|
||||
setUseCommonConfig(hasCommon);
|
||||
}, [codexConfig, commonConfigSnippet, isLoading]);
|
||||
}, [codexConfig, commonConfigSnippet, isLoading, parseCommonConfigSnippet]);
|
||||
|
||||
// 从编辑器当前内容提取通用配置片段
|
||||
const handleExtract = useCallback(async () => {
|
||||
@@ -333,6 +445,10 @@ export function useCodexCommonConfig({
|
||||
}
|
||||
}, [codexConfig, t]);
|
||||
|
||||
const clearCommonConfigError = useCallback(() => {
|
||||
setCommonConfigError("");
|
||||
}, []);
|
||||
|
||||
return {
|
||||
useCommonConfig,
|
||||
commonConfigSnippet,
|
||||
@@ -342,5 +458,6 @@ export function useCodexCommonConfig({
|
||||
handleCommonConfigToggle,
|
||||
handleCommonConfigSnippetChange,
|
||||
handleExtract,
|
||||
clearCommonConfigError,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -216,43 +216,55 @@ export function useGeminiCommonConfig({
|
||||
|
||||
// 初始化时检查通用配置片段(编辑模式)
|
||||
useEffect(() => {
|
||||
if (initialData?.settingsConfig && !isLoading) {
|
||||
try {
|
||||
const env =
|
||||
isPlainObject(initialData.settingsConfig.env) &&
|
||||
Object.keys(initialData.settingsConfig.env).length > 0
|
||||
? (initialData.settingsConfig.env as Record<string, string>)
|
||||
: {};
|
||||
const parsed = parseSnippetEnv(commonConfigSnippet);
|
||||
if (parsed.error) return;
|
||||
const inferredHasCommon = hasEnvCommonConfigSnippet(
|
||||
env,
|
||||
parsed.env as Record<string, string>,
|
||||
);
|
||||
const hasCommon = initialEnabled ?? inferredHasCommon;
|
||||
setUseCommonConfig(hasCommon);
|
||||
if (
|
||||
!initialData?.settingsConfig ||
|
||||
isLoading ||
|
||||
hasInitializedEditMode.current
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
hasCommon &&
|
||||
!inferredHasCommon &&
|
||||
!hasInitializedEditMode.current
|
||||
) {
|
||||
hasInitializedEditMode.current = true;
|
||||
const currentEnv = envStringToObj(envValue);
|
||||
const merged = applySnippetToEnv(currentEnv, parsed.env);
|
||||
const nextEnvString = envObjToString(merged);
|
||||
hasInitializedEditMode.current = true;
|
||||
|
||||
isUpdatingFromCommonConfig.current = true;
|
||||
onEnvChange(nextEnvString);
|
||||
setTimeout(() => {
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
} else {
|
||||
hasInitializedEditMode.current = true;
|
||||
try {
|
||||
const env =
|
||||
isPlainObject(initialData.settingsConfig.env) &&
|
||||
Object.keys(initialData.settingsConfig.env).length > 0
|
||||
? (initialData.settingsConfig.env as Record<string, string>)
|
||||
: {};
|
||||
const parsed = parseSnippetEnv(commonConfigSnippet);
|
||||
if (parsed.error) {
|
||||
if (commonConfigSnippet.trim()) {
|
||||
setCommonConfigError(parsed.error);
|
||||
}
|
||||
} catch {
|
||||
// ignore parse error
|
||||
setUseCommonConfig(false);
|
||||
return;
|
||||
}
|
||||
const inferredHasCommon = hasEnvCommonConfigSnippet(
|
||||
env,
|
||||
parsed.env as Record<string, string>,
|
||||
);
|
||||
const hasCommon = initialEnabled ?? inferredHasCommon;
|
||||
|
||||
if (hasCommon && !inferredHasCommon) {
|
||||
const currentEnv = envStringToObj(envValue);
|
||||
const merged = applySnippetToEnv(currentEnv, parsed.env);
|
||||
const nextEnvString = envObjToString(merged);
|
||||
|
||||
setCommonConfigError("");
|
||||
setUseCommonConfig(true);
|
||||
isUpdatingFromCommonConfig.current = true;
|
||||
onEnvChange(nextEnvString);
|
||||
setTimeout(() => {
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
setCommonConfigError("");
|
||||
setUseCommonConfig(hasCommon);
|
||||
} catch {
|
||||
// ignore parse error
|
||||
}
|
||||
}, [
|
||||
applySnippetToEnv,
|
||||
@@ -270,26 +282,34 @@ export function useGeminiCommonConfig({
|
||||
|
||||
// 新建模式:如果通用配置片段存在且有效,默认启用
|
||||
useEffect(() => {
|
||||
// 仅新建模式、加载完成、尚未初始化过
|
||||
if (!initialData && !isLoading && !hasInitializedNewMode.current) {
|
||||
hasInitializedNewMode.current = true;
|
||||
|
||||
const parsed = parseSnippetEnv(commonConfigSnippet);
|
||||
if (parsed.error) return;
|
||||
const hasContent = Object.keys(parsed.env).length > 0;
|
||||
if (!hasContent) return;
|
||||
|
||||
setUseCommonConfig(true);
|
||||
const currentEnv = envStringToObj(envValue);
|
||||
const merged = applySnippetToEnv(currentEnv, parsed.env);
|
||||
const nextEnvString = envObjToString(merged);
|
||||
|
||||
isUpdatingFromCommonConfig.current = true;
|
||||
onEnvChange(nextEnvString);
|
||||
setTimeout(() => {
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
if (initialData || isLoading || hasInitializedNewMode.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasInitializedNewMode.current = true;
|
||||
|
||||
const parsed = parseSnippetEnv(commonConfigSnippet);
|
||||
if (parsed.error) {
|
||||
if (commonConfigSnippet.trim()) {
|
||||
setCommonConfigError(parsed.error);
|
||||
}
|
||||
setUseCommonConfig(false);
|
||||
return;
|
||||
}
|
||||
const hasContent = Object.keys(parsed.env).length > 0;
|
||||
if (!hasContent) return;
|
||||
|
||||
setCommonConfigError("");
|
||||
setUseCommonConfig(true);
|
||||
const currentEnv = envStringToObj(envValue);
|
||||
const merged = applySnippetToEnv(currentEnv, parsed.env);
|
||||
const nextEnvString = envObjToString(merged);
|
||||
|
||||
isUpdatingFromCommonConfig.current = true;
|
||||
onEnvChange(nextEnvString);
|
||||
setTimeout(() => {
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
}, [
|
||||
initialData,
|
||||
isLoading,
|
||||
@@ -346,13 +366,29 @@ export function useGeminiCommonConfig({
|
||||
|
||||
// 处理通用配置片段变化
|
||||
const handleCommonConfigSnippetChange = useCallback(
|
||||
(value: string) => {
|
||||
(value: string): boolean => {
|
||||
const previousSnippet = commonConfigSnippet;
|
||||
setCommonConfigSnippetState(value);
|
||||
|
||||
if (!value.trim()) {
|
||||
setCommonConfigError("");
|
||||
// 保存到 config.json(清空)
|
||||
|
||||
if (useCommonConfig) {
|
||||
const parsedPrevious = parseSnippetEnv(previousSnippet);
|
||||
if (
|
||||
!parsedPrevious.error &&
|
||||
Object.keys(parsedPrevious.env).length > 0
|
||||
) {
|
||||
const currentEnv = envStringToObj(envValue);
|
||||
const updatedEnv = removeSnippetFromEnv(
|
||||
currentEnv,
|
||||
parsedPrevious.env,
|
||||
);
|
||||
onEnvChange(envObjToString(updatedEnv));
|
||||
}
|
||||
setUseCommonConfig(false);
|
||||
}
|
||||
|
||||
setCommonConfigSnippetState("");
|
||||
configApi
|
||||
.setCommonConfigSnippet("gemini", "")
|
||||
.catch((error: unknown) => {
|
||||
@@ -361,36 +397,16 @@ export function useGeminiCommonConfig({
|
||||
t("geminiConfig.saveFailed", { error: String(error) }),
|
||||
);
|
||||
});
|
||||
|
||||
if (useCommonConfig) {
|
||||
const parsed = parseSnippetEnv(previousSnippet);
|
||||
if (!parsed.error && Object.keys(parsed.env).length > 0) {
|
||||
const currentEnv = envStringToObj(envValue);
|
||||
const updatedEnv = removeSnippetFromEnv(currentEnv, parsed.env);
|
||||
onEnvChange(envObjToString(updatedEnv));
|
||||
}
|
||||
setUseCommonConfig(false);
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 校验 JSON 格式
|
||||
const parsed = parseSnippetEnv(value);
|
||||
if (parsed.error) {
|
||||
setCommonConfigError(parsed.error);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
setCommonConfigError("");
|
||||
configApi
|
||||
.setCommonConfigSnippet("gemini", value)
|
||||
.catch((error: unknown) => {
|
||||
console.error("保存 Gemini 通用配置失败:", error);
|
||||
setCommonConfigError(
|
||||
t("geminiConfig.saveFailed", { error: String(error) }),
|
||||
);
|
||||
});
|
||||
|
||||
// 若当前启用通用配置,需要替换为最新片段
|
||||
if (useCommonConfig) {
|
||||
const prevParsed = parseSnippetEnv(previousSnippet);
|
||||
@@ -413,6 +429,19 @@ export function useGeminiCommonConfig({
|
||||
isUpdatingFromCommonConfig.current = false;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
setCommonConfigError("");
|
||||
setCommonConfigSnippetState(value);
|
||||
configApi
|
||||
.setCommonConfigSnippet("gemini", value)
|
||||
.catch((error: unknown) => {
|
||||
console.error("保存 Gemini 通用配置失败:", error);
|
||||
setCommonConfigError(
|
||||
t("geminiConfig.saveFailed", { error: String(error) }),
|
||||
);
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
[
|
||||
applySnippetToEnv,
|
||||
@@ -487,6 +516,10 @@ export function useGeminiCommonConfig({
|
||||
}
|
||||
}, [envStringToObj, envValue, parseSnippetEnv, t]);
|
||||
|
||||
const clearCommonConfigError = useCallback(() => {
|
||||
setCommonConfigError("");
|
||||
}, []);
|
||||
|
||||
return {
|
||||
useCommonConfig,
|
||||
commonConfigSnippet,
|
||||
@@ -496,5 +529,6 @@ export function useGeminiCommonConfig({
|
||||
handleCommonConfigToggle,
|
||||
handleCommonConfigSnippetChange,
|
||||
handleExtract,
|
||||
clearCommonConfigError,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user