fix(agents): tidy the Built-in agents settings page

- List rows are no longer text-selectable. Settings pages enable text selection
  for their content, and the shared List inherited it, so dragging or ⌘A
  highlighted every row.
- An agent missing from PATH is shown as a muted row with a small "Not found"
  label instead of an orange warning badge; a custom executable gets an accent
  "Custom" label.
- The details header puts the status (with a status dot) and the launch command
  on one line.
- Reset only appears when a custom executable is set.
- The sign-in hint gets its own "Sign in" heading.
- The list/details frame sizes to its content instead of filling the page.
Цей коміт міститься в:
l0ng-ai committed 2026-11-03 22:20:58 +08:00
1 джерело d54ef98a59
коміт 8c9ddb9879
4 files changed
+97 -34

No files matched your search

+49 -11
Переглянути файл
@@ -1,6 +1,6 @@
.split {
display: flex;
flex: 1;
flex: none;
min-height: 320px;
border: 1px solid var(--border-strong);
border-radius: var(--radius-control);
@@ -19,6 +19,20 @@
min-width: 0;
padding: 0 10px;
}
.agentMissing .agentName {
color: var(--fg-muted);
}
.agentMissing svg {
opacity: 0.6;
}
.tag {
flex: none;
color: var(--fg-subtle);
font-size: var(--fs-small);
}
.tagCustom {
color: var(--accent);
}
.agentName {
flex: 1;
overflow: hidden;
@@ -41,19 +55,43 @@
margin: 0;
font-size: var(--fs-input-large);
}
.launch {
display: block;
margin: 8px 0 0;
.subtitle {
display: flex;
align-items: center;
gap: 6px;
min-width: 0;
margin: 6px 0 20px 28px;
color: var(--fg-muted);
font-family: var(--font-mono);
font-size: var(--fs-small);
overflow-wrap: anywhere;
white-space: pre-wrap;
}
.status {
margin: 6px 0 24px;
color: var(--fg-muted);
font-size: var(--fs-small);
.dot {
flex: none;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--fg-subtle);
}
.dotFound {
background: var(--success);
}
.dotCustom {
background: var(--accent);
}
.launch {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: var(--font-mono);
}
.section {
margin: 20px 0 6px;
font-size: var(--fs-ui);
font-weight: 600;
color: var(--fg-strong);
}
.section + .hint {
margin-top: 0;
}
.hint code {
font-family: var(--font-mono);
+43 -23
Переглянути файл
@@ -2,19 +2,26 @@
import { useCallback, useEffect, useState } from "react";
import type { AgentProvider } from "@/bindings";
import { tx, useTx } from "@/platform/i18n";
import { Badge, Button, Icon, IconButton, Input, List, Spinner, T } from "@/ui";
import clsx from "clsx";
import { Button, Icon, IconButton, Input, List, Spinner, T } from "@/ui";
import { useSettingsForm } from "@/workbench/contributions";
import { providerIcon } from "./model";
import type { CommandsBackend } from "./registry";
import styles from "./BuiltinAgents.module.css";
/** Trailing list tag: a missing agent is a normal state, so it is muted rather than a warning. */
function Status({ agent }: { agent: AgentProvider }) {
const translate = useTx();
if (!agent.commandOverridden && agent.installed) return null;
if (agent.commandOverridden)
return (
<span className={clsx(styles.tag, styles.tagCustom)}>
<T>Custom</T>
</span>
);
if (agent.installed) return null;
return (
<Badge tone={agent.commandOverridden ? "accent" : "warning"}>
{agent.commandOverridden ? translate("Custom") : translate("Not found")}
</Badge>
<span className={styles.tag}>
<T>Not found</T>
</span>
);
}
@@ -55,7 +62,7 @@ export function BuiltinAgents({ backend, onChanged }: { backend: CommandsBackend
rowHeight={32}
aria-label={tx("Built-in agents")}
renderItem={(p) => (
<div className={styles.agentRow}>
<div className={clsx(styles.agentRow, !p.installed && !p.commandOverridden && styles.agentMissing)}>
<Icon name={providerIcon(p.id)} size={16} />
<span className={styles.agentName}>{p.name}</span> <Status agent={p} />
</div>
@@ -95,22 +102,28 @@ function Details({
}
}, [base, current, saving, refresh]);
const launch = [agent.defaultProgram, ...agent.defaultArgs].join(" ");
const status = agent.commandOverridden
? tx("Custom executable")
: agent.installed
? tx("Found on PATH")
: tx("Not found on PATH");
return (
<section className={styles.details} aria-label={tx`${agent.name} executable`}>
<div className={styles.title}>
<Icon name={providerIcon(agent.id)} size={20} />
<h3>{agent.name}</h3>
</div>
<code className={styles.launch}>{launch}</code>
<p className={styles.status}>
{agent.commandOverridden ? (
<T>Custom executable</T>
) : agent.installed ? (
<T>Found on PATH</T>
) : (
<T>Not found on PATH</T>
)}
</p>
<div className={styles.subtitle}>
<span
aria-hidden
className={clsx(styles.dot, agent.commandOverridden ? styles.dotCustom : agent.installed && styles.dotFound)}
/>
<span>{status}</span>
<span aria-hidden>·</span>
<code className={styles.launch} title={tx("Launch command")}>
{launch}
</code>
</div>
{agent.commandEditable ? (
<>
<label className={styles.label} htmlFor={`agent-executable-${agent.id}`}>
@@ -140,9 +153,11 @@ function Details({
}
}}
/>
<Button size="small" disabled={saving || !value} onClick={() => set("")}>
<T>Reset</T>
</Button>
{value && (
<Button size="small" disabled={saving} onClick={() => set("")}>
<T>Reset</T>
</Button>
)}
</div>
<p className={styles.hint}>
<T>Leave empty to find the executable on PATH. Changes apply to new sessions.</T>
@@ -160,9 +175,14 @@ function Details({
</p>
)}
{agent.loginHint && (
<p className={styles.hint}>
<LoginHint text={agent.loginHint} />
</p>
<>
<h4 className={styles.section}>
<T>Sign in</T>
</h4>
<p className={styles.hint}>
<LoginHint text={agent.loginHint} />
</p>
</>
)}
{error && (
<p className={styles.error} role="alert">
+2
Переглянути файл
@@ -8,6 +8,8 @@
"Built-in agents": "内置智能体",
"Custom executable": "自定义可执行文件",
"Found on PATH": "已在 PATH 中找到",
"Launch command": "启动命令",
"Sign in": "登录",
"Leave empty to find the executable on PATH. Changes apply to new sessions.": "留空以从 PATH 查找可执行文件。更改对新会话生效。",
"Newer dependency version available": "有更新的依赖版本可用",
"Not found": "未找到",
+3
Переглянути файл
@@ -3,6 +3,9 @@
min-height: 0;
overflow: auto;
outline: none;
/* Rows are controls: a page that opts into text selection must not select them on drag / ⌘A. */
-webkit-user-select: none;
user-select: none;
}
.list::-webkit-scrollbar {
width: 10px;