diff --git a/apps/web/src/components/Settings/Common/SettingRow.tsx b/apps/web/src/components/Settings/Common/SettingRow.tsx index acf5b462b..bba51833d 100644 --- a/apps/web/src/components/Settings/Common/SettingRow.tsx +++ b/apps/web/src/components/Settings/Common/SettingRow.tsx @@ -3,6 +3,8 @@ import React from 'react'; +import { cn } from '@/components/Common/cn'; + interface SettingRowProps { /** Primary label for the setting. Omit when the section heading already names it. */ title?: React.ReactNode; @@ -18,12 +20,15 @@ interface SettingRowProps { className?: string; /** Reduces vertical padding for subordinate settings. */ density?: 'default' | 'compact'; + /** Places controls beside the label or in a full-width row below it. */ + layout?: 'inline' | 'stacked'; } /** * A single setting row inside a {@link SettingSection} card. Renders the - * title (and optional description) on the left and a control on the right. - * The row itself is borderless — dividers come from the parent section. + * title (and optional description) with its controls. Inline rows place the + * control on the right; stacked rows place it full-width below the text. The + * row itself is borderless — dividers come from the parent section. */ export const SettingRow: React.FC = ({ title, @@ -33,10 +38,18 @@ export const SettingRow: React.FC = ({ children, className = '', density = 'default', + layout = 'inline', }) => { + const stacked = layout === 'stacked'; + return (
{leading &&
{leading}
} @@ -59,7 +72,7 @@ export const SettingRow: React.FC = ({ )}
-
{children}
+
{children}
); }; diff --git a/apps/web/src/components/Settings/sections/ExternalAgentRuntimeSettings.test.tsx b/apps/web/src/components/Settings/sections/ExternalAgentRuntimeSettings.test.tsx index 53d57082b..0c5bcbcb8 100644 --- a/apps/web/src/components/Settings/sections/ExternalAgentRuntimeSettings.test.tsx +++ b/apps/web/src/components/Settings/sections/ExternalAgentRuntimeSettings.test.tsx @@ -78,6 +78,35 @@ afterEach(() => { }); describe('ExternalAgentRuntimeSettings', () => { + it('places responsive token controls below the full-width description', async () => { + await act(async () => { + root.render(); + }); + + const tokenInput = container.querySelector( + '#agentlet-connection-token', + ); + if (!tokenInput) throw new Error('Connection token input not found'); + + const controls = tokenInput.parentElement; + const controlRow = controls?.parentElement; + const settingRow = controlRow?.parentElement; + expect(settingRow?.classList.contains('flex-col')).toBe(true); + expect(settingRow?.classList.contains('items-stretch')).toBe(true); + expect(controlRow?.classList.contains('w-full')).toBe(true); + expect(controls?.classList.contains('flex-wrap')).toBe(true); + expect(controls?.classList.contains('w-full')).toBe(true); + expect(tokenInput.classList.contains('flex-1')).toBe(true); + expect(tokenInput.classList.contains('basis-56')).toBe(true); + + const buttons = controls?.querySelectorAll('button') ?? []; + expect(buttons.length).toBeGreaterThan(0); + for (const button of buttons) { + expect(button.classList.contains('shrink-0')).toBe(true); + expect(button.classList.contains('whitespace-nowrap')).toBe(true); + } + }); + it('preserves the process limit when saving the idle timeout', async () => { await act(async () => { root.render(); diff --git a/apps/web/src/components/Settings/sections/ExternalAgentRuntimeSettings.tsx b/apps/web/src/components/Settings/sections/ExternalAgentRuntimeSettings.tsx index aa0aabfce..6715b2e2e 100644 --- a/apps/web/src/components/Settings/sections/ExternalAgentRuntimeSettings.tsx +++ b/apps/web/src/components/Settings/sections/ExternalAgentRuntimeSettings.tsx @@ -240,6 +240,7 @@ export function ExternalAgentRuntimeSettings() { return ( <> {t('settings.agentletConnectionToken')} @@ -265,10 +266,10 @@ export function ExternalAgentRuntimeSettings() { : t('settings.agentletConnectionTokenDescriptionLoading') } > -
+
setTokenInput(event.target.value)} @@ -287,6 +288,7 @@ export function ExternalAgentRuntimeSettings() { variant="outline" tone="info" size="sm" + className="shrink-0 whitespace-nowrap" onClick={() => void saveConnectionToken()} disabled={ !tokenInput.trim() || @@ -301,6 +303,7 @@ export function ExternalAgentRuntimeSettings() {