From d2358c2c0322fcdc5d718c14e46a78211712cec7 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Tue, 24 Feb 2026 03:28:30 +0200 Subject: [PATCH] feat: redesign settings pages to match canonical flat UI patterns (#493) * refactor(settings): new IA shell + projects section + skills catalog discoverability * chore(settings): split providers list by scope; show user before project * fix: navigation flow in mobile Settings * feat: redesign settings pages to use modern elevated surface patterns * feat: replace helper text with tooltips in settings * ui: redesign update dialog and fix external link routing - Restructures UpdateDialog to focus on changelog readability with a wider max-w-4xl canvas - Highlights @username contributor mentions with theme primary color - Strips excessive vertical padding and right-aligns compact action buttons - Disables streamdown's internal link safety dialog in favor of direct Tauri shell routing * feat: refactor Git identities into dedicated Git settings page * feat: unify sidebar background styling across VS Code and web/mobile * fix: adjust button styling and layout for mobile settings pages * feat: add MCP settings page and sidebar * feat: hide models in provider view (thanks to @nguyenngothuong) * feat: add "Add new provider" option to model selector dropdown * fix: local evroc logo + provider dropdown icons * fix: increase width of provider menu * fix: dark theme background color for better contrast * feat: update @opencode-ai/sdk dependency to v1.2.10 * fix: restore session sorting to only use updated time * fix: added settings for sessions deletion dialog * fix: adjust padding on settings pages for better layout * fix: standardize select dropdown height across UI * fix: agent selector UI and notification settings * fix: remove redundant helper text from settings pages * fix: update UI layout for description fields * fix: remove border-none and shadow-none from textarea classes * fix: enable context menu on sidebar items * feat: refactor UI controls and layout patterns across settings pages * fix: use headerless blocks when page title already provides context * fix: remove subtask option from command settings * fix: refactor mcp page settings * fix: reduce spacing in skills configuration pages * feat: refactor voice settings * feat: refactor settings sidebar sections --- .../skills/settings-ui-patterns/SKILL.md | 238 ++++ bun.lock | 8 +- package.json | 2 +- packages/ui/package.json | 2 +- .../ui/src/assets/provider-logos/evroc.svg | 3 + .../src/components/chat/MarkdownRenderer.tsx | 5 +- .../ui/src/components/chat/ModelControls.tsx | 53 +- packages/ui/src/components/layout/Header.tsx | 5 +- .../src/components/layout/SettingsDialog.tsx | 167 --- .../components/sections/agents/AgentsPage.tsx | 1020 +++++++-------- .../sections/agents/AgentsSidebar.tsx | 84 +- .../sections/agents/ModelSelector.tsx | 35 +- .../sections/commands/AgentSelector.tsx | 39 +- .../sections/commands/CommandsPage.tsx | 358 ++--- .../sections/commands/CommandsSidebar.tsx | 81 +- .../git-identities/GitIdentitiesPage.tsx | 533 -------- .../git-identities/GitIdentitiesSidebar.tsx | 416 ------ .../GitIdentityEditorDialog.tsx | 478 +++++++ .../sections/git-identities/GitPage.tsx | 349 +++++ .../src/components/sections/mcp/McpPage.tsx | 381 +++--- .../components/sections/mcp/McpSidebar.tsx | 253 ++-- .../sections/openchamber/AboutSettings.tsx | 143 +- .../sections/openchamber/DefaultsSettings.tsx | 293 +++-- .../sections/openchamber/GitHubSettings.tsx | 268 ++-- .../sections/openchamber/GitSettings.tsx | 98 +- .../openchamber/KeyboardShortcutsSettings.tsx | 240 ++-- .../openchamber/MemoryLimitsSettings.tsx | 115 +- .../openchamber/NotificationSettings.tsx | 673 +++++----- .../sections/openchamber/OpenChamberPage.tsx | 16 +- .../openchamber/OpenChamberSidebar.tsx | 213 --- .../openchamber/OpenChamberVisualSettings.tsx | 1146 ++++++++--------- .../openchamber/OpenCodeCliSettings.tsx | 100 +- .../openchamber/SessionRetentionSettings.tsx | 145 +-- .../sections/openchamber/VoiceSettings.tsx | 878 +++++-------- .../openchamber/WorktreeSectionContent.tsx | 90 +- .../components/sections/openchamber/types.ts | 9 + .../sections/projects/ProjectsPage.tsx | 215 ++++ .../sections/projects/ProjectsSidebar.tsx | 117 ++ .../sections/providers/ProvidersPage.tsx | 1044 ++++++++------- .../sections/providers/ProvidersSidebar.tsx | 208 ++- .../sections/shared/SettingsPageLayout.tsx | 2 +- .../shared/SettingsProjectSelector.tsx | 89 ++ .../sections/shared/SettingsSidebarItem.tsx | 2 +- .../sections/shared/SettingsSidebarLayout.tsx | 75 +- .../components/sections/skills/SkillsPage.tsx | 596 +++------ .../sections/skills/SkillsSidebar.tsx | 94 +- .../skills/catalog/AddCatalogDialog.tsx | 65 +- .../skills/catalog/InstallConflictsDialog.tsx | 14 +- .../skills/catalog/InstallFromRepoDialog.tsx | 90 +- .../skills/catalog/InstallSkillDialog.tsx | 153 ++- .../skills/catalog/SkillsCatalogPage.tsx | 458 +++---- .../components/sections/usage/UsageCard.tsx | 60 +- .../components/sections/usage/UsagePage.tsx | 322 +++-- .../sections/usage/UsageSidebar.tsx | 49 +- .../session/GitHubIssuePickerDialog.tsx | 6 +- .../session/GitHubPullRequestPickerDialog.tsx | 6 +- .../src/components/session/SessionDialogs.tsx | 81 +- .../src/components/session/SessionSidebar.tsx | 222 ++-- .../ui/src/components/ui/CommandPalette.tsx | 44 + .../ui/src/components/ui/ProviderLogo.tsx | 2 +- .../ui/src/components/ui/UpdateDialog.tsx | 251 ++-- .../ui/src/components/ui/button-small.tsx | 5 +- packages/ui/src/components/ui/button.tsx | 1 + .../ui/src/components/ui/number-input.tsx | 356 +++-- packages/ui/src/components/ui/radio.tsx | 69 + .../ui/src/components/views/SettingsView.tsx | 1026 +++++++++------ .../src/components/views/SettingsWindow.tsx | 2 +- .../ui/src/components/views/TerminalView.tsx | 18 +- .../views/git/PullRequestSection.tsx | 6 +- packages/ui/src/hooks/useModelLists.ts | 14 +- packages/ui/src/hooks/useProviderLogo.ts | 44 +- packages/ui/src/hooks/useRouter.ts | 35 +- packages/ui/src/index.css | 4 +- packages/ui/src/lib/api/types.ts | 1 + packages/ui/src/lib/appearanceAutoSave.ts | 6 + packages/ui/src/lib/desktop.ts | 1 + packages/ui/src/lib/opencode/client.ts | 3 +- packages/ui/src/lib/persistence.ts | 6 + packages/ui/src/lib/router/parseRoute.ts | 23 +- packages/ui/src/lib/router/serializeRoute.ts | 7 +- packages/ui/src/lib/router/types.ts | 3 +- packages/ui/src/lib/settings/metadata.ts | 198 +++ .../ui/src/lib/theme/themes/flexoki-dark.json | 26 +- .../src/lib/theme/themes/flexoki-light.json | 14 +- packages/ui/src/stores/useCommandsStore.ts | 4 - .../ui/src/stores/useSkillsCatalogStore.ts | 20 +- packages/ui/src/stores/useUIStore.ts | 89 +- packages/vscode/package.json | 2 +- packages/web/package.json | 2 +- packages/web/server/index.js | 3 + 90 files changed, 8062 insertions(+), 7128 deletions(-) create mode 100644 .opencode/skills/settings-ui-patterns/SKILL.md create mode 100644 packages/ui/src/assets/provider-logos/evroc.svg delete mode 100644 packages/ui/src/components/layout/SettingsDialog.tsx delete mode 100644 packages/ui/src/components/sections/git-identities/GitIdentitiesPage.tsx delete mode 100644 packages/ui/src/components/sections/git-identities/GitIdentitiesSidebar.tsx create mode 100644 packages/ui/src/components/sections/git-identities/GitIdentityEditorDialog.tsx create mode 100644 packages/ui/src/components/sections/git-identities/GitPage.tsx delete mode 100644 packages/ui/src/components/sections/openchamber/OpenChamberSidebar.tsx create mode 100644 packages/ui/src/components/sections/openchamber/types.ts create mode 100644 packages/ui/src/components/sections/projects/ProjectsPage.tsx create mode 100644 packages/ui/src/components/sections/projects/ProjectsSidebar.tsx create mode 100644 packages/ui/src/components/sections/shared/SettingsProjectSelector.tsx create mode 100644 packages/ui/src/components/ui/radio.tsx create mode 100644 packages/ui/src/lib/settings/metadata.ts diff --git a/.opencode/skills/settings-ui-patterns/SKILL.md b/.opencode/skills/settings-ui-patterns/SKILL.md new file mode 100644 index 00000000..0fc9276b --- /dev/null +++ b/.opencode/skills/settings-ui-patterns/SKILL.md @@ -0,0 +1,238 @@ +--- +name: settings-ui-patterns +description: Use when creating or modifying UI components, styling, or visual elements related to Settings in OpenChamber. +license: MIT +compatibility: opencode +--- + +# Settings UI Patterns Skill + +## Purpose +This skill provides instructions for creating or redesigning Settings pages, informational panels, and configuration interfaces within the OpenChamber application. + +## Current Canonical Look (2026) +Use this as source of truth for new settings UI work. + +- **Flat hierarchy first**: Prefer spacing + typography hierarchy over boxed backgrounds. +- **No unnecessary wrappers**: Avoid extra section wrappers that mix unrelated controls. +- **No redundant section titles**: Do not add headers like `Theme Preferences` or `Scaling & Layout` when controls are already self-explanatory. +- **Compact controls**: Option chips and radio rows should be dense, not tall. +- **Left-leading state icon**: Radio/checkbox state icon appears before text. +- **Subtle state contrast**: Inactive radio labels should be visibly dimmer than active labels. +- **Minimal row chrome**: Avoid row hover/background highlighting by default; keep only where explicitly needed. + +## Typography Guidelines +Always utilize the standard OpenChamber typography classes defined in `packages/ui/src/lib/typography.ts`. + +- **Page Title**: Use `typography-ui-header font-semibold text-foreground` for the top-most title of a settings page/dialog. +- **Section Header**: Use `typography-ui-header font-medium text-foreground` for settings sections (e.g. `Notification Events`, `Session Defaults`). +- **Control Group Header**: Use `typography-ui-header font-medium text-foreground` (or `font-normal` if it reads too loud) for grouped controls inside a section (e.g. `Default Tool Output`, `Diff Layout`). +- **Values / Primary Text**: Use `typography-ui-label text-foreground`. Add `tabular-nums` if displaying numbers or stats to ensure vertical alignment. +- **Option Labels**: Use non-bold label text in compact option controls (`font-normal` when needed to override). +- **Meta / Helper Text**: Use `typography-meta text-muted-foreground` or `typography-small text-muted-foreground` for supplemental text. + +## Layout and Spacing Patterns + +### 1. Main Backgrounds +Main wrappers should generally use `bg-background` or `bg-[var(--surface-background)]`. Ensure adequate padding (e.g., `px-5 py-6` or `p-6`). + +### 2. Subsection Grouping +Group related controls with vertical spacing, not mandatory cards. + +- Use `space-y-3` between logical subsections. +- Use `p-2` for subsection internal padding. +- Avoid adding `bg-[var(--surface-elevated)]` unless there is a clear reason. +- Avoid extra row decorations (`rounded-md`, hover fills) unless there is explicit UX value. + +### 3. Header-to-Content Hierarchy (critical) +When removing cards/background wrappers, spacing must be rebalanced so header ownership stays clear. + +- Keep **section-to-section spacing larger** than **header-to-own-content spacing**. +- Typical pattern: + - header wrapper `mb-1 px-1` + - content wrapper `pt-0 pb-2 px-2` + - outer section spacing `mb-8` +- Do not leave legacy `mb-3` style gaps after flattening a section; it makes headers look detached. + +### 4. Headerless Blocks (when context is obvious) +If the page title already provides enough context, remove redundant local headers and place controls directly below the title. + +- Example: project page identity controls can sit directly under project name/path. +- Tighten top gap for this pattern (e.g. top header `mb-4` instead of larger section spacing). + +```tsx +
+
...
+
...
+
+``` + +## Structural Patterns + +### 1. Segmented Option Buttons (compact) +Use for short option sets where button-style segmented choice reads best (e.g. Default Tool Output). + +```tsx +
+ + Collapsed + +
+``` + +### 2. Radio Option Lists (compact rows) +Use for mutually exclusive mode/layout settings (e.g. Diff Layout, Diff View Mode). + +- Use shared `Radio` component from `@/components/ui/radio`. +- Icon first, label second. +- Row container compact: `py-0.5`. +- Inactive label can use `text-foreground/50`. + +```tsx +
+
+ + Dynamic +
+
+``` + +### 3. Checkbox Setting Rows +Use shared `Checkbox` component from `@/components/ui/checkbox` for boolean toggles. + +- Icon first, text immediately after (`gap-2`). +- Typical row spacing for checkbox rows: `py-1.5`. +- Keep row click and keyboard toggle support. +- Prefer checkbox over binary show/hide button pairs for pure boolean state. + +```tsx +
+ + Show Dotfiles +
+``` + +### 4. Invisible Two-Column Alignment +Use consistent label/control columns across settings rows so controls align on a shared vertical line. + +- Desktop row pattern: `flex items-center gap-8` +- Label column width: `w-56 shrink-0` +- Control cluster: `w-fit` + +```tsx +
+ Interface Font Size +
...
+
+``` + +#### Disabled control rule +If a control is unavailable, disable the control only. Do not dim the label row by default. + +#### Width-matching rule +When matching visual widths across different rows, compare full row footprint (control + adjacent action buttons), not just input width. + +### 5. Theme Row Composition +For theme controls in Appearance: + +- `Color Mode` header on first line; option chips below it. +- `Light Theme` and `Dark Theme` on one row where possible, wrapping on small widths. +- Keep selectors near labels and aligned to existing column rhythm. +- Replace persistent helper text with an info tooltip icon near the related action. + +```tsx +
+
Light Theme ...
+
Dark Theme ...
+
+``` + +### 6. Numeric Controls in Settings +Use compact stepper input (`- value +`) plus reset button. + +- Prefer shared `NumberInput` stepper style over slider + numeric combo in dense settings pages. +- Keep reset button adjacent to control (`gap-2`). +- Avoid using Tailwind `overflow-hidden` on mobile for controls; `packages/ui/src/styles/mobile.css` forces `.overflow-hidden { overflow-y: auto !important; }`. + Use `overflow-x-hidden overflow-y-hidden` if you truly need clipping. +- Touch devices: `packages/ui/src/styles/mobile.css` enforces `min-height: 36px` on `button`. If you build custom segmented controls with ` - ); - })} - - - {/* Content area - mobile drill-down pattern */} -
- {activeTab !== 'settings' && !showPageContent && ( -
- {renderSidebarContent()} -
- )} - - {(activeTab === 'settings' || showPageContent) && ( -
- {renderPageContent()} -
- )} -
- - ); - - return ( - ( -
-
- {showPageContent && ( - - )} - Settings -
- {closeButton} -
- )} - > - {mainContent} -
- ); -}; diff --git a/packages/ui/src/components/sections/agents/AgentsPage.tsx b/packages/ui/src/components/sections/agents/AgentsPage.tsx index cca64647..707ffdd8 100644 --- a/packages/ui/src/components/sections/agents/AgentsPage.tsx +++ b/packages/ui/src/components/sections/agents/AgentsPage.tsx @@ -1,15 +1,16 @@ import React from 'react'; -import { Button } from '@/components/ui/button'; import { ButtonSmall } from '@/components/ui/button-small'; import { Input } from '@/components/ui/input'; +import { NumberInput } from '@/components/ui/number-input'; import { Textarea } from '@/components/ui/textarea'; import { toast } from '@/components/ui'; import { useAgentsStore, type AgentConfig, type AgentScope } from '@/stores/useAgentsStore'; import { useConfigStore } from '@/stores/useConfigStore'; import { usePermissionStore } from '@/stores/permissionStore'; import { useDirectoryStore } from '@/stores/useDirectoryStore'; +import { useDeviceInfo } from '@/lib/device'; import { opencodeClient } from '@/lib/opencode/client'; -import { RiAddLine, RiAiAgentFill, RiAiAgentLine, RiInformationLine, RiRobot2Line, RiRobotLine, RiSaveLine, RiSubtractLine, RiUser3Line, RiFolderLine } from '@remixicon/react'; +import { RiCloseLine, RiInformationLine, RiRobot2Line, RiSubtractLine, RiUser3Line, RiFolderLine } from '@remixicon/react'; import { cn } from '@/lib/utils'; import { ModelSelector } from './ModelSelector'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; @@ -19,6 +20,7 @@ import { SelectContent, SelectItem, SelectTrigger, + SelectValue, } from '@/components/ui/select'; type PermissionAction = 'allow' | 'ask' | 'deny'; @@ -180,6 +182,7 @@ const buildPermissionConfigWithGlobal = ( export const AgentsPage: React.FC = () => { + const { isMobile } = useDeviceInfo(); const { selectedAgentName, getAgentByName, createAgent, updateAgent, agents, agentDraft, setAgentDraft } = useAgentsStore(); useConfigStore(); @@ -593,604 +596,501 @@ export const AgentsPage: React.FC = () => { return ( -
- {/* Header */} -
-

- {isNewAgent ? 'New Agent' : selectedAgentName} -

-
+
- {} -
-
-

Basic Information

-

- Configure agent identity and behavior mode -

+ {/* Header & Actions */} +
+
+

+ {isNewAgent ? 'New Agent' : selectedAgentName} +

+

+ {isNewAgent ? 'Configure a new assistant persona' : 'Edit agent settings'} +

+
- {isNewAgent && ( -
- -
-
- @ - setDraftName(e.target.value)} - placeholder="agent-name" - className="flex-1 text-foreground placeholder:text-muted-foreground" + {/* Identity & Role */} +
+
+

+ Identity & Role +

+
+ +
+ + {isNewAgent && ( +
+
+ Agent Name +
+
+
+ @ + setDraftName(e.target.value)} + placeholder="agent-name" + className="h-7 w-40 px-2" + /> +
+ +
+
+ )} + +
+ Description +
+