feat: apply common config as runtime overlay instead of materialized merge

Common config snippets are now dynamically overlaid when writing live
files, rather than being pre-merged into provider snapshots at edit time.
This ensures that updating a snippet immediately takes effect for the
current provider and automatically propagates to other providers on
their next switch.

Key changes:
- Add write_live_with_common_config() overlay pipeline
- Strip common config from live before backfilling provider snapshots
- Normalize provider snapshots on save to keep them snippet-free
- Add explicit commonConfigEnabled flag in ProviderMeta (Option<bool>)
- Migrate legacy providers on snippet save (infer flag from subset check)
- Add Codex TOML snippet validation in set_common_config_snippet
- Stabilize onConfigChange callbacks with useCallback in ProviderForm
This commit is contained in:
Jason
2026-03-08 21:45:20 +08:00
parent fc6f2af4c6
commit 6d078e7f33
10 changed files with 982 additions and 34 deletions
@@ -18,6 +18,7 @@ interface UseCommonConfigSnippetProps {
initialData?: {
settingsConfig?: Record<string, unknown>;
};
initialEnabled?: boolean;
selectedPresetId?: string;
/** When false, the hook skips all logic and returns disabled state. Default: true */
enabled?: boolean;
@@ -31,6 +32,7 @@ export function useCommonConfigSnippet({
settingsConfig,
onConfigChange,
initialData,
initialEnabled,
selectedPresetId,
enabled = true,
}: UseCommonConfigSnippetProps) {
@@ -47,12 +49,15 @@ export function useCommonConfigSnippet({
const isUpdatingFromCommonConfig = useRef(false);
// 用于跟踪新建模式是否已初始化默认勾选
const hasInitializedNewMode = useRef(false);
// 用于跟踪编辑模式是否已初始化显式开关/预览
const hasInitializedEditMode = useRef(false);
// 当预设变化时,重置初始化标记,使新预设能够重新触发初始化逻辑
useEffect(() => {
if (!enabled) return;
hasInitializedNewMode.current = false;
}, [selectedPresetId, enabled]);
hasInitializedEditMode.current = false;
}, [selectedPresetId, enabled, initialEnabled]);
// 初始化:从 config.json 加载,支持从 localStorage 迁移
useEffect(() => {
@@ -115,13 +120,44 @@ export function useCommonConfigSnippet({
if (!enabled) return;
if (initialData && !isLoading) {
const configString = JSON.stringify(initialData.settingsConfig, null, 2);
const hasCommon = hasCommonConfigSnippet(
const inferredHasCommon = hasCommonConfigSnippet(
configString,
commonConfigSnippet,
);
const hasCommon = initialEnabled ?? inferredHasCommon;
setUseCommonConfig(hasCommon);
if (
hasCommon &&
!inferredHasCommon &&
!hasInitializedEditMode.current
) {
hasInitializedEditMode.current = true;
const { updatedConfig, error } = updateCommonConfigSnippet(
settingsConfig,
commonConfigSnippet,
true,
);
if (!error) {
isUpdatingFromCommonConfig.current = true;
onConfigChange(updatedConfig);
setTimeout(() => {
isUpdatingFromCommonConfig.current = false;
}, 0);
}
} else {
hasInitializedEditMode.current = true;
}
}
}, [enabled, initialData, commonConfigSnippet, isLoading]);
}, [
enabled,
initialData,
initialEnabled,
commonConfigSnippet,
isLoading,
onConfigChange,
settingsConfig,
]);
// 新建模式:如果通用配置片段存在且有效,默认启用
useEffect(() => {