From bcc58af9319017a79752d41059b1678dacfe9384 Mon Sep 17 00:00:00 2001 From: chaoxiaoche Date: Fri, 15 May 2026 14:35:06 +0800 Subject: [PATCH] refactor(web): rename Execution mode and tighten settings dialog UI (#1568) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(web): rename Execution mode and tighten settings dialog UI - Rename "Settings → Execution & model" to "Settings → Execution mode" across the web UI, i18n keys, docs, and e2e selectors. - Redesign SettingsDialog: kicker + title row in the modal head, a flatMap-driven agent grid that renders the inline test-result row beside the selected card, compact unavailable cards with right-aligned install/docs links, and an install guide that only shows when the user has no working agent picked. - Trim verbose subtitle / hint copy across chat model, CLI proxy, media providers, custom instructions, and memory sections. - Add an `info` Icon variant for the redesigned settings hints. - Update e2e selectors and docs that referenced the old menu label. Co-authored-by: Cursor * refactor(web): polish Settings dialog — media providers, skills, MCP Media providers - Hide internal Stub fixture provider (settingsVisible: false) - Split provider list into Available (integrated, editable) and Coming Soon (collapsed
drawer with name/hint/Docs link only) - Drop right-side Integrated/Configured badges from every row; all rows in the main list are integrated by definition; inline grey "Saved" chip next to the provider name is the only status indicator now - "Saved" badge moves inline to the right of the provider name and uses a neutral grey treatment (was a standalone green pill below the name) - "Reload from daemon" button shows a 2s green "✓ Reloaded" flash on success instead of leaving a permanent paragraph under the header; errors remain sticky Skills - Replace three pill-row filter banks (Source, Type, Category) with a compact single-row toolbar: search + three inline ); + case 'info': + return ( + + + + + + ); case 'kanban': return ( @@ -435,6 +446,19 @@ export function Icon({ name, size = 14, strokeWidth = 1.6, ...rest }: Props) { ); + case 'sun': + return ( + + + + + ); + case 'moon': + return ( + + + + ); case 'sun-moon': return ( diff --git a/apps/web/src/components/McpClientSection.tsx b/apps/web/src/components/McpClientSection.tsx index 244289e3d..d3abed5fd 100644 --- a/apps/web/src/components/McpClientSection.tsx +++ b/apps/web/src/components/McpClientSection.tsx @@ -366,10 +366,7 @@ export const McpClientSection = forwardRef(

External MCP servers

-

- Surface tools from third-party MCP servers (Higgsfield, GitHub, - filesystem…) to your coding agent. -

+

Third-party tools for your coding agent.

+ {flash?.kind === 'pathCopied' ? ( + + {flashLabel.pathCopied} + + ) : null} + + ) : null} +

{t('settings.memoryDescription')}

-
+
{t('settings.memoryIndex')} diff --git a/apps/web/src/components/PrivacySection.tsx b/apps/web/src/components/PrivacySection.tsx index 55ee8ada5..9992e4761 100644 --- a/apps/web/src/components/PrivacySection.tsx +++ b/apps/web/src/components/PrivacySection.tsx @@ -70,13 +70,6 @@ export function PrivacySection({ cfg, setCfg }: Props): JSX.Element { return (
-
-
-

{t('settings.privacy')}

-

{t('settings.privacyHint')}

-
-
- {!hasMadeConsentDecision ? ( ) : ( @@ -121,9 +114,9 @@ export function PrivacySection({ cfg, setCfg }: Props): JSX.Element { type="button" className="ghost" onClick={deleteMyData} - style={{ alignSelf: 'flex-start' }} + style={{ alignSelf: 'flex-start', marginTop: 12 }} > - + {t('settings.privacyDataDeletion')} diff --git a/apps/web/src/components/RoutinesSection.tsx b/apps/web/src/components/RoutinesSection.tsx index e1c87cc8b..40dc885fb 100644 --- a/apps/web/src/components/RoutinesSection.tsx +++ b/apps/web/src/components/RoutinesSection.tsx @@ -532,11 +532,6 @@ export function RoutinesSection({ onClose }: RoutinesSectionProps) {

Routines

-

- Scheduled, unattended agent sessions. Each run starts a new - conversation — either inside an existing project, or in a fresh - project minted on the spot. -

{!showForm ? ( - ) : null} - -
- - ); - })()} - - ) : null} {agents.length === 0 ? (
{t('settings.noAgentsDetected')} @@ -1892,109 +1887,209 @@ export function SettingsDialog({ ) : ( <>
- {agents.map((a) => { + {agents.flatMap((a) => { const active = cfg.agentId === a.id; - if (a.available) { - return ( - - ); - } - const installUrl = sanitizeHttpsUrl(a.installUrl); - const docsUrl = sanitizeHttpsUrl(a.docsUrl); - const hasLinks = Boolean(installUrl || docsUrl); - const cardLabel = `${a.name} · ${t('common.notInstalled')}`; - return ( -
{ + trackSettingsClickCliProviderCard(analytics.track, { + page: 'settings', + area: 'execution_model', + element: 'cli_provider_card', + action: 'select_cli_provider', + cli_provider_id: agentIdToTracking(a.id), + install_status: a.available ? 'installed' : 'not_installed', + is_selected: !active, + }); + setCfg((c) => ({ ...c, agentId: a.id })); + }} + aria-pressed={active} >
{a.name}
- - {t('common.notInstalled')} - + {a.authStatus === 'missing' ? ( + + {t('settings.agentAuthRequired')} + + ) : a.authStatus === 'unknown' ? ( + + {t('settings.agentAuthUnknown')} + + ) : a.version ? ( + {a.version} + ) : ( + + {t('common.installed')} + + )} +
+
+
- {agents.some((x) => !x.available) ? ( + {/* + Show the install guide only when the user has *no* + working agent picked yet. Older logic surfaced it + whenever any agent on the support list was missing, + which fired for almost everyone (few people install + all 14 supported CLIs) — the four-step quickstart + then sat between the agent grid and the model picker + forever, even after the user had successfully picked + Claude Code months ago. Once a working agent is + selected, the guide has done its job and only adds + noise. + */} + {!agents.find( + (a) => a.id === cfg.agentId && a.available, + ) ? (

{t('settings.agentInstall.pathHint')} @@ -2052,44 +2147,60 @@ export function SettingsDialog({ : modelValue; return (

+
+ {t('settings.agentModelHead')} {selected.name} +
{hasModels ? ( - + <> + + {/* + Hint sits with its own field so the user reads + "Default vs Custom…" right next to the dropdown + that exposes those options. Older layouts parked + this paragraph at the bottom of the section, past + the Memory picker, where it got mistaken for + memory documentation. + */} +

+ {t('settings.modelPickerHint')} +

+ ) : null} {customActive ? (
); })()} -
-
-

{t('settings.cliEnvTitle')}

-

{t('settings.cliEnvHint')}

-
-
- {AGENT_CLI_ENV_FIELDS.map((field) => ( - - ))} -
-
+ {(() => { + /* + Per-agent CLI environment overrides — proxy URLs, custom + config dirs, and a binary path override. The previous + layout listed every supported agent's variables in one + long always-expanded block; for users on Claude Code + the Codex fields were just visual filler (and vice + versa), and the section hijacked Settings real estate + on every open even though nine in ten users never + touch it. Now: filtered to the *currently selected* + agent only, and folded into a collapsed disclosure + that opens to "Advanced: proxy & custom paths" — power + users who route through LiteLLM or installed the + binary out-of-PATH still have one click access; new + users no longer wonder "are these fields I forgot to + fill in?". + */ + const cliEnvFields = AGENT_CLI_ENV_FIELDS.filter( + (field) => field.agentId === cfg.agentId, + ); + if (cliEnvFields.length === 0) return null; + return ( +
+ + + {t('settings.cliEnvTitle')} + + +
+

{t('settings.cliEnvHint')}

+
+ {cliEnvFields.map((field) => ( + + ))} +
+
+
+ ); + })()}
) : ( -
+ /* + BYOK panel — wrap the per-protocol form in a bordered card so + the chips above (Anthropic / OpenAI / Azure / Gemini / Ollama) + visually own the content below. Without the card, the chip + row and the form looked like two unrelated stripes; users + had no anchor for "this is what I configured for the active + tab", and switching tabs felt like the whole right column + just reshuffled. The card lives on the same white-with-soft- + border pattern as `.agent-model-row` so the two BYOK / CLI + panels feel like the same family. + */ +

{API_PROTOCOL_LABELS[apiProtocol]}

@@ -2513,12 +2675,6 @@ export function SettingsDialog({ {activeSection === 'language' ? (
-
-
-

{t('settings.language')}

-

{t('settings.languageHint')}

-
-
{LOCALES.map((code) => { const active = locale === code; @@ -2569,7 +2725,7 @@ export function SettingsDialog({ {activeSection === 'memory' ? ( <> -
+

{t('settings.customInstructionsTitle')}

@@ -2578,7 +2734,7 @@ export function SettingsDialog({