@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,4 +1,5 @@
|
|
|
1
1
|
import { motion } from 'framer-motion';
|
|
2
|
+
import { t } from '@/i18n';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* StreamingStatus — informative placeholder for an assistant bubble that has
|
|
@@ -71,12 +72,12 @@ export function StreamingStatus({
|
|
|
71
72
|
|
|
72
73
|
function selectLabel(round: number, actionCount: number): string {
|
|
73
74
|
if (round >= 2 && actionCount === 0) {
|
|
74
|
-
return
|
|
75
|
+
return t('Round {round} · Continuing reasoning...', { round });
|
|
75
76
|
}
|
|
76
77
|
if (actionCount > 0) {
|
|
77
|
-
return
|
|
78
|
+
return t('Called {count} tools, analyzing...', { count: actionCount });
|
|
78
79
|
}
|
|
79
|
-
return '
|
|
80
|
+
return t('Now thinking...');
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
function Dots() {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LuSparkles } from 'react-icons/lu';
|
|
2
|
+
import { t } from '@/i18n';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* WorkBuddy-style follow-up chips: next-turn user inputs rendered under the
|
|
@@ -22,8 +23,8 @@ export function SuggestedReplies({
|
|
|
22
23
|
>
|
|
23
24
|
<div
|
|
24
25
|
className="mr-0.5 inline-flex items-center select-none"
|
|
25
|
-
title=
|
|
26
|
-
aria-label=
|
|
26
|
+
title={t('Suggestions')}
|
|
27
|
+
aria-label={t('Suggestions')}
|
|
27
28
|
>
|
|
28
29
|
<LuSparkles className="h-3 w-3 text-agent-muted-foreground/80" />
|
|
29
30
|
</div>
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* 不写库。worktree 的合并/丢弃仍归任务弹层——卡片只通知,不重复那套操作。
|
|
11
11
|
*/
|
|
12
12
|
import { LuCheck, LuGitBranch, LuX } from 'react-icons/lu';
|
|
13
|
+
import { t } from '@/i18n';
|
|
13
14
|
import type { LocalTask } from '@/lib/local-api';
|
|
14
15
|
|
|
15
16
|
/** 同时挤在消息列尾部的卡片上限;更多的只留一行汇总指回角标。 */
|
|
@@ -41,7 +42,9 @@ export function TaskOutcomeCards({ tasks, onInspect, onDismiss }: TaskOutcomeCar
|
|
|
41
42
|
))}
|
|
42
43
|
{overflow > 0 && (
|
|
43
44
|
<div className="px-1 text-[11px] text-agent-muted-foreground">
|
|
44
|
-
|
|
45
|
+
{t('{count} more background tasks finished. See the tasks button in the title bar.', {
|
|
46
|
+
count: overflow,
|
|
47
|
+
})}
|
|
45
48
|
</div>
|
|
46
49
|
)}
|
|
47
50
|
</div>
|
|
@@ -86,7 +89,7 @@ function TaskOutcomeCard({
|
|
|
86
89
|
: 'text-emerald-700 dark:text-emerald-300'
|
|
87
90
|
}
|
|
88
91
|
>
|
|
89
|
-
{failed ? '
|
|
92
|
+
{failed ? t('Background task failed: ') : t('Background task completed: ')}
|
|
90
93
|
<span className="font-medium">{task.task}</span>
|
|
91
94
|
</div>
|
|
92
95
|
{detail && (
|
|
@@ -100,7 +103,7 @@ function TaskOutcomeCard({
|
|
|
100
103
|
<span className="min-w-0 truncate font-mono text-[10px]">
|
|
101
104
|
{task.worktreeBranch}
|
|
102
105
|
</span>
|
|
103
|
-
<span
|
|
106
|
+
<span>{t('Awaiting merge. Handle it from the tasks button in the title bar.')}</span>
|
|
104
107
|
</div>
|
|
105
108
|
)}
|
|
106
109
|
</div>
|
|
@@ -111,14 +114,14 @@ function TaskOutcomeCard({
|
|
|
111
114
|
onClick={onDismiss}
|
|
112
115
|
className="rounded px-1.5 py-0.5 text-agent-muted-foreground transition-colors hover:text-agent-foreground"
|
|
113
116
|
>
|
|
114
|
-
|
|
117
|
+
{t('Dismiss')}
|
|
115
118
|
</button>
|
|
116
119
|
<button
|
|
117
120
|
type="button"
|
|
118
121
|
onClick={onInspect}
|
|
119
122
|
className="rounded-full bg-agent-foreground px-3 py-1 font-medium text-agent-canvas transition hover:opacity-90"
|
|
120
123
|
>
|
|
121
|
-
|
|
124
|
+
{t('View process')}
|
|
122
125
|
</button>
|
|
123
126
|
</div>
|
|
124
127
|
</div>
|
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
mergeTaskWorktree,
|
|
19
19
|
type LocalTask,
|
|
20
20
|
} from '@/lib/local-api';
|
|
21
|
-
import {
|
|
21
|
+
import { getHostBridge } from '@/lib/host-bridge';
|
|
22
|
+
import { t } from '@/i18n';
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* TaskPanelModal — 跨 turn 后台任务面板(4.6a/4.6c)。
|
|
@@ -80,7 +81,7 @@ export function TaskPanelModal({
|
|
|
80
81
|
// 任务终态推送 → 重拉列表。preload 只透传 chatId/taskId/status,
|
|
81
82
|
// 记录本体以 SQLite 为准。
|
|
82
83
|
useEffect(() => {
|
|
83
|
-
const bridge =
|
|
84
|
+
const bridge = getHostBridge();
|
|
84
85
|
if (!bridge?.onTaskUpdated) return;
|
|
85
86
|
return bridge.onTaskUpdated((payload) => {
|
|
86
87
|
if (payload.chatId === chatId) void refresh();
|
|
@@ -121,14 +122,14 @@ export function TaskPanelModal({
|
|
|
121
122
|
}
|
|
122
123
|
};
|
|
123
124
|
|
|
124
|
-
const runningCount = tasks?.filter((
|
|
125
|
+
const runningCount = tasks?.filter((task) => task.status === 'running').length ?? 0;
|
|
125
126
|
|
|
126
127
|
return createPortal(
|
|
127
128
|
<div
|
|
128
129
|
className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4"
|
|
129
130
|
role="dialog"
|
|
130
131
|
aria-modal="true"
|
|
131
|
-
aria-label=
|
|
132
|
+
aria-label={t('Background tasks')}
|
|
132
133
|
onMouseDown={(e) => {
|
|
133
134
|
if (e.target === e.currentTarget) onClose();
|
|
134
135
|
}}
|
|
@@ -138,10 +139,15 @@ export function TaskPanelModal({
|
|
|
138
139
|
<div className="flex items-center gap-2 border-b border-agent-border px-3 py-2">
|
|
139
140
|
<LuListTodo className="h-4 w-4 shrink-0 text-agent-muted-foreground" />
|
|
140
141
|
<span className="min-w-0 flex-1 truncate text-xs font-medium text-agent-foreground">
|
|
141
|
-
|
|
142
|
+
{t('Background tasks')}
|
|
142
143
|
{tasks && tasks.length > 0 && (
|
|
143
144
|
<span className="ml-1.5 text-xs font-normal text-agent-muted-foreground">
|
|
144
|
-
{
|
|
145
|
+
{runningCount > 0
|
|
146
|
+
? t('{count} tasks, {running} running', {
|
|
147
|
+
count: tasks.length,
|
|
148
|
+
running: runningCount,
|
|
149
|
+
})
|
|
150
|
+
: t('{count} tasks', { count: tasks.length })}
|
|
145
151
|
</span>
|
|
146
152
|
)}
|
|
147
153
|
</span>
|
|
@@ -149,7 +155,7 @@ export function TaskPanelModal({
|
|
|
149
155
|
type="button"
|
|
150
156
|
onClick={onClose}
|
|
151
157
|
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"
|
|
152
|
-
aria-label=
|
|
158
|
+
aria-label={t('Close')}
|
|
153
159
|
>
|
|
154
160
|
<LuX className="h-3.5 w-3.5" />
|
|
155
161
|
</button>
|
|
@@ -159,11 +165,13 @@ export function TaskPanelModal({
|
|
|
159
165
|
{loading ? (
|
|
160
166
|
<div className="flex items-center justify-center gap-2 px-3 py-6 text-xs text-agent-muted-foreground">
|
|
161
167
|
<LuLoaderCircle className="h-3.5 w-3.5 animate-spin" />
|
|
162
|
-
|
|
168
|
+
{t('Loading tasks...')}
|
|
163
169
|
</div>
|
|
164
170
|
) : !tasks || tasks.length === 0 ? (
|
|
165
171
|
<div className="px-3 py-6 text-center text-xs text-agent-muted-foreground">
|
|
166
|
-
|
|
172
|
+
{t(
|
|
173
|
+
'No background tasks yet. When the assistant starts a cross-turn task with task_run, it appears here.',
|
|
174
|
+
)}
|
|
167
175
|
</div>
|
|
168
176
|
) : (
|
|
169
177
|
tasks.map((task) => {
|
|
@@ -190,7 +198,7 @@ export function TaskPanelModal({
|
|
|
190
198
|
type="button"
|
|
191
199
|
onClick={() => onInspect?.(task)}
|
|
192
200
|
className="min-w-0 flex-1 truncate text-left text-xs font-medium text-agent-foreground hover:underline"
|
|
193
|
-
title=
|
|
201
|
+
title={t('View the reasoning process on the right')}
|
|
194
202
|
>
|
|
195
203
|
{task.task}
|
|
196
204
|
</button>
|
|
@@ -202,9 +210,9 @@ export function TaskPanelModal({
|
|
|
202
210
|
<LuGitBranch className="h-3 w-3" />
|
|
203
211
|
{task.worktreeBranch}
|
|
204
212
|
{task.worktreeState === 'merged'
|
|
205
|
-
?
|
|
213
|
+
? ` · ${t('Merged')}`
|
|
206
214
|
: task.worktreeState === 'discarded'
|
|
207
|
-
?
|
|
215
|
+
? ` · ${t('Discarded')}`
|
|
208
216
|
: ''}
|
|
209
217
|
</span>
|
|
210
218
|
)}
|
|
@@ -212,7 +220,7 @@ export function TaskPanelModal({
|
|
|
212
220
|
type="button"
|
|
213
221
|
onClick={() => setExpandedId(expanded ? null : task.id)}
|
|
214
222
|
className="flex h-5 w-5 shrink-0 items-center justify-center rounded text-agent-muted-foreground transition-colors hover:bg-agent-foreground/5 hover:text-agent-foreground"
|
|
215
|
-
aria-label={expanded ? '
|
|
223
|
+
aria-label={expanded ? t('Collapse') : t('Expand')}
|
|
216
224
|
aria-expanded={expanded}
|
|
217
225
|
>
|
|
218
226
|
{expanded ? (
|
|
@@ -225,12 +233,12 @@ export function TaskPanelModal({
|
|
|
225
233
|
|
|
226
234
|
<div className="mt-1 pl-5 text-[10px] text-agent-muted-foreground/80">
|
|
227
235
|
{task.status === 'running'
|
|
228
|
-
? '
|
|
236
|
+
? t('In progress')
|
|
229
237
|
: task.status === 'blocked'
|
|
230
|
-
? '
|
|
238
|
+
? t('Waiting on dependencies')
|
|
231
239
|
: task.status === 'completed'
|
|
232
|
-
? '
|
|
233
|
-
: '
|
|
240
|
+
? t('Completed')
|
|
241
|
+
: t('Failed')}
|
|
234
242
|
{' · '}
|
|
235
243
|
{formatTime(task.updatedAt)}
|
|
236
244
|
</div>
|
|
@@ -250,10 +258,10 @@ export function TaskPanelModal({
|
|
|
250
258
|
!task.error && (
|
|
251
259
|
<div className="text-xs text-agent-muted-foreground">
|
|
252
260
|
{task.status === 'running'
|
|
253
|
-
? '
|
|
261
|
+
? t('The task is still running. The result appears here when it finishes.')
|
|
254
262
|
: task.status === 'blocked'
|
|
255
|
-
? '
|
|
256
|
-
: '
|
|
263
|
+
? t('Waiting for dependent tasks to finish. It starts automatically when ready.')
|
|
264
|
+
: t('No result.')}
|
|
257
265
|
</div>
|
|
258
266
|
)
|
|
259
267
|
)}
|
|
@@ -271,7 +279,7 @@ export function TaskPanelModal({
|
|
|
271
279
|
) : (
|
|
272
280
|
<LuGitMerge className="h-3 w-3" />
|
|
273
281
|
)}
|
|
274
|
-
|
|
282
|
+
{t('Merge into main repo')}
|
|
275
283
|
</button>
|
|
276
284
|
<button
|
|
277
285
|
type="button"
|
|
@@ -281,7 +289,7 @@ export function TaskPanelModal({
|
|
|
281
289
|
data-task-discard
|
|
282
290
|
>
|
|
283
291
|
<LuTrash2 className="h-3 w-3" />
|
|
284
|
-
|
|
292
|
+
{t('Discard')}
|
|
285
293
|
</button>
|
|
286
294
|
</div>
|
|
287
295
|
)}
|
|
@@ -299,7 +307,9 @@ export function TaskPanelModal({
|
|
|
299
307
|
</div>
|
|
300
308
|
|
|
301
309
|
<div className="border-t border-agent-border px-3 py-1.5 text-[10px] text-agent-muted-foreground/80">
|
|
302
|
-
|
|
310
|
+
{t(
|
|
311
|
+
'Background agents run tasks on their own. Click a task name to see its reasoning on the right. Press Esc to close.',
|
|
312
|
+
)}
|
|
303
313
|
</div>
|
|
304
314
|
</div>
|
|
305
315
|
</div>,
|
|
@@ -2,7 +2,8 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react
|
|
|
2
2
|
import { LuArrowDown, LuLoaderCircle, LuTerminal } from 'react-icons/lu';
|
|
3
3
|
import { DockHeaderButton, DockPanelHeader } from '@/components/DockPanelHeader';
|
|
4
4
|
import { getChildProcess, getTaskProcess } from '@/lib/local-api';
|
|
5
|
-
import {
|
|
5
|
+
import { getHostBridge } from '@/lib/host-bridge';
|
|
6
|
+
import { t } from '@/i18n';
|
|
6
7
|
import { Markdown } from '@/components/chat/Markdown';
|
|
7
8
|
import { TurnProcessGroup } from '@/components/chat/TurnProcessGroup';
|
|
8
9
|
import {
|
|
@@ -153,7 +154,7 @@ export function TaskProcessPanel({
|
|
|
153
154
|
|
|
154
155
|
useEffect(() => {
|
|
155
156
|
if (childRecordId) return;
|
|
156
|
-
const bridge =
|
|
157
|
+
const bridge = getHostBridge();
|
|
157
158
|
if (!bridge?.onTaskProcess) return;
|
|
158
159
|
return bridge.onTaskProcess((payload) => {
|
|
159
160
|
if (payload.taskId !== inspected.id) return;
|
|
@@ -168,11 +169,11 @@ export function TaskProcessPanel({
|
|
|
168
169
|
}, [inspected.id, childRecordId]);
|
|
169
170
|
|
|
170
171
|
const statusLabel = live
|
|
171
|
-
? '
|
|
172
|
+
? t('Reasoning')
|
|
172
173
|
: stale
|
|
173
|
-
? '
|
|
174
|
-
: '
|
|
175
|
-
const panelTitle = `${childRecordId ? '
|
|
174
|
+
? t('Process interrupted (still marked running in the table)')
|
|
175
|
+
: t('Finished');
|
|
176
|
+
const panelTitle = `${childRecordId ? t('Subagent reasoning') : t('Background reasoning')} · ${statusLabel}`;
|
|
176
177
|
|
|
177
178
|
return (
|
|
178
179
|
<div
|
|
@@ -182,13 +183,13 @@ export function TaskProcessPanel({
|
|
|
182
183
|
<DockPanelHeader
|
|
183
184
|
title={panelTitle}
|
|
184
185
|
onClose={onClose}
|
|
185
|
-
closeLabel=
|
|
186
|
+
closeLabel={t('Close process panel')}
|
|
186
187
|
actions={
|
|
187
188
|
onShowTerminal && (
|
|
188
189
|
<DockHeaderButton
|
|
189
190
|
icon={<LuTerminal className="h-3 w-3" />}
|
|
190
|
-
label=
|
|
191
|
-
title=
|
|
191
|
+
label={t('Terminal')}
|
|
192
|
+
title={t('Switch to terminal')}
|
|
192
193
|
onClick={onShowTerminal}
|
|
193
194
|
/>
|
|
194
195
|
)
|
|
@@ -208,7 +209,7 @@ export function TaskProcessPanel({
|
|
|
208
209
|
{loading ? (
|
|
209
210
|
<div className="flex items-center gap-2 text-xs text-agent-muted-foreground">
|
|
210
211
|
<LuLoaderCircle className="h-3.5 w-3.5 animate-spin" />
|
|
211
|
-
|
|
212
|
+
{t('Loading reasoning process...')}
|
|
212
213
|
</div>
|
|
213
214
|
) : error ? (
|
|
214
215
|
<div className="text-xs text-red-600 dark:text-red-300">{error}</div>
|
|
@@ -223,10 +224,10 @@ export function TaskProcessPanel({
|
|
|
223
224
|
emptyFallback={
|
|
224
225
|
<div className="text-xs text-agent-muted-foreground">
|
|
225
226
|
{stale
|
|
226
|
-
? '
|
|
227
|
+
? t('The task stream ended with the process and left no reasoning record to replay.')
|
|
227
228
|
: live
|
|
228
|
-
? '
|
|
229
|
-
: '
|
|
229
|
+
? t('The task has started. Its reasoning process appears here.')
|
|
230
|
+
: t('No reasoning process to show.')}
|
|
230
231
|
</div>
|
|
231
232
|
}
|
|
232
233
|
renderAnswer={(block) => (
|
|
@@ -246,8 +247,8 @@ export function TaskProcessPanel({
|
|
|
246
247
|
scrollToBottom('smooth');
|
|
247
248
|
}}
|
|
248
249
|
className="absolute bottom-2 right-2 flex h-7 w-7 items-center justify-center rounded-full border border-agent-border bg-agent-canvas text-agent-foreground shadow-md transition-colors hover:bg-agent-foreground/5"
|
|
249
|
-
title=
|
|
250
|
-
aria-label=
|
|
250
|
+
title={t('Back to bottom')}
|
|
251
|
+
aria-label={t('Back to bottom')}
|
|
251
252
|
>
|
|
252
253
|
<LuArrowDown className="h-4 w-4" />
|
|
253
254
|
</button>
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from 'react-icons/lu';
|
|
17
17
|
import { openLocalPath } from '@/lib/local-api';
|
|
18
18
|
import { hostToolChrome } from '@/lib/host-tools';
|
|
19
|
+
import { t } from '@/i18n';
|
|
19
20
|
import {
|
|
20
21
|
formatFileSize,
|
|
21
22
|
formatIntermediateDisplayPath,
|
|
@@ -67,7 +68,7 @@ export function TurnFilesCard({ files, executedActions }: TurnFilesCardProps) {
|
|
|
67
68
|
try {
|
|
68
69
|
const result = await openLocalPath(filePath);
|
|
69
70
|
if (!result.success) {
|
|
70
|
-
setOpenError({ path: filePath, message: result.error || '
|
|
71
|
+
setOpenError({ path: filePath, message: result.error || t('Could not open') });
|
|
71
72
|
}
|
|
72
73
|
} catch (err) {
|
|
73
74
|
setOpenError({
|
|
@@ -197,7 +198,7 @@ function DeliverableCard({
|
|
|
197
198
|
</button>
|
|
198
199
|
<div className="mt-0.5 flex min-w-0 items-center gap-1.5 text-[11px] text-agent-muted-foreground">
|
|
199
200
|
<span className="truncate" title={file.description}>
|
|
200
|
-
{file.description ?? `${meta.label} · ${meta.extBadge}`}
|
|
201
|
+
{file.description ?? `${t(meta.label)} · ${meta.extBadge}`}
|
|
201
202
|
</span>
|
|
202
203
|
{size && (
|
|
203
204
|
<>
|
|
@@ -221,13 +222,13 @@ function DeliverableCard({
|
|
|
221
222
|
{busy ? (
|
|
222
223
|
<LuLoaderCircle className="h-3.5 w-3.5 animate-spin text-agent-muted-foreground" />
|
|
223
224
|
) : null}
|
|
224
|
-
<span
|
|
225
|
+
<span>{t('Open with')}</span>
|
|
225
226
|
</button>
|
|
226
227
|
<button
|
|
227
228
|
type="button"
|
|
228
229
|
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
229
230
|
disabled={!allowOpen}
|
|
230
|
-
aria-label=
|
|
231
|
+
aria-label={t('More actions')}
|
|
231
232
|
className="border-l border-agent-border px-1.5 py-1 text-agent-muted-foreground hover:bg-agent-muted/50 hover:text-agent-foreground rounded-r-agent-md transition-colors"
|
|
232
233
|
>
|
|
233
234
|
<LuChevronDown className="h-3.5 w-3.5" />
|
|
@@ -246,7 +247,7 @@ function DeliverableCard({
|
|
|
246
247
|
className="flex w-full items-center gap-2 px-3 py-1.5 text-left text-agent-foreground hover:bg-agent-foreground/5"
|
|
247
248
|
>
|
|
248
249
|
<LuExternalLink className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
249
|
-
<span
|
|
250
|
+
<span>{t('Open with default app')}</span>
|
|
250
251
|
</button>
|
|
251
252
|
<button
|
|
252
253
|
type="button"
|
|
@@ -257,7 +258,7 @@ function DeliverableCard({
|
|
|
257
258
|
className="flex w-full items-center gap-2 px-3 py-1.5 text-left text-agent-foreground hover:bg-agent-foreground/5"
|
|
258
259
|
>
|
|
259
260
|
<LuFolderOpen className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
260
|
-
<span
|
|
261
|
+
<span>{t('Show in folder')}</span>
|
|
261
262
|
</button>
|
|
262
263
|
<button
|
|
263
264
|
type="button"
|
|
@@ -272,7 +273,7 @@ function DeliverableCard({
|
|
|
272
273
|
) : (
|
|
273
274
|
<LuCopy className="h-3.5 w-3.5 text-agent-muted-foreground" />
|
|
274
275
|
)}
|
|
275
|
-
<span>{copied ? '
|
|
276
|
+
<span>{copied ? t('Copied full path') : t('Copy file path')}</span>
|
|
276
277
|
</button>
|
|
277
278
|
</div>
|
|
278
279
|
)}
|
|
@@ -281,7 +282,7 @@ function DeliverableCard({
|
|
|
281
282
|
|
|
282
283
|
{error && (
|
|
283
284
|
<div className="mt-2 px-1 text-[11px] text-agent-destructive" role="status">
|
|
284
|
-
|
|
285
|
+
{t('Could not open: {error}', { error })}
|
|
285
286
|
</div>
|
|
286
287
|
)}
|
|
287
288
|
</div>
|
|
@@ -370,7 +371,7 @@ function EditedFilesCard({
|
|
|
370
371
|
</div>
|
|
371
372
|
<div className="min-w-0">
|
|
372
373
|
<div className="truncate text-xs font-medium text-agent-foreground">
|
|
373
|
-
|
|
374
|
+
{t('Changed {count} files', { count: files.length })}
|
|
374
375
|
</div>
|
|
375
376
|
{(totalAdditions > 0 || totalDeletions > 0) && (
|
|
376
377
|
<div className="flex items-center gap-1.5 font-mono text-[11px] tabular-nums text-agent-muted-foreground">
|
|
@@ -388,10 +389,10 @@ function EditedFilesCard({
|
|
|
388
389
|
<button
|
|
389
390
|
type="button"
|
|
390
391
|
disabled
|
|
391
|
-
title=
|
|
392
|
+
title={t('Undo file changes from this turn')}
|
|
392
393
|
className="flex items-center gap-1 px-1.5 py-0.5 text-xs text-agent-muted-foreground/50 cursor-default"
|
|
393
394
|
>
|
|
394
|
-
<span
|
|
395
|
+
<span>{t('Undo')}</span>
|
|
395
396
|
<LuUndo2 className="h-3 w-3" />
|
|
396
397
|
</button>
|
|
397
398
|
<button
|
|
@@ -400,7 +401,7 @@ function EditedFilesCard({
|
|
|
400
401
|
onClick={() => setExpanded(!expanded)}
|
|
401
402
|
className="rounded-agent-md border border-agent-border bg-agent-canvas px-2.5 py-0.5 text-xs font-medium text-agent-foreground hover:bg-agent-muted/50 transition-colors"
|
|
402
403
|
>
|
|
403
|
-
|
|
404
|
+
{t('View details')}
|
|
404
405
|
</button>
|
|
405
406
|
</div>
|
|
406
407
|
</div>
|
|
@@ -457,7 +458,7 @@ function EditedFilesCard({
|
|
|
457
458
|
|
|
458
459
|
{error && (
|
|
459
460
|
<div className="px-3 pb-1 text-[11px] text-agent-destructive" role="status">
|
|
460
|
-
|
|
461
|
+
{t('Could not open: {error}', { error })}
|
|
461
462
|
</div>
|
|
462
463
|
)}
|
|
463
464
|
</li>
|
|
@@ -3,6 +3,7 @@ import { motion } from 'framer-motion';
|
|
|
3
3
|
import { LuChevronDown, LuChevronUp, LuCheck, LuCopy, LuCornerDownRight } 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 { useSlashSources } from '@/lib/slash-sources';
|
|
7
8
|
import { Markdown } from './Markdown';
|
|
8
9
|
import { getFriendlyDate } from './timestamp';
|
|
@@ -68,7 +69,7 @@ export function UserMessage({
|
|
|
68
69
|
<div className="mb-1 flex items-center gap-1 text-[11px] font-medium text-agent-muted-foreground select-none">
|
|
69
70
|
<LuCornerDownRight className="h-3 w-3 text-agent-primary/80" />
|
|
70
71
|
<span className="rounded bg-agent-primary/10 px-1 py-0.5 text-[10px] text-agent-primary font-medium">
|
|
71
|
-
|
|
72
|
+
{t('Appended')}
|
|
72
73
|
</span>
|
|
73
74
|
</div>
|
|
74
75
|
)}
|
|
@@ -110,12 +111,12 @@ export function UserMessage({
|
|
|
110
111
|
>
|
|
111
112
|
{isExpanded ? (
|
|
112
113
|
<>
|
|
113
|
-
|
|
114
|
+
{t('Collapse')}
|
|
114
115
|
<LuChevronUp className="h-3 w-3" />
|
|
115
116
|
</>
|
|
116
117
|
) : (
|
|
117
118
|
<>
|
|
118
|
-
|
|
119
|
+
{t('Expand all')}
|
|
119
120
|
<LuChevronDown className="h-3 w-3" />
|
|
120
121
|
</>
|
|
121
122
|
)}
|
|
@@ -127,14 +128,14 @@ export function UserMessage({
|
|
|
127
128
|
type="button"
|
|
128
129
|
onClick={() => void copy()}
|
|
129
130
|
className="inline-flex items-center gap-0.5 rounded text-xs text-agent-muted-foreground opacity-0 transition-all duration-200 hover:text-agent-foreground focus:opacity-100 group-hover/message:opacity-100"
|
|
130
|
-
title={copied ? '
|
|
131
|
-
aria-label={copied ? '
|
|
131
|
+
title={copied ? t('Copied') : t('Copy message')}
|
|
132
|
+
aria-label={copied ? t('Copied') : t('Copy message')}
|
|
132
133
|
>
|
|
133
134
|
{copied ? (
|
|
134
135
|
<>
|
|
135
136
|
<LuCheck className="h-3 w-3 text-emerald-600 dark:text-emerald-400" />
|
|
136
137
|
<span className="text-emerald-600 dark:text-emerald-400">
|
|
137
|
-
|
|
138
|
+
{t('Copied')}
|
|
138
139
|
</span>
|
|
139
140
|
</>
|
|
140
141
|
) : (
|
|
@@ -200,12 +201,12 @@ export function UserMessage({
|
|
|
200
201
|
>
|
|
201
202
|
{isExpanded ? (
|
|
202
203
|
<>
|
|
203
|
-
|
|
204
|
+
{t('Collapse')}
|
|
204
205
|
<LuChevronUp className="h-3 w-3" />
|
|
205
206
|
</>
|
|
206
207
|
) : (
|
|
207
208
|
<>
|
|
208
|
-
|
|
209
|
+
{t('Expand all')}
|
|
209
210
|
<LuChevronDown className="h-3 w-3" />
|
|
210
211
|
</>
|
|
211
212
|
)}
|
|
@@ -217,14 +218,14 @@ export function UserMessage({
|
|
|
217
218
|
type="button"
|
|
218
219
|
onClick={() => void copy()}
|
|
219
220
|
className="inline-flex items-center gap-0.5 rounded text-xs text-agent-muted-foreground opacity-0 transition-all duration-200 hover:text-agent-foreground focus:opacity-100 group-hover/message:opacity-100"
|
|
220
|
-
title={copied ? '
|
|
221
|
-
aria-label={copied ? '
|
|
221
|
+
title={copied ? t('Copied') : t('Copy message')}
|
|
222
|
+
aria-label={copied ? t('Copied') : t('Copy message')}
|
|
222
223
|
>
|
|
223
224
|
{copied ? (
|
|
224
225
|
<>
|
|
225
226
|
<LuCheck className="h-3 w-3 text-emerald-600 dark:text-emerald-400" />
|
|
226
227
|
<span className="text-emerald-600 dark:text-emerald-400">
|
|
227
|
-
|
|
228
|
+
{t('Copied')}
|
|
228
229
|
</span>
|
|
229
230
|
</>
|
|
230
231
|
) : (
|