@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { LuCheck, LuChevronLeft, LuPencil, LuWand } from 'react-icons/lu';
|
|
3
|
+
import { t } from '@/i18n';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* AskUserQuestionMenu — W8 结构化提问的分步菜单卡片。
|
|
@@ -94,7 +95,7 @@ function initialDraft(question: AskQuestion, answer: AnswerValue | undefined): {
|
|
|
94
95
|
return {
|
|
95
96
|
selected,
|
|
96
97
|
customMode: customBits.length > 0,
|
|
97
|
-
customText: customBits.join('
|
|
98
|
+
customText: customBits.join(t(', ')),
|
|
98
99
|
};
|
|
99
100
|
}
|
|
100
101
|
if (typeof answer === 'string' && answer.length > 0) {
|
|
@@ -129,7 +130,7 @@ function Footer({
|
|
|
129
130
|
className="inline-flex items-center gap-1 rounded-agent-md border border-agent-border px-2.5 py-1.5 text-xs font-medium text-agent-foreground transition hover:bg-agent-muted"
|
|
130
131
|
>
|
|
131
132
|
<LuChevronLeft size={12} />
|
|
132
|
-
|
|
133
|
+
{t('Previous')}
|
|
133
134
|
</button>
|
|
134
135
|
)}
|
|
135
136
|
{onAutoContinue && (
|
|
@@ -139,7 +140,7 @@ function Footer({
|
|
|
139
140
|
className="inline-flex items-center gap-1 rounded-agent-md px-2.5 py-1.5 text-xs font-medium text-agent-muted-foreground transition hover:bg-agent-muted hover:text-agent-foreground"
|
|
140
141
|
>
|
|
141
142
|
<LuWand size={12} />
|
|
142
|
-
|
|
143
|
+
{t('Let AI decide')}
|
|
143
144
|
</button>
|
|
144
145
|
)}
|
|
145
146
|
</div>
|
|
@@ -175,7 +176,7 @@ function PrimaryButton({
|
|
|
175
176
|
].join(' ')}
|
|
176
177
|
>
|
|
177
178
|
<LuCheck size={12} />
|
|
178
|
-
{isLast ? '
|
|
179
|
+
{isLast ? t('Submit') : t('Next')}
|
|
179
180
|
</button>
|
|
180
181
|
);
|
|
181
182
|
}
|
|
@@ -352,7 +353,7 @@ function SelectQuestionStep({
|
|
|
352
353
|
<LuCheck size={10} />
|
|
353
354
|
</span>
|
|
354
355
|
<LuPencil size={12} className="shrink-0" />
|
|
355
|
-
<span className="min-w-0 flex-1"
|
|
356
|
+
<span className="min-w-0 flex-1">{t('Other / custom')}</span>
|
|
356
357
|
</button>
|
|
357
358
|
);
|
|
358
359
|
};
|
|
@@ -395,7 +396,7 @@ function SelectQuestionStep({
|
|
|
395
396
|
commitMulti();
|
|
396
397
|
}
|
|
397
398
|
}}
|
|
398
|
-
placeholder={question.placeholder || '
|
|
399
|
+
placeholder={question.placeholder || t('Add your details...')}
|
|
399
400
|
className="w-full rounded-agent-md border border-agent-border bg-agent-canvas px-3 py-2 text-xs text-agent-foreground outline-none placeholder:text-agent-muted-foreground focus:border-agent-foreground focus:ring-1 focus:ring-agent-foreground"
|
|
400
401
|
/>
|
|
401
402
|
)}
|
|
@@ -413,7 +414,7 @@ function SelectQuestionStep({
|
|
|
413
414
|
commitCustom();
|
|
414
415
|
}
|
|
415
416
|
}}
|
|
416
|
-
placeholder={question.placeholder || '
|
|
417
|
+
placeholder={question.placeholder || t('Type your answer...')}
|
|
417
418
|
className="w-full rounded-agent-md border border-agent-border bg-agent-canvas px-3 py-2 text-xs text-agent-foreground outline-none placeholder:text-agent-muted-foreground focus:border-agent-foreground focus:ring-1 focus:ring-agent-foreground"
|
|
418
419
|
/>
|
|
419
420
|
<button
|
|
@@ -424,7 +425,7 @@ function SelectQuestionStep({
|
|
|
424
425
|
}}
|
|
425
426
|
className="text-xs text-agent-muted-foreground transition hover:text-agent-foreground"
|
|
426
427
|
>
|
|
427
|
-
←
|
|
428
|
+
{t('← Back to options')}
|
|
428
429
|
</button>
|
|
429
430
|
</div>
|
|
430
431
|
)}
|
|
@@ -434,7 +435,11 @@ function SelectQuestionStep({
|
|
|
434
435
|
canGoBack={Boolean(onBack)}
|
|
435
436
|
onBack={onBack}
|
|
436
437
|
onAutoContinue={onAutoContinue}
|
|
437
|
-
hint={
|
|
438
|
+
hint={
|
|
439
|
+
question.multiSelect
|
|
440
|
+
? t('↑↓ to move, Enter to check')
|
|
441
|
+
: t('↑↓ to choose, Enter to confirm')
|
|
442
|
+
}
|
|
438
443
|
>
|
|
439
444
|
{question.multiSelect ? (
|
|
440
445
|
<PrimaryButton
|
|
@@ -511,7 +516,7 @@ function TextQuestionStep({
|
|
|
511
516
|
commit();
|
|
512
517
|
}
|
|
513
518
|
}}
|
|
514
|
-
placeholder={question.placeholder || '
|
|
519
|
+
placeholder={question.placeholder || t('Type your answer...')}
|
|
515
520
|
className="w-full rounded-agent-md border border-agent-border bg-agent-canvas px-3 py-2 text-xs text-agent-foreground outline-none placeholder:text-agent-muted-foreground focus:border-agent-foreground focus:ring-1 focus:ring-agent-foreground"
|
|
516
521
|
/>
|
|
517
522
|
</div>
|
|
@@ -520,7 +525,7 @@ function TextQuestionStep({
|
|
|
520
525
|
canGoBack={Boolean(onBack)}
|
|
521
526
|
onBack={onBack}
|
|
522
527
|
onAutoContinue={onAutoContinue}
|
|
523
|
-
hint=
|
|
528
|
+
hint={t('Enter to confirm')}
|
|
524
529
|
>
|
|
525
530
|
<PrimaryButton disabled={!value.trim()} isLast={isLast} onClick={commit} />
|
|
526
531
|
</Footer>
|
|
@@ -581,16 +586,16 @@ export function AskUserQuestionMenu({
|
|
|
581
586
|
<div className="flex items-start justify-between gap-2 border-b border-agent-border px-3 py-2">
|
|
582
587
|
<div className="min-w-0 flex-1">
|
|
583
588
|
<p className="text-xs font-medium text-agent-foreground">
|
|
584
|
-
{intro || '
|
|
589
|
+
{intro || t('Your input is needed')}
|
|
585
590
|
</p>
|
|
586
591
|
<p className="mt-0.5 text-[11px] text-agent-muted-foreground">
|
|
587
|
-
|
|
592
|
+
{t('Question {current} / {total}', { current: step + 1, total: normalized.length })}
|
|
588
593
|
{bottomHint ? ` · ${bottomHint}` : ''}
|
|
589
594
|
</p>
|
|
590
595
|
</div>
|
|
591
596
|
{current.type === 'password' || current.multiSelect ? (
|
|
592
597
|
<span className="rounded-full border border-agent-border px-2 py-0.5 text-[10px] font-medium text-agent-muted-foreground">
|
|
593
|
-
{current.type === 'password' ? '
|
|
598
|
+
{current.type === 'password' ? t('Password input') : t('Multiple choice')}
|
|
594
599
|
</span>
|
|
595
600
|
) : null}
|
|
596
601
|
</div>
|
|
@@ -3,6 +3,7 @@ import { motion } from 'framer-motion';
|
|
|
3
3
|
import { LuCheck, LuCopy, LuListChecks, LuLoaderCircle, LuRefreshCw, LuShare2 } from 'react-icons/lu';
|
|
4
4
|
import type { ChatMessage } from '@steerable/agent-protocol';
|
|
5
5
|
import type { LocalChat, LocalChatAgent } from '@/lib/local-api';
|
|
6
|
+
import { t } from '@/i18n';
|
|
6
7
|
import { Markdown } from './Markdown';
|
|
7
8
|
import { ExecutedActionsCard, type ExecutedAction } from './ExecutedActionsCard';
|
|
8
9
|
import type { ChildInfo } from './OrchestrationChildrenCard';
|
|
@@ -228,7 +229,7 @@ function readPersistedAgentId(message: ChatMessage): string | undefined {
|
|
|
228
229
|
function TurnErrorBubble({ reason }: { reason: string }) {
|
|
229
230
|
return (
|
|
230
231
|
<div className="rounded-agent-lg border border-agent-destructive/40 bg-agent-destructive/5 px-2.5 py-2 text-xs leading-relaxed text-agent-destructive shadow-sm">
|
|
231
|
-
|
|
232
|
+
{t('Request failed: {reason}', { reason })}
|
|
232
233
|
</div>
|
|
233
234
|
);
|
|
234
235
|
}
|
|
@@ -339,7 +340,7 @@ export function AssistantMessage({
|
|
|
339
340
|
{isPlanMode && (
|
|
340
341
|
<span className="inline-flex items-center gap-1 rounded-full border border-amber-200 dark:border-amber-800/50 bg-amber-100/70 dark:bg-amber-900/30 px-1.5 py-0.5 text-[10px] font-medium text-amber-700 dark:text-amber-300 select-none">
|
|
341
342
|
<LuListChecks className="h-3.5 w-3.5" />
|
|
342
|
-
<span
|
|
343
|
+
<span>{t('Plan mode (Plan)')}</span>
|
|
343
344
|
</span>
|
|
344
345
|
)}
|
|
345
346
|
</div>
|
|
@@ -369,7 +370,7 @@ export function AssistantMessage({
|
|
|
369
370
|
</div>
|
|
370
371
|
) : (
|
|
371
372
|
<div className={`${bubbleClass(isPlanMode)} text-xs italic text-agent-muted-foreground`}>
|
|
372
|
-
(
|
|
373
|
+
{t('(empty message)')}
|
|
373
374
|
</div>
|
|
374
375
|
)
|
|
375
376
|
}
|
|
@@ -427,7 +428,7 @@ export function AssistantMessage({
|
|
|
427
428
|
</div>
|
|
428
429
|
) : (
|
|
429
430
|
<div className={`${bubbleClass(isPlanMode)} text-xs italic text-agent-muted-foreground`}>
|
|
430
|
-
(
|
|
431
|
+
{t('(empty message)')}
|
|
431
432
|
</div>
|
|
432
433
|
)}
|
|
433
434
|
{displayContent && failure ? (
|
|
@@ -458,14 +459,14 @@ export function AssistantMessage({
|
|
|
458
459
|
type="button"
|
|
459
460
|
onClick={() => void copy()}
|
|
460
461
|
className={META_ACTION}
|
|
461
|
-
title={copied ? '
|
|
462
|
-
aria-label={copied ? '
|
|
462
|
+
title={copied ? t('Copied') : t('Copy message')}
|
|
463
|
+
aria-label={copied ? t('Copied') : t('Copy message')}
|
|
463
464
|
>
|
|
464
465
|
{copied ? (
|
|
465
466
|
<>
|
|
466
467
|
<LuCheck className="h-3 w-3 text-emerald-600 dark:text-emerald-400" />
|
|
467
468
|
<span className="text-emerald-600 dark:text-emerald-400">
|
|
468
|
-
|
|
469
|
+
{t('Copied')}
|
|
469
470
|
</span>
|
|
470
471
|
</>
|
|
471
472
|
) : (
|
|
@@ -493,8 +494,12 @@ export function AssistantMessage({
|
|
|
493
494
|
.finally(() => setRegenerating(false));
|
|
494
495
|
}}
|
|
495
496
|
className={`${META_ACTION} disabled:cursor-not-allowed`}
|
|
496
|
-
title={
|
|
497
|
-
|
|
497
|
+
title={
|
|
498
|
+
regenerating
|
|
499
|
+
? t('Regenerating...')
|
|
500
|
+
: t('Regenerate (the old reply is kept as a branch)')
|
|
501
|
+
}
|
|
502
|
+
aria-label={regenerating ? t('Regenerating') : t('Regenerate')}
|
|
498
503
|
data-action="regenerate"
|
|
499
504
|
>
|
|
500
505
|
<LuRefreshCw
|
|
@@ -559,12 +564,12 @@ function ShareChatButton({ onShare }: { onShare: () => Promise<boolean> }) {
|
|
|
559
564
|
}`}
|
|
560
565
|
title={
|
|
561
566
|
state === 'done'
|
|
562
|
-
? '
|
|
567
|
+
? t('Screenshot copied to clipboard')
|
|
563
568
|
: state === 'error'
|
|
564
|
-
? '
|
|
565
|
-
: '
|
|
569
|
+
? t('Screenshot failed. Try again.')
|
|
570
|
+
: t('Share chat (copy a screenshot to the clipboard)')
|
|
566
571
|
}
|
|
567
|
-
aria-label=
|
|
572
|
+
aria-label={t('Share chat screenshot')}
|
|
568
573
|
>
|
|
569
574
|
{state === 'busy' ? (
|
|
570
575
|
<LuLoaderCircle className="h-3 w-3 animate-spin" />
|
|
@@ -36,6 +36,8 @@ import { hostClipboardAvailable, requestHostPaste, type HostClipboardFile } from
|
|
|
36
36
|
import { attachmentFromPath, type AttachmentFile } from '@/lib/attachments';
|
|
37
37
|
import { dropPointHitsRect, hostFileDropAvailable, listenHostFileDrop } from '@/lib/host-file-drop';
|
|
38
38
|
import type { SteerOutcome } from '@steerable/agent-ui';
|
|
39
|
+
import { t } from '@/i18n';
|
|
40
|
+
import { agentLabel } from '@/i18n/agent-label';
|
|
39
41
|
import {
|
|
40
42
|
isHiddenSlashSkill,
|
|
41
43
|
resolveSlashTool,
|
|
@@ -278,11 +280,11 @@ function parseMentionSegments(
|
|
|
278
280
|
if (!ranges.some((range) => offset < range.end && end > range.start)) {
|
|
279
281
|
const name = match.slice(1);
|
|
280
282
|
const agent = agents.find((a) => a.name === name || a.slug === name);
|
|
281
|
-
const chat = chats.find((c) => (c.title || '
|
|
283
|
+
const chat = chats.find((c) => (c.title || t('Untitled chat')) === name);
|
|
282
284
|
const ref: MentionReference | undefined = agent
|
|
283
285
|
? { type: 'agent', id: agent.id, label: agent.name }
|
|
284
286
|
: chat
|
|
285
|
-
? { type: 'chat', id: chat.id, label: chat.title || '
|
|
287
|
+
? { type: 'chat', id: chat.id, label: chat.title || t('Untitled chat') }
|
|
286
288
|
: undefined;
|
|
287
289
|
|
|
288
290
|
ranges.push({ start: offset, end, text: match, ref });
|
|
@@ -373,7 +375,7 @@ function writeEditorValue(
|
|
|
373
375
|
const isChat = seg.ref?.type === 'chat';
|
|
374
376
|
const isSkill = seg.ref?.type === 'skill';
|
|
375
377
|
const isMcp = seg.ref?.type === 'mcp';
|
|
376
|
-
const removeLabel =
|
|
378
|
+
const removeLabel = t('Remove {name}', { name: seg.text });
|
|
377
379
|
|
|
378
380
|
const chip = document.createElement('button');
|
|
379
381
|
chip.type = 'button';
|
|
@@ -778,7 +780,7 @@ function AttachmentChip({
|
|
|
778
780
|
type="button"
|
|
779
781
|
onClick={onRemove}
|
|
780
782
|
className="flex h-4 w-4 items-center justify-center rounded-full text-agent-muted-foreground transition-colors hover:bg-agent-foreground/10 hover:text-agent-foreground"
|
|
781
|
-
aria-label={
|
|
783
|
+
aria-label={t('Remove file {name}', { name: file.name })}
|
|
782
784
|
>
|
|
783
785
|
<LuX className="h-3 w-3" />
|
|
784
786
|
</button>
|
|
@@ -799,7 +801,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
799
801
|
onRemoveFollowUp,
|
|
800
802
|
isStreaming = false,
|
|
801
803
|
disabled = false,
|
|
802
|
-
placeholder = '
|
|
804
|
+
placeholder = t('Message the Agent...'),
|
|
803
805
|
currentAgent,
|
|
804
806
|
agents = [],
|
|
805
807
|
chats = [],
|
|
@@ -941,8 +943,10 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
941
943
|
.map((chat) => ({
|
|
942
944
|
type: 'chat',
|
|
943
945
|
id: chat.id,
|
|
944
|
-
label: chat.title || '
|
|
945
|
-
description: chat.updatedAt
|
|
946
|
+
label: chat.title || t('Untitled chat'),
|
|
947
|
+
description: chat.updatedAt
|
|
948
|
+
? t('Past chat · {time}', { time: new Date(chat.updatedAt).toLocaleString() })
|
|
949
|
+
: t('Past chat'),
|
|
946
950
|
}));
|
|
947
951
|
|
|
948
952
|
return [...agentSuggestions, ...chatSuggestions].slice(0, MAX_MENTION_SUGGESTIONS);
|
|
@@ -1042,11 +1046,11 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
1042
1046
|
const mcpGroup = suggestions.filter((s) => s.kind === 'mcp');
|
|
1043
1047
|
let idx = 0;
|
|
1044
1048
|
if (skillGroup.length > 0) {
|
|
1045
|
-
rows.push({ type: 'header', label: '
|
|
1049
|
+
rows.push({ type: 'header', label: t('Run a local skill (Skill)') });
|
|
1046
1050
|
for (const s of skillGroup) rows.push({ type: 'item', item: s, idx: idx++ });
|
|
1047
1051
|
}
|
|
1048
1052
|
if (mcpGroup.length > 0) {
|
|
1049
|
-
rows.push({ type: 'header', label: '
|
|
1053
|
+
rows.push({ type: 'header', label: t('Call an MCP tool (MCP Tool)') });
|
|
1050
1054
|
for (const s of mcpGroup) rows.push({ type: 'item', item: s, idx: idx++ });
|
|
1051
1055
|
}
|
|
1052
1056
|
return rows;
|
|
@@ -1687,7 +1691,11 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
1687
1691
|
// 进入待发队列 / 作为新回合发出),草稿都可以清;queued 额外
|
|
1688
1692
|
// 提示用户"不是追加进当前回合"。
|
|
1689
1693
|
if (outcome === 'queued') {
|
|
1690
|
-
showSteerNotice(
|
|
1694
|
+
showSteerNotice(
|
|
1695
|
+
t(
|
|
1696
|
+
'This turn cannot take an interjection. The message was queued and will be sent when this turn ends.',
|
|
1697
|
+
),
|
|
1698
|
+
);
|
|
1691
1699
|
}
|
|
1692
1700
|
onChange('');
|
|
1693
1701
|
});
|
|
@@ -1774,7 +1782,9 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
1774
1782
|
onAutoContinue={() => askUserPrompt.answer({})}
|
|
1775
1783
|
bottomHint={
|
|
1776
1784
|
askUserPrompt.pendingCount > 0
|
|
1777
|
-
?
|
|
1785
|
+
? t('{count} more question groups to answer', {
|
|
1786
|
+
count: askUserPrompt.pendingCount,
|
|
1787
|
+
})
|
|
1778
1788
|
: undefined
|
|
1779
1789
|
}
|
|
1780
1790
|
/>
|
|
@@ -1814,7 +1824,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
1814
1824
|
{mentionQuery && mentionSuggestions.length > 0 && (
|
|
1815
1825
|
<div className="absolute bottom-full left-3 z-50 mb-1 max-h-72 w-80 max-w-[calc(100%-0.75rem)] overflow-y-auto rounded-agent-md border border-agent-border bg-agent-canvas p-1 shadow-lg">
|
|
1816
1826
|
<div className="px-2 py-1 text-[10px] font-semibold uppercase tracking-wider text-agent-muted-foreground">
|
|
1817
|
-
@
|
|
1827
|
+
{t('@ Choose an Agent or past chat')}
|
|
1818
1828
|
</div>
|
|
1819
1829
|
{mentionSuggestions.map((item, idx) => (
|
|
1820
1830
|
<button
|
|
@@ -1848,7 +1858,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
1848
1858
|
<span className="flex items-center gap-1.5">
|
|
1849
1859
|
<span className="truncate text-xs font-medium">@{item.label}</span>
|
|
1850
1860
|
<span className="shrink-0 rounded-full bg-agent-muted px-1.5 py-0.5 text-[10px] text-agent-muted-foreground">
|
|
1851
|
-
{item.type === 'agent' ? 'Agent' : '
|
|
1861
|
+
{item.type === 'agent' ? 'Agent' : t('Chat')}
|
|
1852
1862
|
</span>
|
|
1853
1863
|
</span>
|
|
1854
1864
|
{item.description && (
|
|
@@ -1901,7 +1911,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
1901
1911
|
<div className="text-xs font-medium truncate">
|
|
1902
1912
|
{row.item.skill.displayName ? (
|
|
1903
1913
|
<>
|
|
1904
|
-
{row.item.skill.displayName}
|
|
1914
|
+
{t(row.item.skill.displayName)}
|
|
1905
1915
|
<span className="ml-1.5 text-[10px] font-normal text-agent-muted-foreground/70">
|
|
1906
1916
|
/{row.item.skill.name}
|
|
1907
1917
|
</span>
|
|
@@ -1956,7 +1966,10 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
1956
1966
|
{pendingFollowUps.length > 0 && (
|
|
1957
1967
|
<div className="flex flex-col gap-1 border-b border-agent-border bg-agent-muted/30 px-2.5 py-1.5">
|
|
1958
1968
|
<div className="text-[11px] font-medium text-agent-muted-foreground">
|
|
1959
|
-
|
|
1969
|
+
{t(
|
|
1970
|
+
'Queued ({count}) · Sent when this turn ends · Restored to the input box if stopped',
|
|
1971
|
+
{ count: pendingFollowUps.length },
|
|
1972
|
+
)}
|
|
1960
1973
|
</div>
|
|
1961
1974
|
{pendingFollowUps.map((text, i) => (
|
|
1962
1975
|
<div
|
|
@@ -1967,7 +1980,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
1967
1980
|
{onRemoveFollowUp && (
|
|
1968
1981
|
<button
|
|
1969
1982
|
type="button"
|
|
1970
|
-
aria-label={
|
|
1983
|
+
aria-label={t('Withdraw queued message {index}', { index: i + 1 })}
|
|
1971
1984
|
onClick={() => onRemoveFollowUp(i)}
|
|
1972
1985
|
className="shrink-0 text-agent-muted-foreground opacity-0 transition-opacity hover:text-agent-destructive group-hover:opacity-100"
|
|
1973
1986
|
>
|
|
@@ -2059,8 +2072,8 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
2059
2072
|
onClick={handlePickFiles}
|
|
2060
2073
|
disabled={disabled || isStreaming}
|
|
2061
2074
|
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-agent-muted-foreground transition-colors hover:bg-agent-foreground/5 hover:text-agent-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
2062
|
-
title=
|
|
2063
|
-
aria-label=
|
|
2075
|
+
title={t('Upload files, or paste images into the input box')}
|
|
2076
|
+
aria-label={t('Upload files')}
|
|
2064
2077
|
data-testid="chat-attach"
|
|
2065
2078
|
>
|
|
2066
2079
|
<LuPaperclip className="h-3 w-3" />
|
|
@@ -2093,7 +2106,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
2093
2106
|
<kbd className="rounded border border-agent-border bg-agent-muted px-1 font-sans text-[10px]">
|
|
2094
2107
|
Enter
|
|
2095
2108
|
</kbd>
|
|
2096
|
-
<span className="ml-1"
|
|
2109
|
+
<span className="ml-1">{t('Queue')}</span>
|
|
2097
2110
|
</span>
|
|
2098
2111
|
)}
|
|
2099
2112
|
{onSteer && (
|
|
@@ -2105,14 +2118,14 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
2105
2118
|
<kbd className="rounded border border-agent-border bg-agent-muted px-1 font-sans text-[10px]">
|
|
2106
2119
|
Enter
|
|
2107
2120
|
</kbd>
|
|
2108
|
-
<span className="ml-1"
|
|
2121
|
+
<span className="ml-1">{t('Interject')}</span>
|
|
2109
2122
|
</span>
|
|
2110
2123
|
)}
|
|
2111
2124
|
<span className="ml-2">
|
|
2112
2125
|
<kbd className="rounded border border-agent-border bg-agent-muted px-1 font-sans text-[10px]">
|
|
2113
2126
|
Esc
|
|
2114
2127
|
</kbd>
|
|
2115
|
-
<span className="ml-1"
|
|
2128
|
+
<span className="ml-1">{t('Stop')}</span>
|
|
2116
2129
|
</span>
|
|
2117
2130
|
</>
|
|
2118
2131
|
) : (
|
|
@@ -2120,7 +2133,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
2120
2133
|
<kbd className="rounded border border-agent-border bg-agent-muted px-1 font-sans text-[10px]">
|
|
2121
2134
|
Enter
|
|
2122
2135
|
</kbd>
|
|
2123
|
-
<span className="ml-1"
|
|
2136
|
+
<span className="ml-1">{t('Send')}</span>
|
|
2124
2137
|
<span className="ml-2">
|
|
2125
2138
|
<kbd className="rounded border border-agent-border bg-agent-muted px-1 font-sans text-[10px]">
|
|
2126
2139
|
Shift
|
|
@@ -2129,7 +2142,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
2129
2142
|
<kbd className="rounded border border-agent-border bg-agent-muted px-1 font-sans text-[10px]">
|
|
2130
2143
|
Enter
|
|
2131
2144
|
</kbd>
|
|
2132
|
-
<span className="ml-1"
|
|
2145
|
+
<span className="ml-1">{t('New line')}</span>
|
|
2133
2146
|
</span>
|
|
2134
2147
|
</>
|
|
2135
2148
|
)}
|
|
@@ -2149,11 +2162,14 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
2149
2162
|
title={
|
|
2150
2163
|
isStreaming
|
|
2151
2164
|
? pendingFollowUps.length > 0
|
|
2152
|
-
?
|
|
2153
|
-
|
|
2154
|
-
|
|
2165
|
+
? t(
|
|
2166
|
+
'Stop generating (Esc) · {count} queued messages will be restored to the input box',
|
|
2167
|
+
{ count: pendingFollowUps.length },
|
|
2168
|
+
)
|
|
2169
|
+
: t('Stop generating (Esc)')
|
|
2170
|
+
: t('Send (Enter)')
|
|
2155
2171
|
}
|
|
2156
|
-
aria-label={isStreaming ? '
|
|
2172
|
+
aria-label={isStreaming ? t('Stop generating') : t('Send message')}
|
|
2157
2173
|
data-testid="chat-send"
|
|
2158
2174
|
>
|
|
2159
2175
|
{isStreaming ? (
|
|
@@ -2171,7 +2187,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(
|
|
|
2171
2187
|
);
|
|
2172
2188
|
|
|
2173
2189
|
function agentInitial(agent: LocalChatAgent): string {
|
|
2174
|
-
return agent.
|
|
2190
|
+
return agentLabel(agent).trim()[0]?.toUpperCase() ?? 'A';
|
|
2175
2191
|
}
|
|
2176
2192
|
|
|
2177
2193
|
/**
|
|
@@ -2193,7 +2209,7 @@ function ModeToggle({
|
|
|
2193
2209
|
<div
|
|
2194
2210
|
className="inline-flex h-6 shrink-0 items-center rounded-full border border-agent-border bg-agent-canvas p-0.5"
|
|
2195
2211
|
role="radiogroup"
|
|
2196
|
-
aria-label=
|
|
2212
|
+
aria-label={t('Chat mode')}
|
|
2197
2213
|
data-testid="mode-toggle"
|
|
2198
2214
|
>
|
|
2199
2215
|
<button
|
|
@@ -2202,7 +2218,7 @@ function ModeToggle({
|
|
|
2202
2218
|
aria-checked={mode === 'agent'}
|
|
2203
2219
|
disabled={disabled}
|
|
2204
2220
|
onClick={() => onChange('agent')}
|
|
2205
|
-
title=
|
|
2221
|
+
title={t('Agent mode: carry out the task directly')}
|
|
2206
2222
|
data-testid="mode-agent"
|
|
2207
2223
|
className={[
|
|
2208
2224
|
'inline-flex h-5 items-center gap-1 rounded-full px-1.5 text-[12px] leading-[1.45] transition-colors disabled:cursor-not-allowed disabled:opacity-70',
|
|
@@ -2220,7 +2236,7 @@ function ModeToggle({
|
|
|
2220
2236
|
aria-checked={mode === 'plan'}
|
|
2221
2237
|
disabled={disabled}
|
|
2222
2238
|
onClick={() => onChange('plan')}
|
|
2223
|
-
title=
|
|
2239
|
+
title={t('Plan mode: make a plan first, read only and no execution')}
|
|
2224
2240
|
data-testid="mode-plan"
|
|
2225
2241
|
className={[
|
|
2226
2242
|
'inline-flex h-5 items-center gap-1 rounded-full px-1.5 text-[12px] leading-[1.45] transition-colors disabled:cursor-not-allowed disabled:opacity-70',
|
|
@@ -2267,14 +2283,14 @@ function AgentSelect({
|
|
|
2267
2283
|
|
|
2268
2284
|
const buttonTitle = useMemo(() => {
|
|
2269
2285
|
const activeNames = [
|
|
2270
|
-
agent
|
|
2286
|
+
agentLabel(agent),
|
|
2271
2287
|
...mentionReferences
|
|
2272
2288
|
.filter((ref) => ref.type === 'agent' && ref.id !== agent.id)
|
|
2273
2289
|
.map((ref) => ref.label),
|
|
2274
2290
|
].filter(Boolean) as string[];
|
|
2275
2291
|
|
|
2276
|
-
return
|
|
2277
|
-
}, [agent
|
|
2292
|
+
return t('Active experts: {names}', { names: activeNames.join(', ') });
|
|
2293
|
+
}, [agent, mentionReferences]);
|
|
2278
2294
|
|
|
2279
2295
|
return (
|
|
2280
2296
|
<div
|
|
@@ -2293,7 +2309,7 @@ function AgentSelect({
|
|
|
2293
2309
|
>
|
|
2294
2310
|
<AgentDot agent={agent} />
|
|
2295
2311
|
<span className="truncate font-medium">
|
|
2296
|
-
{agent
|
|
2312
|
+
{agentLabel(agent)}
|
|
2297
2313
|
{mentionedAgentCount > 0 ? ` +${mentionedAgentCount}` : ''}
|
|
2298
2314
|
</span>
|
|
2299
2315
|
<LuChevronDown className="h-3 w-3 shrink-0 text-agent-muted-foreground" />
|
|
@@ -2305,7 +2321,7 @@ function AgentSelect({
|
|
|
2305
2321
|
className="absolute bottom-full left-0 z-50 mb-1 max-h-64 w-72 overflow-y-auto rounded-agent-md border border-agent-border bg-agent-canvas p-1 shadow-lg"
|
|
2306
2322
|
>
|
|
2307
2323
|
<div className="px-2 py-1 text-[10px] font-semibold uppercase tracking-wider text-agent-muted-foreground">
|
|
2308
|
-
|
|
2324
|
+
{t('Choose an expert')}
|
|
2309
2325
|
</div>
|
|
2310
2326
|
{agents.map((item) => {
|
|
2311
2327
|
const isPrimary = item.id === agent.id;
|
|
@@ -2332,16 +2348,16 @@ function AgentSelect({
|
|
|
2332
2348
|
<span className="min-w-0 flex-1">
|
|
2333
2349
|
<span className="flex items-center gap-1.5">
|
|
2334
2350
|
<span className="block truncate text-xs font-medium">
|
|
2335
|
-
{item
|
|
2351
|
+
{agentLabel(item)}
|
|
2336
2352
|
</span>
|
|
2337
2353
|
{isPrimary && (
|
|
2338
2354
|
<span className="inline-flex items-center rounded bg-agent-foreground/10 px-1 py-0.5 text-[9px] font-medium text-agent-foreground">
|
|
2339
|
-
|
|
2355
|
+
{t('Current')}
|
|
2340
2356
|
</span>
|
|
2341
2357
|
)}
|
|
2342
2358
|
{!isPrimary && isMentioned && (
|
|
2343
2359
|
<span className="inline-flex items-center rounded bg-agent-muted px-1 py-0.5 text-[9px] font-medium text-agent-muted-foreground border border-agent-border">
|
|
2344
|
-
|
|
2360
|
+
{t('Mentioned')}
|
|
2345
2361
|
</span>
|
|
2346
2362
|
)}
|
|
2347
2363
|
</span>
|
|
@@ -2366,7 +2382,7 @@ function AgentSelect({
|
|
|
2366
2382
|
className="mt-0.5 flex w-full items-center gap-2 rounded border-t border-agent-border/50 px-2 py-2 text-left text-agent-muted-foreground transition-colors hover:bg-agent-foreground/5 hover:text-agent-foreground"
|
|
2367
2383
|
>
|
|
2368
2384
|
<LuBot className="h-3.5 w-3.5 shrink-0" />
|
|
2369
|
-
<span className="text-xs font-medium"
|
|
2385
|
+
<span className="text-xs font-medium">{t('Manage agents')}</span>
|
|
2370
2386
|
</button>
|
|
2371
2387
|
)}
|
|
2372
2388
|
</div>
|