From 0a8226c3db521cf9141a0c23ef43cbeadb568060 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 19 Aug 2026 22:31:35 -0700 Subject: [PATCH] fix(ui): give embedded settings sections the shared section rhythm (#126541) * fix(ui): give embedded settings sections the shared section rhythm The agents tab panel hosted settings sections in a bare div, so sections stacked with zero separation; the bespoke .agents-main margin rule in agents.css missed the nested tabpanel entirely. Descriptions also pulled up to 4px under control-height header actions (squeezed Verify/Save rows). Add a .settings-stack primitive to settings.css for embedded surfaces, use it on the agent tab panel, delete the page-local margin fork, and let section descriptions clear action-bearing headers. * test(agents): export getRuntimeConfigSourceSnapshot from runtime-snapshot mock Main's checks-node-compact-large shard is red: #126531 routed provider-model-routes through projectConfigOntoRuntimeSourceSnapshot, which reads getRuntimeConfigSourceSnapshot, and this suite's explicit vi.mock factory did not export it (24 failures). Return null so the projection no-ops and resolvers keep reading the provided config. --- .../agent-command.live-model-switch.test.ts | 3 +++ ui/docs/design-system/settings-design.md | 1 + ui/src/pages/agents/view.ts | 1 + ui/src/styles/agents.css | 6 ------ ui/src/styles/settings.css | 15 +++++++++++++++ 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/agents/agent-command.live-model-switch.test.ts b/src/agents/agent-command.live-model-switch.test.ts index 0f5d68f02e1e..db5acdd74c98 100644 --- a/src/agents/agent-command.live-model-switch.test.ts +++ b/src/agents/agent-command.live-model-switch.test.ts @@ -397,6 +397,9 @@ vi.mock("../skills/discovery/chat-commands.runtime.js", () => ({ vi.mock("../config/runtime-snapshot.js", () => ({ getRuntimeConfigSnapshot: () => state.runtimeConfigMock ?? state.defaultRuntimeConfig, + // No source snapshot: runtime-source projection no-ops and resolvers read the + // provided config directly, matching this suite's pre-projection world. + getRuntimeConfigSourceSnapshot: () => null, registerRuntimeConfigSnapshotPreparer: vi.fn(), setRuntimeConfigSnapshot: vi.fn(), })); diff --git a/ui/docs/design-system/settings-design.md b/ui/docs/design-system/settings-design.md index a3aa0381ff20..ee8ee32f8f2a 100644 --- a/ui/docs/design-system/settings-design.md +++ b/ui/docs/design-system/settings-design.md @@ -14,6 +14,7 @@ Every settings surface (the `/settings` takeover pages plus the Plugins/Skills h ``` - **Sections are typography, not chrome.** Grouping comes from whitespace + a small uppercase heading — never a card header. +- **Embedded surfaces keep the rhythm.** A container that hosts sections without `.settings-page`'s centered column (tab panels, split layouts) takes `.settings-stack`; never re-space sections with page-local margin rules. - **Exactly one level of elevation.** A group never contains another card, callout, or bordered box. Nested detail uses `.settings-subrows` (indented rows), a stacked row, or a drill-in nav row. - **Row anatomy:** left is title (`--control-ui-text-md`, weight 500) over an optional one-line description (muted, sm). Right is exactly one control: toggle, select, segmented, button, plain value, or chevron (nav). Wide editors use the `stacked` variant. - **Lists are rows too.** An entity list (plugin, device, session) is a group whose rows carry an action cluster in the control slot — same anatomy as a toggle row. diff --git a/ui/src/pages/agents/view.ts b/ui/src/pages/agents/view.ts index 094475b84a8c..10f45d7e09d4 100644 --- a/ui/src/pages/agents/view.ts +++ b/ui/src/pages/agents/view.ts @@ -330,6 +330,7 @@ export function renderAgents(props: AgentsProps) { )}
diff --git a/ui/src/styles/agents.css b/ui/src/styles/agents.css index 28c881db519e..ffdb2c1a765a 100644 --- a/ui/src/styles/agents.css +++ b/ui/src/styles/agents.css @@ -15,9 +15,3 @@ .agents-panel-body + .settings-kv { border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent); } - -/* .agents-main is a 14px-gap grid (components.css); settings sections need - more whitespace between each other than panel chrome does. */ -.agents-main > .settings-section:not(:first-child) { - margin-top: var(--space-4); -} diff --git a/ui/src/styles/settings.css b/ui/src/styles/settings.css index 8f7d700c7b72..879ca44fd1e3 100644 --- a/ui/src/styles/settings.css +++ b/ui/src/styles/settings.css @@ -87,6 +87,15 @@ max-width: 1120px; } +/* Section rhythm for embedded surfaces (tab panels, split layouts) that host + settings sections without .settings-page's centered column. Sections crammed + into a plain wrapper collapse to zero separation. */ +.settings-stack { + display: flex; + flex-direction: column; + gap: var(--space-8); +} + .settings-page__intro { margin: 0; font-size: var(--control-ui-text-sm); @@ -130,6 +139,12 @@ color: var(--muted); } +/* Control-height header actions overhang the one-line heading; keep the + description's negative pull from squeezing it against the button row. */ +.settings-section__header:has(.settings-section__actions) + .settings-section__desc { + margin-top: 0; +} + .settings-section__actions { display: flex; align-items: center;