From 6890e22017b41913c002b1fd9c16e147553961e0 Mon Sep 17 00:00:00 2001 From: Tracy Johnson Date: Tue, 25 Aug 2026 14:56:07 +0000 Subject: [PATCH 1/6] feat(site): show disabled state on AI provider rows Move the enabled/disabled state out of the status column: disabled providers now show a Disabled badge beside the name with a muted avatar and disabled text for the name and base URL. The status column keeps only the issue badges. Replace the native title tooltip on the Not supported in Agents badge with the shared Tooltip component so it matches the warning tooltip, and constrain tooltip width so long warnings wrap. --- .../components/ProviderRow.stories.tsx | 41 +++++++++++-- .../ProvidersPage/components/ProviderRow.tsx | 57 +++++++++++++++---- 2 files changed, 82 insertions(+), 16 deletions(-) diff --git a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx index 314fb2216edc7..ca82827cb3abe 100644 --- a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx +++ b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx @@ -29,7 +29,7 @@ const meta: Meta = { Name Base URL - Enabled + Status Open provider @@ -82,11 +82,44 @@ export const NotSupportedInAgents: Story = { args: { provider: MockAIProviderCopilot, }, + play: async ({ canvasElement, args }) => { + const canvas = within(canvasElement); + const badge = canvas.getByText("Not supported in Agents"); + await expect(badge).toBeInTheDocument(); + + // Hover shows the explanation in the shared tooltip, matching the + // warning badge instead of the native title tooltip. + await userEvent.hover(badge); + const tooltip = await within(document.body).findByRole("tooltip"); + await expect(tooltip).toHaveTextContent(/AI Gateway proxy/); + + // Activation must not navigate the row. + badge.focus(); + await userEvent.keyboard("{Enter}"); + await userEvent.keyboard(" "); + await userEvent.click(badge); + await expect(args.onClick).not.toHaveBeenCalled(); + }, +}; + +export const Disabled: Story = { + args: { + provider: { ...MockAIProviderOpenAI, enabled: false }, + }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await expect( - canvas.getByText("Not supported in Agents"), - ).toBeInTheDocument(); + await expect(canvas.getByText("Disabled")).toBeInTheDocument(); + await expect(canvas.getByText("OpenAI")).toBeInTheDocument(); + }, +}; + +export const Enabled: Story = { + args: { + provider: { ...MockAIProviderOpenAI, enabled: true }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await expect(canvas.queryByText("Disabled")).not.toBeInTheDocument(); }, }; diff --git a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx index aa71dc2241095..393ff63c40979 100644 --- a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx +++ b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx @@ -13,6 +13,7 @@ import { TooltipTrigger, } from "#/components/Tooltip/Tooltip"; import { useClickableTableRow } from "#/hooks/useClickableTableRow"; +import { cn } from "#/utils/cn"; import { ProviderIcon } from "./ProviderIcon"; import { getProviderDisplayType } from "./providerFormApiMap"; @@ -29,6 +30,7 @@ export const ProviderRow: React.FC = ({ onClick: () => onClick?.(), }); const displayName = provider.display_name || provider.name; + const disabled = !provider.enabled; // Stop activation from bubbling to a parent `useClickableTableRow` // row, which navigates on click, Enter (onKeyDown), and Space @@ -42,11 +44,27 @@ export const ProviderRow: React.FC = ({ + + {displayName} + + {disabled && ( + + Disabled + + )} + + } avatar={ = ({ {provider.base_url} @@ -66,14 +87,24 @@ export const ProviderRow: React.FC = ({
- {provider.enabled && Enabled} {AgentsUnsupportedProviderTypes.some((t) => t === provider.type) && ( - - Not supported in Agents - + + + + Not supported in Agents + + + + This provider works with the AI Gateway proxy but Coder Agents + can't use it. + + )} {provider.status?.warnings && provider.status.warnings.length > 0 && ( @@ -89,9 +120,11 @@ export const ProviderRow: React.FC = ({ Warning - + {provider.status.warnings.map((warning) => ( -

{warning}

+

+ {warning} +

))}
From 8a5a86b8218c8ab7990f6910235bdb345539992f Mon Sep 17 00:00:00 2001 From: Tracy Johnson Date: Tue, 25 Aug 2026 15:13:16 +0000 Subject: [PATCH 2/6] test(site): fold redundant Enabled story into SupportedHasNoAgentsLabel --- .../ProvidersPage/components/ProviderRow.stories.tsx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx index ca82827cb3abe..0e950e2c0e3a3 100644 --- a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx +++ b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx @@ -113,16 +113,6 @@ export const Disabled: Story = { }, }; -export const Enabled: Story = { - args: { - provider: { ...MockAIProviderOpenAI, enabled: true }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await expect(canvas.queryByText("Disabled")).not.toBeInTheDocument(); - }, -}; - export const SupportedHasNoAgentsLabel: Story = { args: { provider: { ...MockAIProviderOpenAI, enabled: true }, @@ -133,6 +123,7 @@ export const SupportedHasNoAgentsLabel: Story = { await expect( canvas.queryByText("Not supported in Agents"), ).not.toBeInTheDocument(); + await expect(canvas.queryByText("Disabled")).not.toBeInTheDocument(); }, }; From 40dd07ca756177b2986a7287771eb037e98daab5 Mon Sep 17 00:00:00 2001 From: Tracy Johnson Date: Tue, 25 Aug 2026 16:20:19 +0000 Subject: [PATCH 3/6] feat(site): hide status column header on AI providers table --- .../pages/AISettingsPage/ProvidersPage/ProvidersPageView.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site/src/pages/AISettingsPage/ProvidersPage/ProvidersPageView.tsx b/site/src/pages/AISettingsPage/ProvidersPage/ProvidersPageView.tsx index 10ac4c75ca736..a4c63e65bee08 100644 --- a/site/src/pages/AISettingsPage/ProvidersPage/ProvidersPageView.tsx +++ b/site/src/pages/AISettingsPage/ProvidersPage/ProvidersPageView.tsx @@ -103,7 +103,9 @@ const ProvidersPageView: React.FC = ({ Name Base URL - Status + + Status + From 503f89616eb23be68b72ed64eccdd21d0cbf6379 Mon Sep 17 00:00:00 2001 From: Tracy Johnson Date: Tue, 25 Aug 2026 16:37:09 +0000 Subject: [PATCH 4/6] feat(site): use secondary text for disabled AI provider names --- .../AISettingsPage/ProvidersPage/components/ProviderRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx index 393ff63c40979..b6819740526e5 100644 --- a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx +++ b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx @@ -47,7 +47,7 @@ export const ProviderRow: React.FC = ({ title={ {displayName} From 7e901eb507a2b949a9dbe704ee40078168ca4081 Mon Sep 17 00:00:00 2001 From: Tracy Johnson Date: Tue, 25 Aug 2026 17:23:31 +0000 Subject: [PATCH 5/6] chore(site): drop redundant comment in ProviderRow story --- .../ProvidersPage/components/ProviderRow.stories.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx index 0e950e2c0e3a3..5fa5080abd3e9 100644 --- a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx +++ b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx @@ -87,8 +87,6 @@ export const NotSupportedInAgents: Story = { const badge = canvas.getByText("Not supported in Agents"); await expect(badge).toBeInTheDocument(); - // Hover shows the explanation in the shared tooltip, matching the - // warning badge instead of the native title tooltip. await userEvent.hover(badge); const tooltip = await within(document.body).findByRole("tooltip"); await expect(tooltip).toHaveTextContent(/AI Gateway proxy/); From a5cc7344a5835c3b225ff4190ab6a27961a94e68 Mon Sep 17 00:00:00 2001 From: Tracy Johnson Date: Tue, 25 Aug 2026 18:01:01 +0000 Subject: [PATCH 6/6] fix(site): use semantic buttons for provider status tooltip badges Render the tooltip trigger badges as native buttons via Badge asChild so they expose a control role to assistive technology instead of a bare focusable div, and render the Disabled badge as a span to avoid a div inside AvatarData's inline title. --- .../components/ProviderRow.stories.tsx | 10 +++++----- .../ProvidersPage/components/ProviderRow.tsx | 18 +++++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx index 5fa5080abd3e9..73e5bccca585d 100644 --- a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx +++ b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.stories.tsx @@ -84,7 +84,9 @@ export const NotSupportedInAgents: Story = { }, play: async ({ canvasElement, args }) => { const canvas = within(canvasElement); - const badge = canvas.getByText("Not supported in Agents"); + const badge = canvas.getByRole("button", { + name: "Not supported in Agents", + }); await expect(badge).toBeInTheDocument(); await userEvent.hover(badge); @@ -139,13 +141,11 @@ export const WithHostnameCollisionWarning: Story = { }, play: async ({ canvasElement, args }) => { const canvas = within(canvasElement); - const badge = canvas.getByText(/warning/i); + const badge = canvas.getByRole("button", { name: /^Warning:/ }); await expect(badge).toBeInTheDocument(); - await expect(badge).toHaveAttribute( - "aria-label", + await expect(badge).toHaveAccessibleName( expect.stringContaining("api.openai.com"), ); - await expect(badge).toHaveAttribute("tabIndex", "0"); // Hover shows the tooltip with the warning text. await userEvent.hover(badge); diff --git a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx index b6819740526e5..51c0696ab4a29 100644 --- a/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx +++ b/site/src/pages/AISettingsPage/ProvidersPage/components/ProviderRow.tsx @@ -52,8 +52,8 @@ export const ProviderRow: React.FC = ({ {displayName} {disabled && ( - - Disabled + + Disabled )} @@ -91,13 +91,13 @@ export const ProviderRow: React.FC = ({ - Not supported in Agents + @@ -110,14 +110,18 @@ export const ProviderRow: React.FC = ({ - Warning +