* fix(ui): make composer keyboard interactions consistent * docs(settings): refine description visibility guidance --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
3.9 KiB
Settings Layout
All primitives and class constants below live in
packages/ui/src/components/sections/shared/SettingsSection.tsx and
SettingsPageLayout.tsx. Import them; never re-declare local equivalents.
Page Skeleton
<SettingsPageLayout
title={t('settings.page.x.title')}
description={t('settings.page.x.description')}
showSaveStatus
>
<SettingsSection title={t('...sectionA')} divider={false}>…</SettingsSection>
<SettingsSection title={t('...sectionB')}>…</SettingsSection>
</SettingsPageLayout>
SettingsPageLayoutowns scrolling, page padding, the@containercontext, and the quiet save indicator (showSaveStatus).- Sections separate with a top border (
divider, default true); the first section under the page header passesdivider={false}. - Section titles are real headers (L2). Do not nest an umbrella section around a list of
SettingsControlGroups when each group deserves its own header — promote groups to sections instead (see the Chat page precedent).
Hierarchy Levels
| Level | Component / class | Use |
|---|---|---|
| L1 | SETTINGS_PAGE_TITLE_CLASS (via SettingsPageLayout) |
Page title |
| L2 | SettingsSection title (SETTINGS_SECTION_TITLE_CLASS) |
Section |
| L3 | SettingsControlGroup title (SETTINGS_GROUP_TITLE_CLASS) |
Sub-cluster inside a section |
| L4 | SETTINGS_FIELD_LABEL_CLASS |
Field / control labels |
| Helper | SETTINGS_HELPER_CLASS, SETTINGS_DESCRIPTION_CLASS |
Rare visible helper text (most goes behind info; see the skill's Description Policy) |
Navigation Placement
Sidebar groups (packages/ui/src/lib/settings/metadata.ts, order in SettingsView.tsx):
- OpenChamber (
generalgroup): General, Appearance, Chat, Notifications, Sessions, Shortcuts, Voice, Usage, About. - Workspace (
projects): Projects, Remote Instances, External Tunnel, Git. - OpenCode (
opencode): Providers, Agents, Behavior, Commands, MCP, Plugins. - Library (
content): Magic Prompts, Snippets, Skills, Skills Catalog.
Placement rules:
- General hosts app-level settings that don't belong to a feature page: startup/tray/window, network access + UI password, passkeys, OpenCode CLI binary, terminal shell/navigation, message stream transport, privacy.
- Feature pages (Appearance, Chat, Sessions…) keep only settings about that feature. If a setting reads awkwardly on its page, move it to General rather than inventing a new page.
- New pages need metadata,
pageOrder, nav icon,settings.page.<slug>.title/descriptionin every locale, and mobile whitelist (MOBILE_SETTINGS_PAGESinMobileApp.tsx) when relevant.
Responsiveness: Container Queries
The settings pane is far narrower than the viewport (3-pane dialog). All
pane content responds to the pane via container queries — @xl: (36rem) and
@3xl: (48rem) — never viewport sm:/lg:. SettingsPageLayout provides
the @container scope; SettingsFieldRow, SettingsTwoColumn, and the
trigger-width constants already carry the right variants.
Exception: SettingsView navigation chrome (outside the pane) uses viewport
sm: to give phones 44px touch rows and plain bg-background; keep that
pattern when touching nav.
Spacing
- Sections own vertical rhythm: divider +
py-8come fromSettingsSection. - Fields inside a column:
SETTINGS_FIELDS_STACK_CLASS(space-y-4). - Checkbox/radio lists:
SETTINGS_OPTION_STACK_CLASS(space-y-1.5). - Groups requiring a title and visible description: separate them from preceding controls with
space-y-6on the parent. Keep simple checkbox/radio lists compact. The title sits closer to its own description and controls than to the preceding group; useSettingsControlGroup's internal spacing. - Two-column areas:
SettingsTwoColumn(@3xl:grid-cols-2); useSettingsStackedFieldinside cells (aSettingsFieldRowoverflows half-width columns). - No elevated backgrounds, rounded rows, or hover fills without explicit UX value.