feat(site/src): nest agent pages under a Coder Agents subsection in AI nav (#26667)

Groups the agent-related AI settings pages under a new **Coder Agents**
parent in the sidebar, with a continuous left rule connecting the
children and an active-segment indicator that lights up the rule where
the current sub-item sits.

The new nav order:

- AI Governance
- AI Gateway keys
- Providers
- Coder Agents
  - Models
  - MCP servers
  - Templates
  - Spend
  - Instructions
  - Lifecycle

All target pages already exist on main (Danielle's recent migrations of
Models, MCP servers, Templates, Instructions, Lifecycle, Spend, and
Coder Agents into AI Settings). This PR only changes the sidebar visual
structure: the children move into an indented group with a `border-l
border-l-border` rule, and the active child paints a
`border-l-content-primary` segment over that rule via `-ml-px` so the
rule and indicator share a column instead of stacking.

<details>
<summary>Design notes</summary>

Concept 1 from the earlier exploration: always-expanded with indents,
the parent is its own page. Chosen because it adds no expand/collapse
state, no "which child is the default" question, and no animation work;
the parent reuses the existing nav-item, and the children sit in a
wrapper `div` with a left rule. The site bundle ships without Tailwind's
preflight, so the wrapper and sub-item borders are paired with
`border-solid` to actually paint, matching the pattern already in
`Sidebar.tsx`.

</details>

---

_This PR was prepared by Coder Agents on behalf of @tracyjohnsonux._
This commit is contained in:
TJ
2026-06-29 13:46:09 +00:00
committed by GitHub
parent 56373a09fc
commit 5f42bbcdbf
5 changed files with 130 additions and 36 deletions
+1 -1
View File
@@ -195,7 +195,7 @@ deployment. Use this to encode organizational conventions:
- Required review processes before merging.
- Any guardrails specific to your environment.
Configure the system prompt from **AI Settings** > **Instructions**
Configure the system prompt from **AI Settings** > **Coder Agents** > **Instructions**
or via the API at `PUT /api/experimental/chats/config/system-prompt`.
See [Platform Controls](./platform-controls/index.md) for details.
@@ -52,7 +52,7 @@ Administrators can set a system prompt that applies to all agent sessions. This
is useful for establishing organizational conventions: coding standards,
commit message formats, preferred libraries, or repository-specific context.
This setting is available under **AI Settings** > **Instructions** and is only accessible to administrators. Developers do not see or interact with it.
This setting is available under **AI Settings** > **Coder Agents** > **Instructions** and is only accessible to administrators. Developers do not see or interact with it.
### Plan mode instructions
@@ -61,7 +61,7 @@ enters plan mode. These instructions supplement the built-in planning behavior
and are useful for organization-specific planning requirements such as required
plan sections, approval checkpoints, or review workflows.
This setting is available under **AI Settings** > **Instructions**. Developers do not edit it directly.
This setting is available under **AI Settings** > **Coder Agents** > **Instructions**. Developers do not edit it directly.
The same value is exposed over the experimental chat configuration API:
@@ -4,12 +4,12 @@ Administrators can register external MCP servers that provide additional tools
for agent chat sessions. Configured servers are injected into or offered to
users during chat depending on the availability policy.
This is an admin-only feature accessible at **AI Settings** > **MCP servers**
This is an admin-only feature accessible at **AI Settings** > **Coder Agents** > **MCP servers**
(`/ai/settings/mcp-servers`).
## Add an MCP server
1. Navigate to **AI Settings** > **MCP servers**.
1. Navigate to **AI Settings** > **Coder Agents** > **MCP servers**.
1. Click **Add**.
1. Fill in the configuration fields described below.
1. Click **Save**.
@@ -0,0 +1,85 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import { reactRouterParameters } from "storybook-addon-remix-react-router";
import { MockNoPermissions, MockPermissions } from "#/testHelpers/entities";
import AISettingsSidebarView from "./AISettingsSidebarView";
const meta: Meta<typeof AISettingsSidebarView> = {
title: "modules/management/AISettingsSidebarView",
component: AISettingsSidebarView,
args: {
permissions: MockPermissions,
},
parameters: {
reactRouter: reactRouterParameters({
location: { path: "/ai/settings/coder-agents" },
routing: [
{ path: "/ai/settings/governance", useStoryElement: true },
{ path: "/ai/settings/gateway-keys", useStoryElement: true },
{ path: "/ai/settings/providers", useStoryElement: true },
{ path: "/ai/settings/coder-agents", useStoryElement: true },
{ path: "/ai/settings/models", useStoryElement: true },
{ path: "/ai/settings/mcp-servers", useStoryElement: true },
{ path: "/ai/settings/templates", useStoryElement: true },
{ path: "/ai/settings/spend", useStoryElement: true },
{ path: "/ai/settings/instructions", useStoryElement: true },
{ path: "/ai/settings/lifecycle", useStoryElement: true },
],
}),
},
};
export default meta;
type Story = StoryObj<typeof AISettingsSidebarView>;
export const CoderAgentsActive: Story = {};
export const ModelsActive: Story = {
parameters: {
reactRouter: reactRouterParameters({
location: { path: "/ai/settings/models" },
routing: [{ path: "/ai/settings/models", useStoryElement: true }],
}),
},
};
export const SpendActive: Story = {
parameters: {
reactRouter: reactRouterParameters({
location: { path: "/ai/settings/spend" },
routing: [{ path: "/ai/settings/spend", useStoryElement: true }],
}),
},
};
export const LifecycleActive: Story = {
parameters: {
reactRouter: reactRouterParameters({
location: { path: "/ai/settings/lifecycle" },
routing: [{ path: "/ai/settings/lifecycle", useStoryElement: true }],
}),
},
};
export const ProvidersActive: Story = {
parameters: {
reactRouter: reactRouterParameters({
location: { path: "/ai/settings/providers" },
routing: [{ path: "/ai/settings/providers", useStoryElement: true }],
}),
},
};
export const NoDeploymentConfig: Story = {
args: {
permissions: {
...MockPermissions,
editDeploymentConfig: false,
},
},
};
export const NoPermissions: Story = {
args: {
permissions: MockNoPermissions,
},
};
@@ -1,15 +1,36 @@
import type { FC } from "react";
import type { FC, ReactNode } from "react";
import { NavLink } from "react-router";
import {
Sidebar as BaseSidebar,
SettingsSidebarNavItem as SidebarNavItem,
} from "#/components/Sidebar/Sidebar";
import type { Permissions } from "#/modules/permissions";
import { cn } from "#/utils/cn";
interface AISettingsSidebarViewProps {
/** Site-wide permissions. */
permissions: Permissions;
}
const SubNavItem: FC<{ href: string; children?: ReactNode }> = ({
href,
children,
}) => (
<NavLink
to={href}
className={({ isActive }) =>
cn(
"relative -ml-px text-sm text-content-secondary no-underline font-medium py-2 pl-4 pr-3 transition-colors",
"border-0 border-solid border-l border-l-transparent hover:text-content-primary",
isActive &&
"border-l-content-primary font-semibold text-content-primary",
)
}
>
{children}
</NavLink>
);
const AISettingsSidebarView: FC<AISettingsSidebarViewProps> = ({
permissions,
}) => {
@@ -23,7 +44,7 @@ const AISettingsSidebarView: FC<AISettingsSidebarViewProps> = ({
)}
{permissions.viewAIGatewayKeys && (
<SidebarNavItem href="/ai/settings/gateway-keys">
AI Gateway Keys
AI Gateway keys
</SidebarNavItem>
)}
{permissions.viewAnyAIProvider && (
@@ -32,35 +53,23 @@ const AISettingsSidebarView: FC<AISettingsSidebarViewProps> = ({
</SidebarNavItem>
)}
{permissions.editDeploymentConfig && (
<SidebarNavItem href="/ai/settings/models">Models</SidebarNavItem>
)}
{permissions.editDeploymentConfig && (
<SidebarNavItem href="/ai/settings/spend">Spend</SidebarNavItem>
)}
{permissions.editDeploymentConfig && (
<SidebarNavItem href="/ai/settings/instructions">
Instructions
</SidebarNavItem>
)}
{permissions.editDeploymentConfig && (
<SidebarNavItem href="/ai/settings/coder-agents">
Coder Agents
</SidebarNavItem>
)}
{permissions.editDeploymentConfig && (
<SidebarNavItem href="/ai/settings/lifecycle">
Lifecycle
</SidebarNavItem>
)}
{permissions.editDeploymentConfig && (
<SidebarNavItem href="/ai/settings/templates">
Templates
</SidebarNavItem>
)}
{permissions.editDeploymentConfig && (
<SidebarNavItem href="/ai/settings/mcp-servers">
MCP servers
</SidebarNavItem>
<>
<SidebarNavItem href="/ai/settings/coder-agents">
Coder Agents
</SidebarNavItem>
<div className="flex flex-col gap-1 ml-3 border-0 border-solid border-l border-l-border">
<SubNavItem href="/ai/settings/models">Models</SubNavItem>
<SubNavItem href="/ai/settings/mcp-servers">
MCP servers
</SubNavItem>
<SubNavItem href="/ai/settings/templates">Templates</SubNavItem>
<SubNavItem href="/ai/settings/spend">Spend</SubNavItem>
<SubNavItem href="/ai/settings/instructions">
Instructions
</SubNavItem>
<SubNavItem href="/ai/settings/lifecycle">Lifecycle</SubNavItem>
</div>
</>
)}
</div>
</BaseSidebar>