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:
Jason committed 2026-10-23 15:15:19 +08:00
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>
+22 -1
View File
@@ -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");