-
+
{t(I18nKey.ONBOARDING$BACKEND_TITLE)}
diff --git a/src/components/features/onboarding/steps/choose-agent-step.tsx b/src/components/features/onboarding/steps/choose-agent-step.tsx
index 313dcb585..d9452df81 100644
--- a/src/components/features/onboarding/steps/choose-agent-step.tsx
+++ b/src/components/features/onboarding/steps/choose-agent-step.tsx
@@ -187,7 +187,7 @@ export function ChooseAgentStep({
className="flex flex-col gap-6"
>
-
+
{t(I18nKey.ONBOARDING$AGENT_TITLE)}
@@ -221,7 +221,7 @@ export function ChooseAgentStep({
diff --git a/src/components/features/onboarding/steps/say-hello-step.tsx b/src/components/features/onboarding/steps/say-hello-step.tsx
index fc68f65d5..7688cb54b 100644
--- a/src/components/features/onboarding/steps/say-hello-step.tsx
+++ b/src/components/features/onboarding/steps/say-hello-step.tsx
@@ -69,7 +69,7 @@ export function SayHelloStep({ onBack, onLaunched }: SayHelloStepProps) {
className="flex flex-col gap-6"
>
-
+
{t(I18nKey.ONBOARDING$HELLO_TITLE)}
diff --git a/src/components/features/onboarding/steps/setup-llm-step.tsx b/src/components/features/onboarding/steps/setup-llm-step.tsx
index fb403c8b1..3c47677cd 100644
--- a/src/components/features/onboarding/steps/setup-llm-step.tsx
+++ b/src/components/features/onboarding/steps/setup-llm-step.tsx
@@ -107,7 +107,7 @@ export function SetupLlmStep({ onBack, onNext }: SetupLlmStepProps) {
className="flex flex-col gap-6 max-h-[calc(90vh-7rem)]"
>
-
+
{t(I18nKey.ONBOARDING$LLM_TITLE)}
diff --git a/src/components/features/settings/api-key-modal-base.tsx b/src/components/features/settings/api-key-modal-base.tsx
index 584d9e2e6..0617b149a 100644
--- a/src/components/features/settings/api-key-modal-base.tsx
+++ b/src/components/features/settings/api-key-modal-base.tsx
@@ -92,7 +92,11 @@ export function ApiKeyModalBase({
MODAL_MAX_WIDTH_VIEWPORT,
)}
>
-
+
{children}
{footer}
diff --git a/src/components/features/settings/brand-button.tsx b/src/components/features/settings/brand-button.tsx
index 48ddaff67..2fced8705 100644
--- a/src/components/features/settings/brand-button.tsx
+++ b/src/components/features/settings/brand-button.tsx
@@ -1,5 +1,6 @@
import { forwardRef } from "react";
import { cn } from "#/utils/utils";
+import { formControlButtonClassName } from "#/utils/form-control-classes";
interface BrandButtonProps {
testId?: string;
@@ -48,16 +49,16 @@ export const BrandButton = forwardRef<
aria-label={ariaLabel}
aria-busy={ariaBusy}
className={cn(
- "w-fit p-2 text-sm rounded-sm disabled:opacity-30 disabled:cursor-not-allowed cursor-pointer",
+ formControlButtonClassName,
variant === "primary" &&
"bg-primary text-[var(--oh-color-base)] hover:opacity-80",
variant === "secondary" &&
- "border border-[var(--oh-border)] text-white hover:bg-surface-raised",
+ "border border-[var(--oh-border)] bg-base-secondary text-white hover:bg-surface-raised",
variant === "tertiary" &&
"bg-[var(--oh-interactive-hover)] text-white hover:opacity-80",
variant === "danger" && "bg-red-600 text-white hover:bg-red-700",
variant === "ghost-danger" &&
- "bg-transparent text-red-600 underline hover:text-red-700 hover:no-underline font-medium",
+ "h-auto min-h-0 bg-transparent px-0 text-red-600 underline hover:text-red-700 hover:no-underline font-normal",
startContent && "flex items-center justify-center gap-2",
className,
)}
diff --git a/src/components/features/settings/llm-profiles/llm-profiles-manager.tsx b/src/components/features/settings/llm-profiles/llm-profiles-manager.tsx
index af646690d..85e87e9d1 100644
--- a/src/components/features/settings/llm-profiles/llm-profiles-manager.tsx
+++ b/src/components/features/settings/llm-profiles/llm-profiles-manager.tsx
@@ -53,7 +53,7 @@ export function LlmProfilesManager({
<>
-
+
{t(I18nKey.SETTINGS$AVAILABLE_PROFILES)}
{onAddProfile ? (
diff --git a/src/components/features/settings/llm-profiles/llm-settings-local-view.tsx b/src/components/features/settings/llm-profiles/llm-settings-local-view.tsx
index b60ac0de9..d83a23757 100644
--- a/src/components/features/settings/llm-profiles/llm-settings-local-view.tsx
+++ b/src/components/features/settings/llm-profiles/llm-settings-local-view.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useCallback, useMemo } from "react";
+import React, { useState, useCallback, useMemo, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { LlmProfilesManager } from "./llm-profiles-manager";
import { ProfileNameInput } from "./profile-name-input";
@@ -21,6 +21,8 @@ import {
import { SdkSectionSaveControl } from "../sdk-settings/sdk-section-page";
import { SettingsFormValues } from "#/utils/sdk-settings-schema";
import { ArrowLeft } from "lucide-react";
+import { Typography } from "#/ui/typography";
+import { useSettingsSectionHeader } from "#/contexts/settings-section-header-context";
type ViewMode = "list" | "create" | "edit";
@@ -41,6 +43,7 @@ interface EditingProfile {
export function LlmSettingsLocalView() {
const { t } = useTranslation("openhands");
+ const { setHideSectionHeader } = useSettingsSectionHeader();
const saveProfile = useSaveLlmProfile();
const { data: profilesData } = useLlmProfiles();
@@ -54,6 +57,11 @@ export function LlmSettingsLocalView() {
);
const [isSaving, setIsSaving] = useState(false);
+ useEffect(() => {
+ setHideSectionHeader(viewMode !== "list");
+ return () => setHideSectionHeader(false);
+ }, [viewMode, setHideSectionHeader]);
+
// Get existing profile names for validation
const existingNames = useMemo(
() => new Set(profilesData?.profiles.map((p) => p.name) ?? []),
@@ -268,20 +276,18 @@ export function LlmSettingsLocalView() {
{/* Header with back button */}
-
-
-
- {profileEditorTitle}
-
-
+
+
+ {profileEditorTitle}
+
void;
onKeyDown: (e: React.KeyboardEvent, index: number) => void;
menuItemsRef: React.MutableRefObject<(HTMLButtonElement | null)[]>;
disabled?: boolean;
- className?: string;
testId: string;
- destructive?: boolean;
}
function MenuItem({
index,
+ icon,
label,
onClick,
onKeyDown,
menuItemsRef,
disabled,
- className,
testId,
- destructive,
}: MenuItemProps) {
return (
);
}
@@ -97,13 +99,13 @@ export function ProfileActionsMenu({
const updatePosition = () => {
const rect = anchorElement.getBoundingClientRect();
if (!rect) return;
- // 4px gap matches the previous `mt-1` spacing.
- const gap = 4;
+ const gap = 8;
setPortalStyle({
position: "fixed",
zIndex: 9999,
top: rect.bottom + gap,
right: window.innerWidth - rect.right,
+ width: "max-content",
});
};
@@ -176,11 +178,7 @@ export function ProfileActionsMenu({
}
label={t(I18nKey.SETTINGS$PROFILE_EDIT)}
onClick={() => handleAction(onEdit)}
onKeyDown={handleKeyDown}
@@ -198,6 +197,7 @@ export function ProfileActionsMenu({
/>
}
label={t(I18nKey.BUTTON$RENAME)}
onClick={() => handleAction(onRename)}
onKeyDown={handleKeyDown}
@@ -206,6 +206,7 @@ export function ProfileActionsMenu({
/>
}
label={t(I18nKey.SETTINGS$PROFILE_SET_ACTIVE)}
onClick={() => handleAction(onSetActive)}
onKeyDown={handleKeyDown}
@@ -215,13 +216,12 @@ export function ProfileActionsMenu({
/>
}
label={t(I18nKey.BUTTON$DELETE)}
onClick={() => handleAction(onDelete)}
onKeyDown={handleKeyDown}
menuItemsRef={menuItemsRef}
- className="text-red-400"
testId="profile-delete"
- destructive
/>
);
diff --git a/src/components/features/settings/llm-profiles/profile-row.tsx b/src/components/features/settings/llm-profiles/profile-row.tsx
index 2d049c8c3..e5490eecc 100644
--- a/src/components/features/settings/llm-profiles/profile-row.tsx
+++ b/src/components/features/settings/llm-profiles/profile-row.tsx
@@ -5,6 +5,11 @@ import { ProfileInfo } from "#/api/profiles-service/profiles-service.api";
import { I18nKey } from "#/i18n/declaration";
import { EllipsisButton } from "#/components/features/conversation-panel/ellipsis-button";
import { BrandBadge } from "#/components/shared/badge";
+import { cn } from "#/utils/utils";
+import {
+ settingsListIconActionButtonClassName,
+ settingsListRowClassName,
+} from "#/utils/settings-list-classes";
interface ProfileRowProps {
profile: ProfileInfo;
@@ -32,18 +37,18 @@ export function ProfileRow({
return (
-
+
{profile.name}
{profile.model ? (
{profile.model}
@@ -51,7 +56,7 @@ export function ProfileRow({
) : null}
{isActive && (
{t(I18nKey.SETTINGS$PROFILE_ACTIVE)}
@@ -64,6 +69,7 @@ export function ProfileRow({
onClick={() => setMenuOpen((open) => !open)}
ariaLabel={t(I18nKey.SETTINGS$PROFILE_MENU)}
testId="profile-menu-trigger"
+ className={settingsListIconActionButtonClassName}
/>
{menuOpen && (
+
{profiles.map((profile) => (
@@ -399,9 +401,9 @@ export function MCPServerForm({
defaultValue={formatEnvironmentVariables(server?.env)}
placeholder="KEY1=value1
KEY2=value2"
className={cn(
- "resize-none",
- "bg-tertiary border border-[var(--oh-border-input)] rounded-sm p-2 placeholder:text-tertiary-alt",
- "disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)] disabled:cursor-not-allowed",
+ formControlMultilineFieldClassName,
+ "resize-none placeholder:italic",
+ "disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]",
)}
/>
diff --git a/src/components/features/settings/sdk-settings/schema-field.tsx b/src/components/features/settings/sdk-settings/schema-field.tsx
index 294e80a59..f69e0b8df 100644
--- a/src/components/features/settings/sdk-settings/schema-field.tsx
+++ b/src/components/features/settings/sdk-settings/schema-field.tsx
@@ -14,6 +14,10 @@ import {
resolveSchemaFieldLabel,
} from "#/utils/sdk-settings-field-metadata";
import { cn } from "#/utils/utils";
+import {
+ formControlMultilineFieldClassName,
+ formControlSwitchDescriptionClassName,
+} from "#/utils/form-control-classes";
// ---------------------------------------------------------------------------
// Help links – UI-only mapping from field keys to user-facing guidance.
@@ -117,7 +121,9 @@ export function SchemaField({
>
{label}
-
+
+
+
);
}
@@ -167,9 +173,9 @@ export function SchemaField({
disabled={isDisabled}
onChange={(event) => onChange(event.target.value)}
className={cn(
- "bg-tertiary border border-[var(--oh-border-input)] min-h-32 w-full min-w-0 rounded-sm p-2 font-mono text-sm",
- "placeholder:text-tertiary-alt",
- "disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)] disabled:cursor-not-allowed",
+ formControlMultilineFieldClassName,
+ "min-h-32 font-mono placeholder:italic",
+ "disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]",
)}
/>
diff --git a/src/components/features/settings/sdk-settings/view-toggle.tsx b/src/components/features/settings/sdk-settings/view-toggle.tsx
index 70f9591b8..83a9dbe45 100644
--- a/src/components/features/settings/sdk-settings/view-toggle.tsx
+++ b/src/components/features/settings/sdk-settings/view-toggle.tsx
@@ -2,6 +2,7 @@ import { useTranslation } from "react-i18next";
import { I18nKey } from "#/i18n/declaration";
import { SettingsView } from "#/utils/sdk-settings-schema";
import { cn } from "#/utils/utils";
+import { formControlTransitionClassName } from "#/utils/form-control-classes";
interface ViewToggleProps {
view: SettingsView;
@@ -13,7 +14,8 @@ interface ViewToggleProps {
const tabButtonClass = (isActive: boolean, isDisabled: boolean) =>
cn(
- "w-fit px-2 py-2 text-sm cursor-pointer rounded-none bg-transparent transition-[color,border-color]",
+ "w-fit px-2 py-2 text-sm cursor-pointer rounded-none bg-transparent",
+ formControlTransitionClassName,
"border-b-2 pb-2",
isActive
? "text-white border-white"
diff --git a/src/components/features/settings/secrets-settings/secret-form.tsx b/src/components/features/settings/secrets-settings/secret-form.tsx
index 1b63ea8db..6cffc4ff6 100644
--- a/src/components/features/settings/secrets-settings/secret-form.tsx
+++ b/src/components/features/settings/secrets-settings/secret-form.tsx
@@ -6,6 +6,10 @@ import { useCreateSecret } from "#/hooks/mutation/use-create-secret";
import { useUpdateSecret } from "#/hooks/mutation/use-update-secret";
import { SettingsInput } from "../settings-input";
import { cn } from "#/utils/utils";
+import {
+ formControlMultilineFieldClassName,
+ formControlSettingsFieldClassName,
+} from "#/utils/form-control-classes";
import { BrandButton } from "../brand-button";
import { useSearchSecrets } from "#/hooks/query/use-get-secrets";
import { OptionalTag } from "../optional-tag";
@@ -138,7 +142,8 @@ export function SecretForm({
required
className={cn(
"resize-none",
- "bg-tertiary border border-[var(--oh-border-input)] rounded-sm p-2 placeholder:text-tertiary-alt",
+ formControlMultilineFieldClassName,
+ "placeholder:italic",
"disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)] disabled:cursor-not-allowed",
)}
rows={8}
@@ -156,9 +161,8 @@ export function SecretForm({
name="secret-description"
defaultValue={secretDescription}
className={cn(
- "resize-none",
- "bg-tertiary border border-[var(--oh-border-input)] rounded-sm p-2 placeholder:text-tertiary-alt",
- "disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)] disabled:cursor-not-allowed",
+ formControlSettingsFieldClassName,
+ "disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]",
)}
/>
diff --git a/src/components/features/settings/secrets-settings/secret-list-item.tsx b/src/components/features/settings/secrets-settings/secret-list-item.tsx
index 81d14af40..e89205b08 100644
--- a/src/components/features/settings/secrets-settings/secret-list-item.tsx
+++ b/src/components/features/settings/secrets-settings/secret-list-item.tsx
@@ -1,13 +1,24 @@
import { Pencil, Trash2 } from "lucide-react";
+import { cn } from "#/utils/utils";
+import {
+ settingsListIconActionButtonClassName,
+ settingsListRowClassName,
+ settingsListTableCellClassName,
+ settingsListTableRowClassName,
+} from "#/utils/settings-list-classes";
export function SecretListItemSkeleton() {
return (
-
-
-
+
+
+
-
@@ -30,29 +41,35 @@ export function SecretListItem({
onDelete,
}: SecretListItemProps) {
return (
-
- |
+ |
+ |
{title}
|
{description || ""}
|
-
+ |
@@ -61,7 +78,7 @@ export function SecretListItem({
type="button"
onClick={onDelete}
aria-label={`Delete ${title}`}
- className="inline-flex cursor-pointer items-center justify-center rounded-md p-1 text-muted transition-colors hover:bg-interactive-hover hover:text-white"
+ className={settingsListIconActionButtonClassName}
>
diff --git a/src/components/features/settings/settings-dropdown-input.tsx b/src/components/features/settings/settings-dropdown-input.tsx
index ac3a2bebe..603627805 100644
--- a/src/components/features/settings/settings-dropdown-input.tsx
+++ b/src/components/features/settings/settings-dropdown-input.tsx
@@ -3,6 +3,8 @@ import React, { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { OptionalTag } from "./optional-tag";
import { cn } from "#/utils/utils";
+import { formControlSettingsFieldClassName } from "#/utils/form-control-classes";
+import { heroUiAutocompleteSelectorButtonClassName } from "#/ui/combobox-caret";
interface SettingsDropdownInputProps {
testId: string;
@@ -79,14 +81,13 @@ export function SettingsDropdownInput({
className="w-full"
classNames={{
popoverContent: "bg-content1 rounded-xl",
- selectorButton:
- "!rounded-none !bg-transparent data-[hover=true]:!bg-transparent !min-w-0 !w-auto !h-auto px-1",
+ selectorButton: heroUiAutocompleteSelectorButtonClassName,
}}
selectorButtonProps={{ disableRipple: true }}
inputProps={{
classNames: {
inputWrapper: cn(
- "bg-tertiary border border-[var(--oh-border-input)] h-10 w-full min-w-0 rounded-sm p-2 placeholder:text-tertiary-alt",
+ formControlSettingsFieldClassName,
inputWrapperClassName,
),
input: inputClassName,
diff --git a/src/components/features/settings/settings-input.tsx b/src/components/features/settings/settings-input.tsx
index 0a4ee2370..1d35bcb9b 100644
--- a/src/components/features/settings/settings-input.tsx
+++ b/src/components/features/settings/settings-input.tsx
@@ -1,5 +1,6 @@
import { forwardRef } from "react";
import { cn } from "#/utils/utils";
+import { formControlSettingsFieldClassName } from "#/utils/form-control-classes";
import { OptionalTag } from "./optional-tag";
interface SettingsInputProps {
@@ -106,8 +107,8 @@ export const SettingsInput = forwardRef (
aria-describedby={errorId ?? ariaDescribedBy}
aria-invalid={!!error || ariaInvalid}
className={cn(
- "bg-tertiary border border-[var(--oh-border-input)] h-10 w-full min-w-0 rounded-sm p-2 placeholder:text-tertiary-alt",
- "disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)] disabled:cursor-not-allowed",
+ formControlSettingsFieldClassName,
+ "disabled:bg-[var(--oh-surface-raised)] disabled:border-[var(--oh-border-subtle)]",
error && "border-red-500",
inputClassName,
)}
diff --git a/src/components/features/settings/settings-nav-link.tsx b/src/components/features/settings/settings-nav-link.tsx
index 74528fa9f..209866a4d 100644
--- a/src/components/features/settings/settings-nav-link.tsx
+++ b/src/components/features/settings/settings-nav-link.tsx
@@ -4,6 +4,7 @@ import { cn } from "#/utils/utils";
import { Typography } from "#/ui/typography";
import { I18nKey } from "#/i18n/declaration";
import { SettingsNavItem } from "#/constants/settings-nav";
+import { navInteractiveTransitionClassName } from "#/components/features/sidebar/sidebar-layout";
interface SettingsNavLinkProps {
item: SettingsNavItem;
@@ -55,20 +56,21 @@ export function SettingsNavLink({
onClick={onClick}
className={({ isActive }) =>
cn(
- "group flex items-center gap-3 p-1 sm:px-3.5 sm:py-2 rounded transition-all duration-200",
+ "group flex items-center gap-3 p-1 sm:px-3.5 sm:py-2 rounded",
+ navInteractiveTransitionClassName,
isActive ? "bg-tertiary" : "hover:bg-[var(--oh-surface-raised)]",
isActive ? "[&_*]:text-white" : "",
)
}
>
-
+
{icon}
{t(text as I18nKey)}
diff --git a/src/components/features/settings/settings-navigation.tsx b/src/components/features/settings/settings-navigation.tsx
index 315c39b56..02bb33c08 100644
--- a/src/components/features/settings/settings-navigation.tsx
+++ b/src/components/features/settings/settings-navigation.tsx
@@ -9,6 +9,7 @@ import { SettingsNavHeader } from "./settings-nav-header";
import { SettingsNavDivider } from "./settings-nav-divider";
import { SettingsNavLink } from "./settings-nav-link";
import { SidebarNavLink } from "#/components/features/sidebar/sidebar-nav-link";
+import { navInteractiveTransitionClassName } from "#/components/features/sidebar/sidebar-layout";
import { BackendSyncedSettingsBadge } from "#/components/features/settings/backend-synced-settings-badge";
interface SettingsNavigationProps {
@@ -111,7 +112,10 @@ export function SettingsMobileDrawer({
|