From cec1fa04d77c853630ae37709a4856af25e248fc Mon Sep 17 00:00:00 2001 From: m4 Date: Tue, 21 Jul 2026 22:59:11 +0800 Subject: [PATCH] feat: left-right layout for the Registry Editor With multiple providers configured, the vertical stack required long scrolling. The left pane now lists providers with enabled markers and per-model availability dots plus the template picker; the right pane shows the selected provider's editor, auto-selecting newly added providers and clamping selection on remove. Co-Authored-By: Claude Opus 4.7 --- src/app/components/ConfigDialog.tsx | 2 +- src/app/components/RegistryEditor.tsx | 261 ++++++++++++++++++-------- 2 files changed, 181 insertions(+), 82 deletions(-) diff --git a/src/app/components/ConfigDialog.tsx b/src/app/components/ConfigDialog.tsx index 6177aa8..583fde0 100644 --- a/src/app/components/ConfigDialog.tsx +++ b/src/app/components/ConfigDialog.tsx @@ -27,7 +27,7 @@ export function ConfigDialog({ open, onOpenChange }: ConfigDialogProps) { open={open} onOpenChange={onOpenChange} > - + Model registry diff --git a/src/app/components/RegistryEditor.tsx b/src/app/components/RegistryEditor.tsx index 19d7fe1..a11e91b 100644 --- a/src/app/components/RegistryEditor.tsx +++ b/src/app/components/RegistryEditor.tsx @@ -159,6 +159,32 @@ function AvailabilityBadge({ ); } +/** Compact per-model status dot for the provider list (left pane). */ +function ModelStatusDot({ + data, + ref_, +}: { + data: GetModelRegistryResponse; + ref_: ModelRef; +}) { + const status = availabilityFor(data, ref_); + const state = status?.state ?? null; + const color = + state === null + ? "bg-muted-foreground/40" + : state === "enabled" || state === "verified" + ? "bg-emerald-600" + : state === "verification_failed" + ? "bg-destructive" + : "bg-[var(--color-warning)]"; + return ( + + ); +} + function ModelEditor({ data, spec, @@ -276,7 +302,7 @@ function ModelEditor({ /> -
+
setSelectedTemplate(event.target.value)} + aria-label="Provider template" + > + {PROVIDER_TEMPLATES.map((template) => ( + + ))} + + + +

+ Templates only prefill the adapter, Base URL, and suggested + models; a saved provider is an ordinary profile. +

+
+
-
- - - - Templates only prefill the adapter, Base URL, and suggested models; - a saved provider is an ordinary profile. - +
+ {selectedProvider === null ? ( +

+ Select a provider on the left, or add one from a template. +

+ ) : ( + + setCredentialWrites((current) => { + const next = new Map(current); + if (secret) next.set(credentialId, secret); + else next.delete(credentialId); + return next; + }) + } + onChange={(next) => + setDraft({ + ...draft, + providers: draft.providers.map((current, i) => + i === selectedIndex ? next : current + ), + }) + } + onRemove={() => { + setDraft({ + ...draft, + providers: draft.providers.filter((_, i) => i !== selectedIndex), + }); + setSelectedProviderIndex((current) => + selectedIndex < current + ? current - 1 + : Math.max(0, selectedIndex - 1) + ); + }} + onTestModel={(ref) => void testModel(ref)} + /> + )} +
{saveIssues && saveIssues.details.length > 0 && (