diff --git a/apps/web/AGENTS.md b/apps/web/AGENTS.md index 1bfad55338..cf271c7f2c 100644 --- a/apps/web/AGENTS.md +++ b/apps/web/AGENTS.md @@ -136,11 +136,8 @@ surface. name the target only (for example, `Bitbucket Pull Request`) and preserve their registered provider icon. - **Interactivity:** all buttons and links with actions must have `cursor-pointer` class. -- **Self-documenting settings:** every setting must explain in visible, plain-language copy what - changes, when the setting applies, and when the user should choose each non-obvious option. State - important exclusions, precedence, cost, or destructive consequences next to the control when they - can affect the decision. Do not rely on tooltips, external documentation, or implementation terms - alone to teach the setting. +- **Self-documenting settings:** give each setting a short, plain-language description of its effect. Use `SettingsInfo` for optional scope and implementation details: hover/focus on desktop, a drawer on touch devices. Keep active errors, permissions, managed values, and essential input constraints visible. +- **Settings composition:** use `SettingsGroup` for bordered groups and `SettingsRow` for simple preferences; keep one domain owner/save contributor and attach discovery to actual controls. Keep sections expanded and use existing header tabs for larger pages. Preserve specialized editor/table/diagnostic/credential layouts. - **Settings save coordination:** settings surfaces with local unsaved state must register a contributor with `useSettingsSaveContributor` (or use `SettingsPageTemplate`) so the shared floating **Save changes** control, navigation guard, and discard flow own persistence. Do not add diff --git a/apps/web/app/settings/agents/page.tsx b/apps/web/app/settings/agents/page.tsx index b48a0bbd75..151c11bc02 100644 --- a/apps/web/app/settings/agents/page.tsx +++ b/apps/web/app/settings/agents/page.tsx @@ -3,6 +3,7 @@ import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { settingsActionClassName } from "@/components/settings/settings-control"; +import { SettingsGroup } from "@/components/settings/settings-group"; import Link from "@/components/routing/app-link"; import { IconAlertTriangle, @@ -93,47 +94,41 @@ function InstalledAgentsHeader({ }) { const { t } = useTranslation(); return ( -
{t("agents:installedAgentsDescription")}
-{t("settings:agentGeneratedTaskTitlesDescription")}
+{t("settings:agentGeneratedTaskTitlesDisabledHint")}
+ + } + controlId="agent-generated-task-titles" + touchTarget="switch" + discoveryTargetId={GENERAL_SETTINGS_TARGETS.agentGeneratedTitles} + isDirty={isDirty} + control={ +- {t("settings:agentGeneratedTaskTitlesDisabledHint")} -
-- {t("settings:animateRichOutputChartsDescription")} -
-{description}
} +{t("settings:settingsMenuShapePerDevice")}
+ ); } diff --git a/apps/web/components/settings/settings-page-template.tsx b/apps/web/components/settings/settings-page-template.tsx index 80f746a47a..b1e21110a5 100644 --- a/apps/web/components/settings/settings-page-template.tsx +++ b/apps/web/components/settings/settings-page-template.tsx @@ -32,6 +32,7 @@ type SettingsPageTemplateProps = { onDiscard?: () => void; showSaveButton?: boolean; showPageChrome?: boolean; + contentFrame?: "card" | "none"; children: React.ReactNode; deleteSection?: React.ReactNode; }; @@ -50,6 +51,7 @@ export function SettingsPageTemplate({ onDiscard, showSaveButton = true, showPageChrome = true, + contentFrame = "card", children, deleteSection, }: SettingsPageTemplateProps) { @@ -72,11 +74,17 @@ export function SettingsPageTemplate({ {showPageChrome &&{description}
} -{t("settings:sleepInhibitionDescription")}
+{t("settings:sleepInhibitionSwitchHint")}
+{t("settings:sleepInhibitionCaveat")}
++ {t("settings:sleepInhibitionSwitchHint")} +
++ {t("settings:sleepInhibitionStatusLabel")}: {t(statusMessageKey(snapshot))} +
+{t("settings:sleepInhibitionAdminOnly")}
+ )} + {state.saveFailed && ( +- {t("settings:sleepInhibitionSwitchHint")} -
-- {t("settings:sleepInhibitionStatusLabel")}: {t(statusMessageKey(snapshot))} -
-- {t("settings:sleepInhibitionCaveat")} -
-{t("settings:sleepInhibitionAdminOnly")}
- )} - {state.saveFailed && ( -- {t("settings:usefulWhenKandevIsSelfHosted")} -
-+ {t("settings:usefulWhenKandevIsSelfHosted")} +
+- {t("system:licensesPageDescription")} -
-{description}
- )} -{t("system:messageQueueUnlimitedHelp")}
-{t("system:messageQueueLimitDescription")}
+{t("system:messageQueueUnlimitedHelp")}
+{t("system:messageQueueEffectiveHelp")}
+ + } + control={ + onDraftChange(event.target.value)} + className={settingsControlClassName("w-full md:w-40")} + /> + } + /> +- {t("system:messageQueueEffectiveHelp")} -
- {t("system:messageQueueMergeDescription")} -
-{t("system:messageQueueMergeDescription")}
+{t("system:messageQueueMergeNotice")}
+ + } + control={{t("system:messageQueueMergeNotice")}
-- {t("system:messageQueueAutoMergeDescription")} -
-{t("system:messageQueueAutoMergeNotice")}
-{t("system:messageQueueAutoMergeDescription")}
+{t("system:messageQueueAutoMergeNotice")}
+ + } + control={ +{t("system:messageQueueLimitDescription")}
+ const content = ( + <> + {withinGroup ? ( ++ {t("system:messageQueueLimitDescription")} +
+ )}+ {state.invalidReason} +
+ )} {state.isLocked && (- {t("system:sessionCapacityEnabledDescription")} -
-{t("system:sessionCapacityEnabledDescription")}
+{t("system:sessionCapacityBehaviorHelp")}
+ + } + control={- {t("system:sessionCapacityMaximumHelp")} -
{error && ({error} @@ -148,7 +163,7 @@ function EffectiveCapacity({ const { t } = useTranslation(); const current = enabled ? String(maximum) : t("system:sessionCapacityNoLimit"); return ( -
{t("system:sessionCapacityScope")}
- {t("system:sessionCapacityLimitDescription")} -
-{t("system:sessionCapacityBehaviorHelp")}
-{t("system:sessionCapacityAdminOnly")}
@@ -272,6 +295,40 @@ export function SessionCapacitySettings() { )}+ {t("settings:pinTheAgentsLiveTodoChecklistAs")} +
++ {t("settings:onlyPinWhenTodoListIsNotEmptyDescription")} +
+- {t("settings:pinTheAgentsLiveTodoChecklistAs")} -
-- {t("settings:onlyPinWhenTodoListIsNotEmptyDescription")} -
-{t("common:loading")}
} - {!units.loading && units.units.length === 0 && ( -{t("settings:unitsEmpty")}
- )} - {units.units.map((unit) => ( -{t("common:loading")}
} + {!units.loading && units.units.length === 0 && ( +{t("settings:unitsEmpty")}
+ )} + {units.units.map((unit) => ( +- {t("settings:markMessagesThatArrivedWhileA")} -
-- {t("settings:utilityDefaultModelDescription")} -
+- {t("settings:utilityActionsDescription")} -
+- {t("settings:utilityCustomAgentsDescription")} -
+{t("settings:utilityCustomAgentsEmpty")} @@ -286,7 +276,7 @@ export function CustomAgentsSection({ ))}