diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json index 8c9420bada..69e8fbf2bc 100644 --- a/config/quality/file-size-baseline.json +++ b/config/quality/file-size-baseline.json @@ -299,7 +299,7 @@ "tests/unit/provider-models-route.test.ts": 1752, "tests/unit/provider-validation-specialty.test.ts": 2874, "_rebaseline_pr4613_compatible_provider_groups": "Reconcile #4613 already-merged growth: providers-page-utils.test.ts 1004->1052 (+48, buildCompatibleProviderGroups partition unit test). Fast-gate PR->release does not run check:file-size, so this surfaced post-merge.", - "tests/unit/providers-page-utils.test.ts": 1092, + "tests/unit/providers-page-utils.test.ts": 1109, "tests/unit/reasoning-cache.test.ts": 980, "tests/unit/route-edge-coverage.test.ts": 1234, "tests/unit/search-handler-extended.test.ts": 1124, diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleNodeCard.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleNodeCard.tsx index 1b01f2eb7d..5347c075a8 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleNodeCard.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleNodeCard.tsx @@ -3,6 +3,7 @@ // Phase 1t.2 extraction — Issue #3501 import { useRouter } from "next/navigation"; import { Card, Button } from "@/shared/components"; +import ProviderIcon from "@/shared/components/ProviderIcon"; import { getApiLabel, getApiPath } from "../providerPageHelpers"; import type { ProviderMessageTranslator } from "../providerPageHelpers"; @@ -11,6 +12,8 @@ interface ProviderNode { apiType?: string; chatPath?: string; prefix?: string; + /** Optional operator-supplied remote icon URL (#2166). */ + iconUrl?: string; [key: string]: unknown; } @@ -42,24 +45,35 @@ export default function CompatibleNodeCard({ return (
-
-

- {isCcCompatible - ? t("ccCompatibleDetailsTitle") - : isAnthropicCompatible - ? t("anthropicCompatibleDetails") - : t("openaiCompatibleDetails")} -

-

- {getApiLabel(t, isAnthropicProtocolCompatible, providerNode?.apiType)} ·{" "} - {(providerNode.baseUrl || "").replace(/\/$/, "")}/ - {getApiPath( - isCcCompatible, - isAnthropicCompatible, - providerNode?.apiType, - providerNode?.chatPath - )} -

+
+ {providerNode.iconUrl && ( + + )} +
+

+ {isCcCompatible + ? t("ccCompatibleDetailsTitle") + : isAnthropicCompatible + ? t("anthropicCompatibleDetails") + : t("openaiCompatibleDetails")} +

+

+ {getApiLabel(t, isAnthropicProtocolCompatible, providerNode?.apiType)} ·{" "} + {(providerNode.baseUrl || "").replace(/\/$/, "")}/ + {getApiPath( + isCcCompatible, + isAnthropicCompatible, + providerNode?.apiType, + providerNode?.chatPath + )} +

+
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditCompatibleNodeModal.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditCompatibleNodeModal.tsx index 049797e4df..ec0f1fcdf7 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditCompatibleNodeModal.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditCompatibleNodeModal.tsx @@ -11,6 +11,7 @@ interface EditCompatibleNodeModalNode { baseUrl?: string; chatPath?: string; modelsPath?: string; + iconUrl?: string; } interface EditCompatibleNodeModalProps { @@ -38,6 +39,7 @@ export default function EditCompatibleNodeModal({ baseUrl: "https://api.openai.com/v1", chatPath: "", modelsPath: "", + iconUrl: "", }); const [saving, setSaving] = useState(false); const [checkKey, setCheckKey] = useState(""); @@ -63,6 +65,7 @@ export default function EditCompatibleNodeModal({ : "https://api.openai.com/v1"), chatPath: node.chatPath || (isCcCompatible ? CC_COMPATIBLE_DEFAULT_CHAT_PATH : ""), modelsPath: isCcCompatible ? "" : node.modelsPath || "", + iconUrl: node.iconUrl || "", }); setShowAdvanced( !!( @@ -93,6 +96,7 @@ export default function EditCompatibleNodeModal({ baseUrl: formData.baseUrl, chatPath: formData.chatPath || (isCcCompatible ? CC_COMPATIBLE_DEFAULT_CHAT_PATH : ""), modelsPath: isCcCompatible ? "" : formData.modelsPath, + iconUrl: formData.iconUrl.trim(), }; if (!isAnthropic) { payload.apiType = formData.apiType; @@ -208,6 +212,13 @@ export default function EditCompatibleNodeModal({ }) } /> + setFormData({ ...formData, iconUrl: e.target.value })} + placeholder="https://example.com/logo.png" + hint={t("iconUrlHint")} + />