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
This commit is contained in:
Bohdan Triapitsyn
2026-02-24 03:28:30 +02:00
committed by GitHub
parent d2d39c48ac
commit d2358c2c03
90 changed files with 8062 additions and 7128 deletions
@@ -733,10 +733,11 @@ export const SimpleMarkdownRenderer: React.FC<{
content: string;
className?: string;
variant?: MarkdownVariant;
disableLinkSafety?: boolean;
onShowPopup?: (content: ToolPopupContent) => void;
mermaidControls?: MermaidControlOptions;
allowMermaidWheelZoom?: boolean;
}> = ({ content, className, variant = 'assistant', onShowPopup, mermaidControls, allowMermaidWheelZoom = false }) => {
}> = ({ content, className, variant = 'assistant', disableLinkSafety, onShowPopup, mermaidControls, allowMermaidWheelZoom = false }) => {
const streamdownContainerRef = React.useRef<HTMLDivElement>(null);
const mermaidBlocks = React.useMemo(() => extractMermaidBlocks(content), [content]);
useMermaidInlineInteractions({
@@ -767,6 +768,8 @@ export const SimpleMarkdownRenderer: React.FC<{
plugins={streamdownPlugins}
mermaid={mermaidOptions}
components={streamdownComponents}
// @ts-expect-error Streamdown type missing linkSafety in older minor
linkSafety={disableLinkSafety ? { enabled: false } : undefined}
>
{content}
</Streamdown>
@@ -1,6 +1,7 @@
import React from 'react';
import type { ComponentType } from 'react';
import {
RiAddLine,
RiAiAgentLine,
RiArrowDownSLine,
RiArrowGoBackLine,
@@ -187,6 +188,8 @@ const CURRENCY_FORMATTER = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
});
const ADD_PROVIDER_ID = '__add_provider__';
const formatTokens = (value?: number | null) => {
if (typeof value !== 'number' || Number.isNaN(value)) {
return '—';
@@ -297,6 +300,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
settingsDefaultVariant,
settingsDefaultAgent,
setProvider,
setSelectedProvider,
setModel,
setCurrentVariant,
getCurrentModelVariants,
@@ -362,7 +366,10 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
addRecentEffort,
isModelSelectorOpen,
setModelSelectorOpen,
setSettingsDialogOpen,
setSettingsPage,
} = useUIStore();
const hiddenModels = useUIStore((state) => state.hiddenModels);
// Separate state for agent selector to avoid conflict with model selector
const [isAgentSelectorOpen, setIsAgentSelectorOpen] = React.useState(false);
@@ -393,6 +400,13 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
// Use global state for model selector (allows Ctrl+M shortcut)
const agentMenuOpen = isModelSelectorOpen;
const setAgentMenuOpen = setModelSelectorOpen;
const openAddProviderSettings = React.useCallback(() => {
setSelectedProvider(ADD_PROVIDER_ID);
setSettingsPage('providers');
setSettingsDialogOpen(true);
setAgentMenuOpen(false);
closeMobilePanel();
}, [setSelectedProvider, setSettingsPage, setSettingsDialogOpen, setAgentMenuOpen, closeMobilePanel]);
const [desktopModelQuery, setDesktopModelQuery] = React.useState('');
const [modelSelectedIndex, setModelSelectedIndex] = React.useState(0);
const modelItemRefs = React.useRef<(HTMLDivElement | null)[]>([]);
@@ -535,6 +549,21 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
const currentProvider = getCurrentProvider();
const models = Array.isArray(currentProvider?.models) ? currentProvider.models : [];
const visibleProviders = React.useMemo(() => {
return providers
.map((provider) => {
const providerModels = Array.isArray(provider.models) ? provider.models : [];
const visibleModels = providerModels.filter((model: ProviderModel) => {
const modelId = typeof model?.id === 'string' ? model.id : '';
return !hiddenModels.some(
(item) => item.providerID === String(provider.id) && item.modelID === modelId
);
});
return { ...provider, models: visibleModels };
})
.filter((provider) => provider.models.length > 0);
}, [providers, hiddenModels]);
const currentMetadata =
currentProviderId && currentModelId ? getModelMetadata(currentProviderId, currentModelId) : undefined;
const currentCapabilityIcons = getCapabilityIcons(currentMetadata);
@@ -1439,7 +1468,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
if (!isCompact) return null;
const normalizedQuery = mobileModelQuery.trim();
const filteredProviders = providers
const filteredProviders = visibleProviders
.map((provider) => {
const providerModels = Array.isArray(provider.models) ? provider.models : [];
const matchesProvider = normalizedQuery.length === 0
@@ -2092,7 +2121,7 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
});
// Filter providers and their models
const filteredProviders = providers
const filteredProviders = visibleProviders
.map((provider) => {
const providerModels = Array.isArray(provider.models) ? provider.models : [];
const filteredModels = providerModels.filter((model: ProviderModel) => {
@@ -2218,6 +2247,26 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
{/* Scrollable content */}
<ScrollableOverlay outerClassName="max-h-[min(400px,calc(100dvh-12rem))] flex-1">
<div className="p-1">
<div
role="button"
tabIndex={0}
onClick={openAddProviderSettings}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
openAddProviderSettings();
}
}}
className="typography-meta group flex items-center gap-1 rounded-md px-2 py-1.5 cursor-pointer hover:bg-interactive-hover/50"
>
<span className="flex h-4 w-4 items-center justify-center text-muted-foreground">
<RiAddLine className="h-4 w-4 -mr-0.5" />
</span>
<span className="font-medium text-foreground">Add new provider</span>
</div>
<DropdownMenuSeparator />
{!hasResults && (
<div className="px-2 py-4 text-center typography-meta text-muted-foreground">
No models found