@steerable/agent-shell-web 0.6.53 → 0.6.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/package.json +4 -4
- package/src/AppShell.tsx +3 -3
- package/src/brand.ts +1 -1
- package/src/components/AgentSidebar.tsx +64 -56
- package/src/components/ChatHeader.tsx +41 -25
- package/src/components/ConfirmDialog.tsx +4 -3
- package/src/components/CreateProjectModal.tsx +31 -21
- package/src/components/LocalLlmSettingsModal.tsx +4 -3
- package/src/components/SidebarRelease.tsx +6 -5
- package/src/components/TerminalPanel.tsx +6 -5
- package/src/components/TerminalView.tsx +15 -10
- package/src/components/chat/ApprovalModal.tsx +40 -26
- package/src/components/chat/ApprovalPromptProvider.tsx +5 -5
- package/src/components/chat/AskUserPromptProvider.tsx +4 -4
- package/src/components/chat/AskUserQuestionMenu.tsx +19 -14
- package/src/components/chat/AssistantMessage.tsx +18 -13
- package/src/components/chat/ChatInput.tsx +56 -40
- package/src/components/chat/ChatProjectBadge.tsx +27 -22
- package/src/components/chat/EmptyChatHero.tsx +2 -1
- package/src/components/chat/ExecPolicyPicker.tsx +12 -11
- package/src/components/chat/ExecutedActionsCard.tsx +45 -33
- package/src/components/chat/FilePathCode.tsx +8 -3
- package/src/components/chat/InterruptedTurnCard.tsx +4 -3
- package/src/components/chat/LocalChatPanel.tsx +2 -1
- package/src/components/chat/Markdown.tsx +2 -1
- package/src/components/chat/MessageList.tsx +3 -2
- package/src/components/chat/ModelPicker.tsx +25 -16
- package/src/components/chat/OrchestrationChildrenCard.tsx +2 -1
- package/src/components/chat/OrchestrationFlowCard.tsx +23 -22
- package/src/components/chat/PlanTodoList.tsx +2 -1
- package/src/components/chat/ProjectTrustBanner.tsx +19 -5
- package/src/components/chat/SessionOrchestrationFlow.tsx +27 -26
- package/src/components/chat/SessionTodoList.tsx +8 -7
- package/src/components/chat/SessionTreeModal.tsx +10 -9
- package/src/components/chat/StreamingStatus.tsx +4 -3
- package/src/components/chat/SuggestedReplies.tsx +3 -2
- package/src/components/chat/TaskOutcomeCards.tsx +8 -5
- package/src/components/chat/TaskPanelModal.tsx +33 -23
- package/src/components/chat/TaskProcessPanel.tsx +16 -15
- package/src/components/chat/TurnFilesCard.tsx +14 -13
- package/src/components/chat/UserMessage.tsx +12 -11
- package/src/components/chat/executed-actions-model.ts +47 -35
- package/src/components/chat/orchestration-flow-model.ts +16 -5
- package/src/components/chat/process-status.ts +8 -7
- package/src/components/chat/todo-list-model.ts +5 -4
- package/src/components/chat/turn-files.ts +8 -8
- package/src/components/chat/useChatTasks.ts +3 -3
- package/src/components/settings/AgentCapabilityFields.tsx +26 -23
- package/src/components/settings/AgentsSettingsPanel.tsx +33 -28
- package/src/components/settings/AppUpdateSettingsPanel.tsx +3 -2
- package/src/components/settings/AppearanceSettingsPanel.tsx +81 -37
- package/src/components/settings/DiagnoseSettingsPanel.tsx +11 -8
- package/src/components/settings/InsightsSettingsPanel.tsx +58 -43
- package/src/components/settings/LlmSettingsPanel.tsx +107 -86
- package/src/components/settings/McpSettingsPanel.tsx +72 -46
- package/src/components/settings/ModelIdCombobox.tsx +2 -1
- package/src/components/settings/OrchestrationSettingsPanel.tsx +8 -5
- package/src/components/settings/PortableSettingsPanel.tsx +83 -55
- package/src/components/settings/PythonRunnerSettingsPanel.tsx +26 -23
- package/src/components/settings/SecuritySettingsPanel.tsx +58 -34
- package/src/components/settings/SettingsNavMenu.tsx +3 -2
- package/src/components/settings/SettingsSaveButton.tsx +2 -1
- package/src/components/settings/SkillsSettingsPanel.tsx +43 -38
- package/src/components/settings/TelemetrySettingsPanel.tsx +20 -17
- package/src/components/settings/UsagePanel.tsx +22 -23
- package/src/components/settings/WebSearchSettingsPanel.tsx +28 -19
- package/src/components/settings/llm-vendors.ts +12 -7
- package/src/components/settings/model-capabilities.ts +16 -15
- package/src/components/settings/preset-choice-model.ts +6 -3
- package/src/fixtures/browser-dev-data.json +24 -24
- package/src/hooks/useChatsAndAgents.ts +9 -9
- package/src/hooks/useProjects.ts +3 -3
- package/src/i18n/agent-label.ts +15 -0
- package/src/i18n/index.tsx +68 -0
- package/src/i18n/runtime.ts +117 -0
- package/src/i18n/translate.ts +65 -0
- package/src/layouts/AgentLayout.tsx +13 -12
- package/src/lib/app-release.ts +16 -11
- package/src/lib/attachments.ts +18 -9
- package/src/lib/{browser-dev-electron-mock.ts → browser-dev-host-mock.ts} +42 -42
- package/src/lib/chat-title.ts +3 -2
- package/src/lib/chat-transport.ts +16 -12
- package/src/lib/date-groups.ts +23 -15
- package/src/lib/host-bridge.ts +371 -4
- package/src/lib/host-paste.ts +1 -1
- package/src/lib/http-bridge.ts +12 -9
- package/src/lib/insights.ts +2 -2
- package/src/lib/local-api.ts +5 -5
- package/src/lib/portable.ts +11 -10
- package/src/lib/show-thinking-content.ts +15 -3
- package/src/lib/slash-sources.ts +3 -3
- package/src/lib/tauri-bridge.ts +1 -1
- package/src/main.tsx +27 -4
- package/src/pages/AgentPage.tsx +39 -34
- package/src/pages/ChatPanelPreviewPage.tsx +46 -44
- package/src/pages/SettingsPage.tsx +51 -45
- package/src/vite-env.d.ts +3 -0
- package/vite.base.js +6 -2
- package/src/lib/electron-bridge.ts +0 -390
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
LuTrash2,
|
|
7
7
|
} from 'react-icons/lu';
|
|
8
8
|
import { ConfirmDialog } from '@/components/ConfirmDialog';
|
|
9
|
-
import {
|
|
9
|
+
import { t } from '@/i18n';
|
|
10
|
+
import { hasHostBridge } from '@/lib/host-bridge';
|
|
10
11
|
import {
|
|
11
12
|
archiveChatAgent,
|
|
12
13
|
createChatAgent,
|
|
@@ -61,15 +62,15 @@ function agentInitial(agent: LocalChatAgent): string {
|
|
|
61
62
|
function capabilitySummary(agent: LocalChatAgent): string[] {
|
|
62
63
|
const labels: string[] = [];
|
|
63
64
|
const skillCount = agent.skillIds?.length ?? 0;
|
|
64
|
-
if (skillCount > 0) labels.push(
|
|
65
|
-
if (agent.allowExternalSkills === false) labels.push('
|
|
66
|
-
if (agent.loadAllSkills) labels.push('
|
|
65
|
+
if (skillCount > 0) labels.push(t('Skills {count}', { count: skillCount }));
|
|
66
|
+
if (agent.allowExternalSkills === false) labels.push(t('Selected skills only'));
|
|
67
|
+
if (agent.loadAllSkills) labels.push(t('All skills'));
|
|
67
68
|
const policy = agent.toolPolicy;
|
|
68
69
|
if (policy && policy.mode !== 'all' && policy.tools.length > 0) {
|
|
69
70
|
labels.push(
|
|
70
71
|
policy.mode === 'allowlist'
|
|
71
|
-
?
|
|
72
|
-
:
|
|
72
|
+
? t('Only {count} tools', { count: policy.tools.length })
|
|
73
|
+
: t('{count} tools blocked', { count: policy.tools.length }),
|
|
73
74
|
);
|
|
74
75
|
}
|
|
75
76
|
return labels;
|
|
@@ -100,7 +101,7 @@ export function AgentsSettingsPanel({
|
|
|
100
101
|
const [error, setError] = useState<string | null>(null);
|
|
101
102
|
|
|
102
103
|
const fetchAgents = useCallback(async () => {
|
|
103
|
-
if (!
|
|
104
|
+
if (!hasHostBridge()) return;
|
|
104
105
|
setLoading(true);
|
|
105
106
|
setError(null);
|
|
106
107
|
try {
|
|
@@ -150,7 +151,7 @@ export function AgentsSettingsPanel({
|
|
|
150
151
|
|
|
151
152
|
const handleSave = async () => {
|
|
152
153
|
const trimmedName = name.trim();
|
|
153
|
-
if (!trimmedName || !
|
|
154
|
+
if (!trimmedName || !hasHostBridge()) return;
|
|
154
155
|
setSaving(true);
|
|
155
156
|
setError(null);
|
|
156
157
|
try {
|
|
@@ -158,7 +159,7 @@ export function AgentsSettingsPanel({
|
|
|
158
159
|
name: trimmedName,
|
|
159
160
|
color,
|
|
160
161
|
description: description.trim() || null,
|
|
161
|
-
rolePrompt: rolePrompt.trim() ||
|
|
162
|
+
rolePrompt: rolePrompt.trim() || t('You are **{name}**.', { name: trimmedName }),
|
|
162
163
|
skillIds: capability.skillIds,
|
|
163
164
|
allowExternalSkills: capability.allowExternalSkills,
|
|
164
165
|
loadAllSkills: capability.loadAllSkills,
|
|
@@ -203,20 +204,21 @@ export function AgentsSettingsPanel({
|
|
|
203
204
|
return (
|
|
204
205
|
<div className="space-y-3">
|
|
205
206
|
<p className="text-[11px] text-agent-muted-foreground">
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
{t(
|
|
208
|
+
'Custom agents appear in the expert picker above the input box. The role prompt opens the conversation as its persona, and the checked skills and tool permissions apply on every turn. Built-in agents can have their text and capabilities edited, but cannot be deleted.',
|
|
209
|
+
)}
|
|
208
210
|
</p>
|
|
209
211
|
|
|
210
212
|
{formOpen ? (
|
|
211
213
|
<div className="space-y-2 rounded-agent-md border border-agent-border/60 bg-agent-muted/30 p-2.5">
|
|
212
214
|
<h4 className="text-xs font-semibold text-agent-foreground">
|
|
213
|
-
{editingId ? '
|
|
215
|
+
{editingId ? t('Edit agent') : t('New agent')}
|
|
214
216
|
</h4>
|
|
215
217
|
<input
|
|
216
218
|
type="text"
|
|
217
219
|
value={name}
|
|
218
220
|
onChange={(event) => setName(event.target.value)}
|
|
219
|
-
placeholder=
|
|
221
|
+
placeholder={t('Name, e.g. Geology advisor')}
|
|
220
222
|
maxLength={40}
|
|
221
223
|
className="h-8 w-full rounded-agent-md border border-agent-border bg-agent-canvas px-3 text-xs text-agent-foreground focus:outline-none focus:ring-2 focus:ring-agent-foreground/30"
|
|
222
224
|
data-testid="agent-form-name"
|
|
@@ -225,13 +227,13 @@ export function AgentsSettingsPanel({
|
|
|
225
227
|
type="text"
|
|
226
228
|
value={description}
|
|
227
229
|
onChange={(event) => setDescription(event.target.value)}
|
|
228
|
-
placeholder=
|
|
230
|
+
placeholder={t('One-line summary (optional)')}
|
|
229
231
|
maxLength={200}
|
|
230
232
|
className="h-8 w-full rounded-agent-md border border-agent-border bg-agent-canvas px-3 text-xs text-agent-foreground focus:outline-none focus:ring-2 focus:ring-agent-foreground/30"
|
|
231
233
|
data-testid="agent-form-description"
|
|
232
234
|
/>
|
|
233
235
|
<div>
|
|
234
|
-
<p className="mb-1.5 text-[10px] font-medium text-agent-muted-foreground"
|
|
236
|
+
<p className="mb-1.5 text-[10px] font-medium text-agent-muted-foreground">{t('Color')}</p>
|
|
235
237
|
<div className="flex flex-wrap gap-1.5" data-testid="agent-form-colors">
|
|
236
238
|
{AGENT_COLORS.map((item) => (
|
|
237
239
|
<button
|
|
@@ -245,7 +247,7 @@ export function AgentsSettingsPanel({
|
|
|
245
247
|
}`}
|
|
246
248
|
style={{ backgroundColor: item }}
|
|
247
249
|
title={item}
|
|
248
|
-
aria-label={
|
|
250
|
+
aria-label={t('Color {color}', { color: item })}
|
|
249
251
|
/>
|
|
250
252
|
))}
|
|
251
253
|
</div>
|
|
@@ -253,7 +255,7 @@ export function AgentsSettingsPanel({
|
|
|
253
255
|
<textarea
|
|
254
256
|
value={rolePrompt}
|
|
255
257
|
onChange={(event) => setRolePrompt(event.target.value)}
|
|
256
|
-
placeholder=
|
|
258
|
+
placeholder={t('Role prompt. For example: You are a geology advisor. Answer concisely, giving the conclusion first and then the evidence.')}
|
|
257
259
|
rows={6}
|
|
258
260
|
className="w-full rounded-agent-md border border-agent-border bg-agent-canvas px-3 py-2 text-xs text-agent-foreground focus:outline-none focus:ring-2 focus:ring-agent-foreground/30"
|
|
259
261
|
data-testid="agent-form-role"
|
|
@@ -268,7 +270,7 @@ export function AgentsSettingsPanel({
|
|
|
268
270
|
}}
|
|
269
271
|
className="h-8 rounded-full px-4 text-xs font-medium text-agent-muted-foreground transition-colors hover:bg-agent-muted hover:text-agent-foreground"
|
|
270
272
|
>
|
|
271
|
-
|
|
273
|
+
{t('Cancel')}
|
|
272
274
|
</button>
|
|
273
275
|
<button
|
|
274
276
|
type="button"
|
|
@@ -281,7 +283,7 @@ export function AgentsSettingsPanel({
|
|
|
281
283
|
: 'bg-agent-foreground text-agent-canvas hover:opacity-90'
|
|
282
284
|
}`}
|
|
283
285
|
>
|
|
284
|
-
{saving ? <LuLoaderCircle className="h-3 w-3 animate-spin" /> : '
|
|
286
|
+
{saving ? <LuLoaderCircle className="h-3 w-3 animate-spin" /> : t('Save')}
|
|
285
287
|
</button>
|
|
286
288
|
</div>
|
|
287
289
|
</div>
|
|
@@ -293,13 +295,13 @@ export function AgentsSettingsPanel({
|
|
|
293
295
|
data-testid="agent-add"
|
|
294
296
|
>
|
|
295
297
|
<LuPlus className="h-3.5 w-3.5" />
|
|
296
|
-
|
|
298
|
+
{t('Add agent')}
|
|
297
299
|
</button>
|
|
298
300
|
)}
|
|
299
301
|
|
|
300
302
|
<div className="space-y-2">
|
|
301
303
|
<h4 className="text-xs font-semibold uppercase tracking-wide text-agent-muted-foreground">
|
|
302
|
-
|
|
304
|
+
{t('Existing agents')}
|
|
303
305
|
{agents.length > 0 && (
|
|
304
306
|
<span className="ml-1.5 font-normal normal-case">· {agents.length}</span>
|
|
305
307
|
)}
|
|
@@ -307,11 +309,11 @@ export function AgentsSettingsPanel({
|
|
|
307
309
|
{loading ? (
|
|
308
310
|
<div className="flex items-center gap-2 py-4 text-xs text-agent-muted-foreground">
|
|
309
311
|
<LuLoaderCircle className="h-3.5 w-3.5 animate-spin" />
|
|
310
|
-
|
|
312
|
+
{t('Loading agents...')}
|
|
311
313
|
</div>
|
|
312
314
|
) : agents.length === 0 ? (
|
|
313
315
|
<div className="py-6 text-center text-xs text-agent-muted-foreground">
|
|
314
|
-
|
|
316
|
+
{t('No agents yet')}
|
|
315
317
|
</div>
|
|
316
318
|
) : (
|
|
317
319
|
<div className="divide-y divide-agent-border/40 pr-1">
|
|
@@ -334,7 +336,7 @@ export function AgentsSettingsPanel({
|
|
|
334
336
|
</span>
|
|
335
337
|
{agent.isBuiltin && (
|
|
336
338
|
<span className="flex-shrink-0 rounded bg-agent-muted px-1 py-0.5 text-[9px] font-medium text-agent-muted-foreground">
|
|
337
|
-
|
|
339
|
+
{t('Built-in')}
|
|
338
340
|
</span>
|
|
339
341
|
)}
|
|
340
342
|
</div>
|
|
@@ -364,7 +366,7 @@ export function AgentsSettingsPanel({
|
|
|
364
366
|
type="button"
|
|
365
367
|
onClick={() => openEdit(agent)}
|
|
366
368
|
className="rounded-full p-1 text-agent-muted-foreground transition-colors hover:bg-agent-muted hover:text-agent-foreground"
|
|
367
|
-
title=
|
|
369
|
+
title={t('Edit')}
|
|
368
370
|
data-testid={`agent-edit-${agent.id}`}
|
|
369
371
|
>
|
|
370
372
|
<LuPencil className="h-3.5 w-3.5" />
|
|
@@ -377,7 +379,7 @@ export function AgentsSettingsPanel({
|
|
|
377
379
|
}
|
|
378
380
|
disabled={archivingId === agent.id}
|
|
379
381
|
className="rounded-full p-1 text-agent-muted-foreground transition-colors hover:bg-agent-muted hover:text-agent-destructive"
|
|
380
|
-
title=
|
|
382
|
+
title={t('Delete')}
|
|
381
383
|
data-testid={`agent-archive-${agent.id}`}
|
|
382
384
|
>
|
|
383
385
|
{archivingId === agent.id ? (
|
|
@@ -404,10 +406,13 @@ export function AgentsSettingsPanel({
|
|
|
404
406
|
)}
|
|
405
407
|
<ConfirmDialog
|
|
406
408
|
open={pendingArchive !== null}
|
|
407
|
-
title=
|
|
409
|
+
title={t('Delete agent')}
|
|
408
410
|
description={
|
|
409
411
|
pendingArchive
|
|
410
|
-
?
|
|
412
|
+
? t(
|
|
413
|
+
'Delete "{name}"? It will be removed from the expert picker. Existing conversations are not affected.',
|
|
414
|
+
{ name: pendingArchive.name },
|
|
415
|
+
)
|
|
411
416
|
: ''
|
|
412
417
|
}
|
|
413
418
|
pending={pendingArchive !== null && archivingId === pendingArchive.id}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LuInfo } from 'react-icons/lu';
|
|
2
2
|
import { useAppRelease } from '@/components/SidebarRelease';
|
|
3
|
+
import { t } from '@/i18n';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* 综合设置里的版本与更新。侧栏只显示版本号;检查、下载和安装在这里。
|
|
@@ -17,11 +18,11 @@ export function AppUpdateSettingsPanel() {
|
|
|
17
18
|
<section className="space-y-2" data-testid="settings-section-update">
|
|
18
19
|
<h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
|
|
19
20
|
<LuInfo className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
20
|
-
|
|
21
|
+
{t('About')}
|
|
21
22
|
</h2>
|
|
22
23
|
<div className="flex items-center gap-3 rounded-agent-md border border-agent-border bg-agent-card p-2.5">
|
|
23
24
|
<div className="min-w-0 flex-1">
|
|
24
|
-
<p className="text-xs font-medium text-agent-foreground"
|
|
25
|
+
<p className="text-xs font-medium text-agent-foreground">{t('Current version')}</p>
|
|
25
26
|
<p
|
|
26
27
|
className="mt-1 text-xs tabular-nums text-agent-muted-foreground"
|
|
27
28
|
data-testid="settings-app-version"
|
|
@@ -3,54 +3,98 @@ import {
|
|
|
3
3
|
persistThinkingDisplay,
|
|
4
4
|
useThinkingDisplay,
|
|
5
5
|
} from '@/lib/show-thinking-content';
|
|
6
|
+
import { localeEndonym, supportedLocales, useI18n } from '@/i18n';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* 界面偏好:思考过程正文按「隐藏 / 显示5行 / 完整显示」三档。
|
|
9
|
-
*
|
|
10
|
+
* 产品声明了多于一种语言时,在这里切换。立即写入 localStorage。
|
|
10
11
|
*/
|
|
11
12
|
export function AppearanceSettingsPanel() {
|
|
13
|
+
const { t, locale, setLocale } = useI18n();
|
|
12
14
|
const mode = useThinkingDisplay();
|
|
13
15
|
const current = THINKING_DISPLAY_OPTIONS.find((item) => item.mode === mode);
|
|
16
|
+
const locales = supportedLocales();
|
|
14
17
|
|
|
15
18
|
return (
|
|
16
|
-
<div
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
<div className="space-y-2">
|
|
20
|
+
{locales.length > 1 ? (
|
|
21
|
+
<div
|
|
22
|
+
className="space-y-2 rounded-agent-md border border-agent-border bg-agent-card p-2.5"
|
|
23
|
+
data-testid="locale-settings"
|
|
24
|
+
>
|
|
25
|
+
<p className="text-xs font-medium text-agent-foreground">{t('Language')}</p>
|
|
26
|
+
<div
|
|
27
|
+
className="flex w-full rounded-full border border-agent-border bg-agent-canvas p-0.5"
|
|
28
|
+
role="radiogroup"
|
|
29
|
+
aria-label={t('Language')}
|
|
30
|
+
data-testid="locale-toggle"
|
|
31
|
+
>
|
|
32
|
+
{locales.map((item) => {
|
|
33
|
+
const selected = locale === item;
|
|
34
|
+
return (
|
|
35
|
+
<button
|
|
36
|
+
key={item}
|
|
37
|
+
type="button"
|
|
38
|
+
role="radio"
|
|
39
|
+
aria-checked={selected}
|
|
40
|
+
data-testid={`locale-option-${item}`}
|
|
41
|
+
onClick={() => setLocale(item)}
|
|
42
|
+
className={[
|
|
43
|
+
'h-7 flex-1 rounded-full px-2 text-xs transition-colors',
|
|
44
|
+
selected
|
|
45
|
+
? 'bg-agent-foreground/10 text-agent-foreground'
|
|
46
|
+
: 'text-agent-muted-foreground hover:text-agent-foreground',
|
|
47
|
+
].join(' ')}
|
|
48
|
+
>
|
|
49
|
+
{localeEndonym(item)}
|
|
50
|
+
</button>
|
|
51
|
+
);
|
|
52
|
+
})}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
) : null}
|
|
26
56
|
<div
|
|
27
|
-
className="
|
|
28
|
-
|
|
29
|
-
aria-label="显示思考内容"
|
|
30
|
-
data-testid="thinking-display-toggle"
|
|
57
|
+
className="space-y-2 rounded-agent-md border border-agent-border bg-agent-card p-2.5"
|
|
58
|
+
data-testid="appearance-settings-panel"
|
|
31
59
|
>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
<div>
|
|
61
|
+
<p className="text-xs font-medium text-agent-foreground">{t('Show thinking')}</p>
|
|
62
|
+
<p className="mt-1 text-xs leading-relaxed text-agent-muted-foreground">
|
|
63
|
+
{current ? t(current.hint) : null}{' '}
|
|
64
|
+
{t(
|
|
65
|
+
'These options only control the thinking text. Tool cards and work rows stay as they are. A work row shows "Working", or the count and elapsed time after it finishes.',
|
|
66
|
+
)}
|
|
67
|
+
</p>
|
|
68
|
+
</div>
|
|
69
|
+
<div
|
|
70
|
+
className="flex w-full rounded-full border border-agent-border bg-agent-canvas p-0.5"
|
|
71
|
+
role="radiogroup"
|
|
72
|
+
aria-label={t('Show thinking')}
|
|
73
|
+
data-testid="thinking-display-toggle"
|
|
74
|
+
>
|
|
75
|
+
{THINKING_DISPLAY_OPTIONS.map((item) => {
|
|
76
|
+
const selected = mode === item.mode;
|
|
77
|
+
return (
|
|
78
|
+
<button
|
|
79
|
+
key={item.mode}
|
|
80
|
+
type="button"
|
|
81
|
+
role="radio"
|
|
82
|
+
aria-checked={selected}
|
|
83
|
+
aria-label={t(item.label)}
|
|
84
|
+
data-testid={`thinking-display-${item.mode}`}
|
|
85
|
+
onClick={() => persistThinkingDisplay(item.mode)}
|
|
86
|
+
className={[
|
|
87
|
+
'h-7 flex-1 rounded-full px-2 text-xs transition-colors',
|
|
88
|
+
selected
|
|
89
|
+
? 'bg-agent-foreground/10 text-agent-foreground'
|
|
90
|
+
: 'text-agent-muted-foreground hover:text-agent-foreground',
|
|
91
|
+
].join(' ')}
|
|
92
|
+
>
|
|
93
|
+
{t(item.label)}
|
|
94
|
+
</button>
|
|
95
|
+
);
|
|
96
|
+
})}
|
|
97
|
+
</div>
|
|
54
98
|
</div>
|
|
55
99
|
</div>
|
|
56
100
|
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { LuCircleCheck, LuCircleX, LuLoader, LuPlay } from 'react-icons/lu';
|
|
3
3
|
import { diagnoseLlmConnection, type DiagnoseResult } from '@/lib/local-api';
|
|
4
|
+
import { t } from '@/i18n';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* 设置页「链路诊断」面板:一键探测当前 LLM 配置的连通性
|
|
@@ -29,8 +30,10 @@ export function DiagnoseSettingsPanel() {
|
|
|
29
30
|
return (
|
|
30
31
|
<div className="space-y-2 rounded-agent-md border border-agent-border bg-agent-card p-2.5">
|
|
31
32
|
<p className="text-xs text-agent-muted-foreground">
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
{t(
|
|
34
|
+
'Diagnose network connectivity to the current model service, including DNS resolution, TCP connection, TLS handshake, the HTTP endpoint, and chat completion.',
|
|
35
|
+
)}{' '}
|
|
36
|
+
{t('The diagnosis runs in the host process and also reports system and environment proxy settings.')}
|
|
34
37
|
</p>
|
|
35
38
|
|
|
36
39
|
<button
|
|
@@ -42,19 +45,19 @@ export function DiagnoseSettingsPanel() {
|
|
|
42
45
|
{running ? (
|
|
43
46
|
<>
|
|
44
47
|
<LuLoader className="h-3.5 w-3.5 animate-spin" />
|
|
45
|
-
|
|
48
|
+
{t('Diagnosing…')}
|
|
46
49
|
</>
|
|
47
50
|
) : (
|
|
48
51
|
<>
|
|
49
52
|
<LuPlay className="h-3.5 w-3.5" />
|
|
50
|
-
|
|
53
|
+
{t('Start diagnosis')}
|
|
51
54
|
</>
|
|
52
55
|
)}
|
|
53
56
|
</button>
|
|
54
57
|
|
|
55
58
|
{error && (
|
|
56
59
|
<p className="rounded-agent-md border border-agent-destructive/20 bg-agent-destructive/10 p-2.5 text-xs text-agent-destructive">
|
|
57
|
-
|
|
60
|
+
{t('Diagnosis request failed: {error}', { error })}
|
|
58
61
|
</p>
|
|
59
62
|
)}
|
|
60
63
|
|
|
@@ -64,12 +67,12 @@ export function DiagnoseSettingsPanel() {
|
|
|
64
67
|
{result.ok ? (
|
|
65
68
|
<span className="inline-flex items-center gap-1 text-agent-success">
|
|
66
69
|
<LuCircleCheck className="h-4 w-4" />
|
|
67
|
-
|
|
70
|
+
{t('All checks passed')}
|
|
68
71
|
</span>
|
|
69
72
|
) : (
|
|
70
73
|
<span className="inline-flex items-center gap-1 text-agent-destructive">
|
|
71
74
|
<LuCircleX className="h-4 w-4" />
|
|
72
|
-
|
|
75
|
+
{t('Diagnosis failed')}
|
|
73
76
|
</span>
|
|
74
77
|
)}
|
|
75
78
|
</div>
|
|
@@ -98,7 +101,7 @@ export function DiagnoseSettingsPanel() {
|
|
|
98
101
|
|
|
99
102
|
{result.ambientProxies.length > 0 && (
|
|
100
103
|
<div className="rounded-agent-sm border border-agent-border bg-agent-muted/30 p-2.5 text-xs">
|
|
101
|
-
<p className="font-medium text-agent-foreground"
|
|
104
|
+
<p className="font-medium text-agent-foreground">{t('Detected proxy settings')}</p>
|
|
102
105
|
<ul className="mt-1 list-inside list-disc space-y-0.5 text-agent-muted-foreground">
|
|
103
106
|
{result.ambientProxies.map((proxy) => (
|
|
104
107
|
<li key={proxy}>{proxy}</li>
|