feat: add Claude common config snippet functionality

- Create useCommonConfigSnippet hook to manage common config state
- Create CommonConfigEditor component with modal for editing
- Support merging/removing common config snippets from provider configs
- Persist common config to localStorage for reuse across providers
- Auto-detect if provider config contains common snippet
- Replace JSON editor with CommonConfigEditor in ProviderForm
This commit is contained in:
Jason
2025-10-16 20:32:11 +08:00
parent 74afca7b58
commit 856beb3b70
4 changed files with 373 additions and 35 deletions

View File

@@ -12,8 +12,6 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { useTheme } from "@/components/theme-provider";
import JsonEditor from "@/components/JsonEditor";
import { providerSchema, type ProviderFormData } from "@/lib/schemas/provider";
import type { AppType } from "@/lib/api";
import type { ProviderCategory, CustomEndpoint } from "@/types";
@@ -27,6 +25,7 @@ import ApiKeyInput from "@/components/ProviderForm/ApiKeyInput";
import EndpointSpeedTest from "@/components/ProviderForm/EndpointSpeedTest";
import CodexConfigEditor from "@/components/ProviderForm/CodexConfigEditor";
import KimiModelSelector from "@/components/ProviderForm/KimiModelSelector";
import { CommonConfigEditor } from "./CommonConfigEditor";
import { Zap } from "lucide-react";
import {
useProviderCategory,
@@ -38,6 +37,7 @@ import {
useCustomEndpoints,
useKimiModelSelector,
useTemplateValues,
useCommonConfigSnippet,
} from "./hooks";
const CLAUDE_DEFAULT_CONFIG = JSON.stringify({ env: {}, config: {} }, null, 2);
@@ -68,7 +68,6 @@ export function ProviderForm({
initialData,
}: ProviderFormProps) {
const { t } = useTranslation();
const { theme } = useTheme();
const isEditMode = Boolean(initialData);
const [selectedPresetId, setSelectedPresetId] = useState<string | null>(
@@ -172,14 +171,6 @@ export function ProviderForm({
form.reset(defaultValues);
}, [defaultValues, form]);
const isDarkMode = useMemo(() => {
if (theme === "dark") return true;
if (theme === "light") return false;
return typeof window !== "undefined"
? window.document.documentElement.classList.contains("dark")
: false;
}, [theme]);
const presetCategoryLabels: Record<string, string> = useMemo(
() => ({
official: t("providerPreset.categoryOfficial", {
@@ -243,6 +234,21 @@ export function ProviderForm({
onConfigChange: (config) => form.setValue("settingsConfig", config),
});
// 使用通用配置片段 hook (仅 Claude 模式)
const {
useCommonConfig,
commonConfigSnippet,
commonConfigError,
handleCommonConfigToggle,
handleCommonConfigSnippetChange,
} = useCommonConfigSnippet({
settingsConfig: form.watch("settingsConfig"),
onConfigChange: (config) => form.setValue("settingsConfig", config),
initialData: appType === "claude" ? initialData : undefined,
});
const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false);
const handleSubmit = (values: ProviderFormData) => {
// 验证模板变量(仅 Claude 模式)
if (appType === "claude" && templateValueEntries.length > 0) {
@@ -790,7 +796,7 @@ export function ProviderForm({
/>
)}
{/* 配置编辑器Claude 使用 JSON 编辑器Codex 使用专用编辑器 */}
{/* 配置编辑器Claude 使用通用配置编辑器Codex 使用专用编辑器 */}
{appType === "codex" ? (
<CodexConfigEditor
authValue={codexAuth}
@@ -805,29 +811,17 @@ export function ProviderForm({
authError={codexAuthError}
/>
) : (
<FormField
control={form.control}
name="settingsConfig"
render={({ field }) => (
<FormItem>
<FormLabel>
{t("provider.configJson", { defaultValue: "配置 JSON" })}
</FormLabel>
<FormControl>
<div className="rounded-md border">
<JsonEditor
value={field.value}
onChange={field.onChange}
placeholder={CLAUDE_DEFAULT_CONFIG}
darkMode={isDarkMode}
rows={14}
showValidation
/>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
<CommonConfigEditor
value={form.watch("settingsConfig")}
onChange={(value) => form.setValue("settingsConfig", value)}
useCommonConfig={useCommonConfig}
onCommonConfigToggle={handleCommonConfigToggle}
commonConfigSnippet={commonConfigSnippet}
onCommonConfigSnippetChange={handleCommonConfigSnippetChange}
commonConfigError={commonConfigError}
onEditClick={() => setIsCommonConfigModalOpen(true)}
isModalOpen={isCommonConfigModalOpen}
onModalClose={() => setIsCommonConfigModalOpen(false)}
/>
)}