mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-10-26 01:34:51 +08:00
feat(kimi): move request headers into collapsible advanced options
Custom headers are rarely needed for Kimi Code providers, so keep them under an "Advanced options" section like the Claude and Codex forms. It opens by itself when headers are already set, from an existing card or a preset that carries them.
This commit is contained in:
1 parent
8969264b96
commit
b96ff61821
2 files changed
+64
-8
No files matched your search
@@ -1,12 +1,17 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Plus, Trash2 } from "lucide-react";
|
import { ChevronDown, ChevronRight, Plus, Trash2 } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Form } from "@/components/ui/form";
|
import { Form } from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import {
|
||||||
|
Collapsible,
|
||||||
|
CollapsibleContent,
|
||||||
|
CollapsibleTrigger,
|
||||||
|
} from "@/components/ui/collapsible";
|
||||||
import { HelpTip } from "@/components/ui/help-tip";
|
import { HelpTip } from "@/components/ui/help-tip";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@@ -192,6 +197,13 @@ export function KimiProviderForm({
|
|||||||
const { type: providerType, base_url: baseUrl = "" } = config.provider;
|
const { type: providerType, base_url: baseUrl = "" } = config.provider;
|
||||||
const apiKey = config.provider.api_key ?? "";
|
const apiKey = config.provider.api_key ?? "";
|
||||||
const headersKey = JSON.stringify(config.provider.custom_headers ?? {});
|
const headersKey = JSON.stringify(config.provider.custom_headers ?? {});
|
||||||
|
// 请求头很少用到,收在高级选项里;已经有值(编辑已有卡、选了带请求头的预设)时展开。
|
||||||
|
const hasCustomHeaders =
|
||||||
|
Object.keys(config.provider.custom_headers ?? {}).length > 0;
|
||||||
|
const [advancedExpanded, setAdvancedExpanded] = useState(hasCustomHeaders);
|
||||||
|
useEffect(() => {
|
||||||
|
if (hasCustomHeaders) setAdvancedExpanded(true);
|
||||||
|
}, [hasCustomHeaders]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFetchedModels((prev) => (prev.length === 0 ? prev : []));
|
setFetchedModels((prev) => (prev.length === 0 ? prev : []));
|
||||||
setIsFetchingModels(false);
|
setIsFetchingModels(false);
|
||||||
@@ -520,12 +532,35 @@ export function KimiProviderForm({
|
|||||||
partnerPromotionKey={preset?.partnerPromotionKey}
|
partnerPromotionKey={preset?.partnerPromotionKey}
|
||||||
required={!config.provider.oauth}
|
required={!config.provider.oauth}
|
||||||
/>
|
/>
|
||||||
<RequestHeadersEditor
|
<Collapsible
|
||||||
headers={config.provider.custom_headers ?? {}}
|
open={advancedExpanded}
|
||||||
onHeadersChange={(custom_headers) =>
|
onOpenChange={setAdvancedExpanded}
|
||||||
updateProvider({ custom_headers })
|
className="rounded-lg border border-border p-4"
|
||||||
}
|
>
|
||||||
/>
|
<CollapsibleTrigger asChild>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={null}
|
||||||
|
size="sm"
|
||||||
|
className="h-8 w-full justify-start gap-1.5 px-0 text-sm font-medium text-fg-1 hover:opacity-70"
|
||||||
|
>
|
||||||
|
{advancedExpanded ? (
|
||||||
|
<ChevronDown className="h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
)}
|
||||||
|
{t("providerForm.advancedOptionsToggle")}
|
||||||
|
</Button>
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
<CollapsibleContent className="pt-2">
|
||||||
|
<RequestHeadersEditor
|
||||||
|
headers={config.provider.custom_headers ?? {}}
|
||||||
|
onHeadersChange={(custom_headers) =>
|
||||||
|
updateProvider({ custom_headers })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</CollapsibleContent>
|
||||||
|
</Collapsible>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label>{t("kimi.models")}</Label>
|
<Label>{t("kimi.models")}</Label>
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
import {
|
||||||
|
cleanup,
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
waitFor,
|
||||||
|
} from "@testing-library/react";
|
||||||
import { beforeAll, describe, expect, it, vi } from "vitest";
|
import { beforeAll, describe, expect, it, vi } from "vitest";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { http, HttpResponse } from "msw";
|
import { http, HttpResponse } from "msw";
|
||||||
@@ -270,6 +276,21 @@ describe("KimiProviderForm", () => {
|
|||||||
expect(submit.mock.calls[0][0].providerKey).toBe("managed:kimi-code");
|
expect(submit.mock.calls[0][0].providerKey).toBe("managed:kimi-code");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps request headers in advanced options, open only when some are set", () => {
|
||||||
|
setup();
|
||||||
|
expect(screen.getByDisplayValue("X-Custom")).toBeInTheDocument();
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
const { custom_headers: _, ...provider } = config.provider;
|
||||||
|
setup(vi.fn(), undefined, { ...config, provider });
|
||||||
|
const toggle = screen.getByRole("button", {
|
||||||
|
name: "providerForm.advancedOptionsToggle",
|
||||||
|
});
|
||||||
|
expect(toggle).toHaveAttribute("aria-expanded", "false");
|
||||||
|
fireEvent.click(toggle);
|
||||||
|
expect(toggle).toHaveAttribute("aria-expanded", "true");
|
||||||
|
});
|
||||||
|
|
||||||
it("blocks invalid JSON until the editor is corrected", () => {
|
it("blocks invalid JSON until the editor is corrected", () => {
|
||||||
const submit = setup();
|
const submit = setup();
|
||||||
const editor = screen.getByLabelText("provider.configJson");
|
const editor = screen.getByLabelText("provider.configJson");
|
||||||
|
|||||||
Reference in new issue
Block a user