@steerable/agent-shell-web 0.6.52 → 0.6.55

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.
Files changed (93) hide show
  1. package/package.json +4 -4
  2. package/src/brand.ts +1 -1
  3. package/src/components/AgentSidebar.tsx +60 -52
  4. package/src/components/ChatHeader.tsx +41 -25
  5. package/src/components/ConfirmDialog.tsx +4 -3
  6. package/src/components/CreateProjectModal.tsx +29 -19
  7. package/src/components/LocalLlmSettingsModal.tsx +4 -3
  8. package/src/components/SidebarRelease.tsx +2 -1
  9. package/src/components/TerminalPanel.tsx +3 -2
  10. package/src/components/TerminalView.tsx +12 -7
  11. package/src/components/chat/ApprovalModal.tsx +39 -25
  12. package/src/components/chat/ApprovalPromptProvider.tsx +1 -1
  13. package/src/components/chat/AskUserPromptProvider.tsx +1 -1
  14. package/src/components/chat/AskUserQuestionMenu.tsx +19 -14
  15. package/src/components/chat/AssistantMessage.tsx +18 -13
  16. package/src/components/chat/ChatInput.tsx +138 -42
  17. package/src/components/chat/ChatProjectBadge.tsx +24 -19
  18. package/src/components/chat/EmptyChatHero.tsx +2 -1
  19. package/src/components/chat/ExecPolicyPicker.tsx +12 -11
  20. package/src/components/chat/ExecutedActionsCard.tsx +45 -33
  21. package/src/components/chat/FilePathCode.tsx +8 -3
  22. package/src/components/chat/InterruptedTurnCard.tsx +4 -3
  23. package/src/components/chat/LocalChatPanel.tsx +2 -1
  24. package/src/components/chat/Markdown.tsx +2 -1
  25. package/src/components/chat/MessageList.tsx +3 -2
  26. package/src/components/chat/ModelPicker.tsx +25 -16
  27. package/src/components/chat/OrchestrationChildrenCard.tsx +2 -1
  28. package/src/components/chat/OrchestrationFlowCard.tsx +23 -22
  29. package/src/components/chat/PlanTodoList.tsx +2 -1
  30. package/src/components/chat/ProjectTrustBanner.tsx +19 -5
  31. package/src/components/chat/SessionOrchestrationFlow.tsx +27 -26
  32. package/src/components/chat/SessionTodoList.tsx +8 -7
  33. package/src/components/chat/SessionTreeModal.tsx +10 -9
  34. package/src/components/chat/StreamingStatus.tsx +4 -3
  35. package/src/components/chat/SuggestedReplies.tsx +3 -2
  36. package/src/components/chat/TaskOutcomeCards.tsx +8 -5
  37. package/src/components/chat/TaskPanelModal.tsx +31 -21
  38. package/src/components/chat/TaskProcessPanel.tsx +14 -13
  39. package/src/components/chat/TurnFilesCard.tsx +14 -13
  40. package/src/components/chat/UserMessage.tsx +12 -11
  41. package/src/components/chat/executed-actions-model.ts +47 -35
  42. package/src/components/chat/orchestration-flow-model.ts +16 -5
  43. package/src/components/chat/process-status.ts +8 -7
  44. package/src/components/chat/todo-list-model.ts +5 -4
  45. package/src/components/chat/turn-files.ts +8 -8
  46. package/src/components/settings/AgentCapabilityFields.tsx +24 -21
  47. package/src/components/settings/AgentsSettingsPanel.tsx +30 -25
  48. package/src/components/settings/AppUpdateSettingsPanel.tsx +3 -2
  49. package/src/components/settings/AppearanceSettingsPanel.tsx +81 -37
  50. package/src/components/settings/DiagnoseSettingsPanel.tsx +11 -8
  51. package/src/components/settings/InsightsSettingsPanel.tsx +43 -28
  52. package/src/components/settings/LlmSettingsPanel.tsx +104 -83
  53. package/src/components/settings/McpSettingsPanel.tsx +193 -71
  54. package/src/components/settings/ModelIdCombobox.tsx +2 -1
  55. package/src/components/settings/OrchestrationSettingsPanel.tsx +8 -5
  56. package/src/components/settings/PortableSettingsPanel.tsx +83 -55
  57. package/src/components/settings/PythonRunnerSettingsPanel.tsx +22 -19
  58. package/src/components/settings/SecuritySettingsPanel.tsx +55 -31
  59. package/src/components/settings/SettingsNavMenu.tsx +3 -2
  60. package/src/components/settings/SettingsSaveButton.tsx +2 -1
  61. package/src/components/settings/SkillsSettingsPanel.tsx +36 -31
  62. package/src/components/settings/TelemetrySettingsPanel.tsx +15 -12
  63. package/src/components/settings/UsagePanel.tsx +19 -20
  64. package/src/components/settings/WebSearchSettingsPanel.tsx +23 -14
  65. package/src/components/settings/llm-vendors.ts +12 -7
  66. package/src/components/settings/model-capabilities.ts +16 -15
  67. package/src/components/settings/preset-choice-model.ts +6 -3
  68. package/src/fixtures/browser-dev-data.json +24 -24
  69. package/src/hooks/useProjects.ts +1 -1
  70. package/src/i18n/agent-label.ts +15 -0
  71. package/src/i18n/index.tsx +68 -0
  72. package/src/i18n/runtime.ts +117 -0
  73. package/src/i18n/translate.ts +65 -0
  74. package/src/layouts/AgentLayout.tsx +7 -6
  75. package/src/lib/app-release.ts +15 -10
  76. package/src/lib/attachments.ts +24 -7
  77. package/src/lib/browser-dev-electron-mock.ts +34 -34
  78. package/src/lib/chat-title.ts +3 -2
  79. package/src/lib/chat-transport.ts +6 -2
  80. package/src/lib/date-groups.ts +23 -15
  81. package/src/lib/electron-bridge.ts +19 -6
  82. package/src/lib/host-file-drop.ts +68 -0
  83. package/src/lib/host-paste.ts +38 -9
  84. package/src/lib/http-bridge.ts +4 -1
  85. package/src/lib/portable.ts +8 -7
  86. package/src/lib/show-thinking-content.ts +15 -3
  87. package/src/lib/tauri-bridge.ts +5 -0
  88. package/src/main.tsx +25 -2
  89. package/src/pages/AgentPage.tsx +17 -12
  90. package/src/pages/ChatPanelPreviewPage.tsx +46 -44
  91. package/src/pages/SettingsPage.tsx +48 -42
  92. package/src/vite-env.d.ts +3 -0
  93. package/vite.base.js +6 -2
@@ -17,6 +17,7 @@ import {
17
17
  } from 'react-icons/lu';
18
18
  import { useOutletContext, useSearchParams } from 'react-router-dom';
19
19
  import { isElectron } from '@/lib/electron-bridge';
20
+ import { getLocale, t } from '@/i18n';
20
21
  import { useAppRelease } from '@/components/SidebarRelease';
21
22
  import { AgentsSettingsPanel } from '@/components/settings/AgentsSettingsPanel';
22
23
  import { AppearanceSettingsPanel } from '@/components/settings/AppearanceSettingsPanel';
@@ -49,24 +50,28 @@ type PluginTab = 'agents' | 'skills' | 'mcp' | 'web-search';
49
50
 
50
51
  const PLUGIN_TABS: readonly PluginTab[] = ['agents', 'skills', 'mcp', 'web-search'];
51
52
 
52
- const PLUGIN_TAB_META: Record<
53
- PluginTab,
54
- { label: string; noun: string; Icon: typeof LuBot }
55
- > = {
56
- agents: { label: '智能体', noun: '智能体', Icon: LuBot },
57
- skills: { label: 'Skills', noun: '技能', Icon: LuBlocks },
58
- mcp: { label: 'MCP', noun: 'MCP 服务', Icon: LuPlug },
59
- 'web-search': { label: '网络搜索', noun: '网络搜索', Icon: LuSearch },
60
- };
53
+ /** 每次渲染现取,文案跟随当前语言。 */
54
+ function pluginTabMeta(): Record<PluginTab, { label: string; noun: string; Icon: typeof LuBot }> {
55
+ return {
56
+ agents: { label: t('Agents'), noun: t('agents'), Icon: LuBot },
57
+ skills: { label: 'Skills', noun: t('skills'), Icon: LuBlocks },
58
+ mcp: { label: 'MCP', noun: t('MCP servers'), Icon: LuPlug },
59
+ 'web-search': { label: t('Web search'), noun: t('web search'), Icon: LuSearch },
60
+ };
61
+ }
61
62
 
62
63
  function isPluginTab(value: string | null): value is PluginTab {
63
64
  return (PLUGIN_TABS as readonly string[]).includes(value ?? '');
64
65
  }
65
66
 
66
67
  function pluginsSummary(tabs: readonly PluginTab[]): string {
67
- const nouns = tabs.map((id) => PLUGIN_TAB_META[id].noun);
68
- if (nouns.length <= 1) return `管理${nouns[0] ?? '插件'}。`;
69
- return `管理${nouns.slice(0, -1).join('、')}和${nouns[nouns.length - 1]}。`;
68
+ const meta = pluginTabMeta();
69
+ const nouns = tabs.map((id) => meta[id].noun);
70
+ const items =
71
+ nouns.length === 0
72
+ ? t('plugins')
73
+ : new Intl.ListFormat(getLocale(), { type: 'conjunction' }).format(nouns);
74
+ return t('Manage {items}.', { items });
70
75
  }
71
76
 
72
77
  function enabledPluginTabs(): PluginTab[] {
@@ -185,30 +190,30 @@ export function SettingsPage() {
185
190
  const list: SettingsNavItem[] = [];
186
191
 
187
192
  if (settingsChrome('appearance')) {
188
- list.push({ id: 'appearance', label: '界面', Icon: LuMonitor });
193
+ list.push({ id: 'appearance', label: t('Interface'), Icon: LuMonitor });
189
194
  }
190
- list.push({ id: 'orchestration', label: '协同编排', Icon: LuGitFork });
195
+ list.push({ id: 'orchestration', label: t('Orchestration'), Icon: LuGitFork });
191
196
 
192
197
  if (settingsChrome('llm')) {
193
- list.push({ id: 'llm', label: '本地模型设置', Icon: LuSettings });
198
+ list.push({ id: 'llm', label: t('Local model settings'), Icon: LuSettings });
194
199
  }
195
200
  if (settingsChrome('web-search')) {
196
- list.push({ id: 'web-search', label: '网络搜索', Icon: LuSearch });
201
+ list.push({ id: 'web-search', label: t('Web search'), Icon: LuSearch });
197
202
  }
198
203
  if (settingsChrome('usage')) {
199
- list.push({ id: 'usage', label: '用量与成本', Icon: LuChartBar });
204
+ list.push({ id: 'usage', label: t('Usage and cost'), Icon: LuChartBar });
200
205
  }
201
206
  if (settingsChrome('diagnose')) {
202
- list.push({ id: 'diagnose', label: '链路诊断', Icon: LuNetwork });
207
+ list.push({ id: 'diagnose', label: t('Connection diagnostics'), Icon: LuNetwork });
203
208
  }
204
209
  if (settingsChrome('security')) {
205
- list.push({ id: 'security', label: '安全', Icon: LuShieldCheck });
210
+ list.push({ id: 'security', label: t('Security'), Icon: LuShieldCheck });
206
211
  }
207
212
  if (settingsChrome('insights')) {
208
- list.push({ id: 'insights', label: '帮助改进产品', Icon: LuChartBar });
213
+ list.push({ id: 'insights', label: t('Help improve the product'), Icon: LuChartBar });
209
214
  }
210
215
  if (settingsChrome('telemetry')) {
211
- list.push({ id: 'telemetry', label: '遥测(OTLP)', Icon: LuActivity });
216
+ list.push({ id: 'telemetry', label: t('Telemetry (OTLP)'), Icon: LuActivity });
212
217
  }
213
218
 
214
219
  const packPanels = getPackSettingsPanels();
@@ -221,13 +226,13 @@ export function SettingsPage() {
221
226
  }
222
227
 
223
228
  if (isPortableEnabled()) {
224
- list.push({ id: 'portable', label: '备份与迁移', Icon: LuDownload });
229
+ list.push({ id: 'portable', label: t('Backup and migration'), Icon: LuDownload });
225
230
  }
226
231
  if (isElectron()) {
227
- list.push({ id: 'python-runner', label: 'Python 运行环境', Icon: LuCodeXml });
232
+ list.push({ id: 'python-runner', label: t('Python runtime'), Icon: LuCodeXml });
228
233
  }
229
234
  if (release.version) {
230
- list.push({ id: 'update', label: '软件更新', Icon: LuInfo });
235
+ list.push({ id: 'update', label: t('Software update'), Icon: LuInfo });
231
236
  }
232
237
 
233
238
  return list;
@@ -271,7 +276,8 @@ export function SettingsPage() {
271
276
  }
272
277
  }, [menuItems]);
273
278
 
274
- const title = section === 'plugins' ? '插件' : '设置';
279
+ const title = section === 'plugins' ? t('Plugins') : t('Settings');
280
+ const pluginMeta = pluginTabMeta();
275
281
 
276
282
  return (
277
283
  <div ref={pageContainerRef} className="flex h-full w-full flex-col overflow-hidden">
@@ -281,12 +287,12 @@ export function SettingsPage() {
281
287
  {section === 'plugins' && pluginTabs.length > 1 && (
282
288
  <div
283
289
  role="tablist"
284
- aria-label="插件"
290
+ aria-label={t('Plugins')}
285
291
  data-testid="plugins-tabs"
286
292
  className="flex min-w-0 items-center gap-1"
287
293
  >
288
294
  {pluginTabs.map((id) => {
289
- const meta = PLUGIN_TAB_META[id];
295
+ const meta = pluginMeta[id];
290
296
  const active = pluginTab === id;
291
297
  return (
292
298
  <button
@@ -343,7 +349,7 @@ export function SettingsPage() {
343
349
  <div ref={contentRef} className="min-w-0 flex-1 max-w-3xl space-y-4">
344
350
  {!isElectron() && (
345
351
  <p className="rounded-agent-md border border-agent-destructive/20 bg-agent-destructive/10 p-2.5 text-xs text-agent-destructive">
346
- 浏览器预览模式 — 没有 Electron IPC 桥接,部分设置不可用。
352
+ {t('Browser preview mode: no Electron IPC bridge, so some settings are unavailable.')}
347
353
  </p>
348
354
  )}
349
355
 
@@ -356,7 +362,7 @@ export function SettingsPage() {
356
362
  <section className="space-y-2" data-testid="settings-section-skills">
357
363
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
358
364
  <LuBlocks className="h-3.5 w-3.5 text-agent-muted-foreground" />
359
- 本地技能
365
+ {t('Local skills')}
360
366
  </h2>
361
367
  <SkillsSettingsPanel />
362
368
  </section>
@@ -365,7 +371,7 @@ export function SettingsPage() {
365
371
  <section className="space-y-2" data-testid="settings-section-mcp">
366
372
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
367
373
  <LuPlug className="h-3.5 w-3.5 text-agent-muted-foreground" />
368
- MCP 服务
374
+ {t('MCP servers')}
369
375
  </h2>
370
376
  <McpSettingsPanel />
371
377
  </section>
@@ -374,7 +380,7 @@ export function SettingsPage() {
374
380
  <section className="space-y-2" data-testid="settings-section-agents">
375
381
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
376
382
  <LuBot className="h-3.5 w-3.5 text-agent-muted-foreground" />
377
- 智能体
383
+ {t('Agents')}
378
384
  </h2>
379
385
  <AgentsSettingsPanel onCatalogChange={catalog?.refreshAgents} />
380
386
  </section>
@@ -383,7 +389,7 @@ export function SettingsPage() {
383
389
  <section className="space-y-2" data-testid="settings-section-web-search">
384
390
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
385
391
  <LuSearch className="h-3.5 w-3.5 text-agent-muted-foreground" />
386
- 网络搜索
392
+ {t('Web search')}
387
393
  </h2>
388
394
  <WebSearchSettingsPanel />
389
395
  </section>
@@ -397,7 +403,7 @@ export function SettingsPage() {
397
403
  <section className="space-y-2" data-testid="settings-section-appearance">
398
404
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
399
405
  <LuMonitor className="h-3.5 w-3.5 text-agent-muted-foreground" />
400
- 界面
406
+ {t('Interface')}
401
407
  </h2>
402
408
  <AppearanceSettingsPanel />
403
409
  </section>
@@ -406,7 +412,7 @@ export function SettingsPage() {
406
412
  <section className="space-y-2" data-testid="settings-section-orchestration">
407
413
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
408
414
  <LuSettings className="h-3.5 w-3.5 text-agent-muted-foreground" />
409
- 协同编排
415
+ {t('Orchestration')}
410
416
  </h2>
411
417
  <OrchestrationSettingsPanel />
412
418
  </section>
@@ -415,7 +421,7 @@ export function SettingsPage() {
415
421
  <section className="space-y-2" data-testid="settings-section-llm">
416
422
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
417
423
  <LuSettings className="h-3.5 w-3.5 text-agent-muted-foreground" />
418
- 本地模型设置
424
+ {t('Local model settings')}
419
425
  </h2>
420
426
  <LlmSettingsPanel
421
427
  ref={llmPanelRef}
@@ -429,7 +435,7 @@ export function SettingsPage() {
429
435
  <section className="space-y-2" data-testid="settings-section-web-search">
430
436
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
431
437
  <LuSearch className="h-3.5 w-3.5 text-agent-muted-foreground" />
432
- 网络搜索
438
+ {t('Web search')}
433
439
  </h2>
434
440
  <WebSearchSettingsPanel />
435
441
  </section>
@@ -439,7 +445,7 @@ export function SettingsPage() {
439
445
  <section className="space-y-2" data-testid="settings-section-usage">
440
446
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
441
447
  <LuChartBar className="h-3.5 w-3.5 text-agent-muted-foreground" />
442
- 用量与成本
448
+ {t('Usage and cost')}
443
449
  </h2>
444
450
  <UsagePanel />
445
451
  </section>
@@ -449,7 +455,7 @@ export function SettingsPage() {
449
455
  <section className="space-y-2" data-testid="settings-section-diagnose">
450
456
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
451
457
  <LuNetwork className="h-3.5 w-3.5 text-agent-muted-foreground" />
452
- 链路诊断
458
+ {t('Connection diagnostics')}
453
459
  </h2>
454
460
  <DiagnoseSettingsPanel />
455
461
  </section>
@@ -459,7 +465,7 @@ export function SettingsPage() {
459
465
  <section className="space-y-2" data-testid="settings-section-security">
460
466
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
461
467
  <LuShieldCheck className="h-3.5 w-3.5 text-agent-muted-foreground" />
462
- 安全
468
+ {t('Security')}
463
469
  </h2>
464
470
  <SecuritySettingsPanel />
465
471
  </section>
@@ -469,7 +475,7 @@ export function SettingsPage() {
469
475
  <section className="space-y-2" data-testid="settings-section-insights">
470
476
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
471
477
  <LuChartBar className="h-3.5 w-3.5 text-agent-muted-foreground" />
472
- 帮助改进产品
478
+ {t('Help improve the product')}
473
479
  </h2>
474
480
  <InsightsSettingsPanel />
475
481
  </section>
@@ -479,7 +485,7 @@ export function SettingsPage() {
479
485
  <section className="space-y-2" data-testid="settings-section-telemetry">
480
486
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
481
487
  <LuActivity className="h-3.5 w-3.5 text-agent-muted-foreground" />
482
- 遥测(OTLP)
488
+ {t('Telemetry (OTLP)')}
483
489
  </h2>
484
490
  <TelemetrySettingsPanel />
485
491
  </section>
@@ -507,7 +513,7 @@ export function SettingsPage() {
507
513
  <section className="space-y-2" data-testid="settings-section-portable">
508
514
  <h2 className="flex items-center gap-1.5 text-xs font-semibold text-agent-foreground">
509
515
  <LuDownload className="h-3.5 w-3.5 text-agent-muted-foreground" />
510
- 备份与迁移
516
+ {t('Backup and migration')}
511
517
  </h2>
512
518
  <PortableSettingsPanel />
513
519
  </section>
package/src/vite-env.d.ts CHANGED
@@ -14,6 +14,9 @@ interface ImportMetaEnv {
14
14
  readonly VITE_SETTINGS?: string;
15
15
  /** `'true'` 才打开对话与配置的导出/导入。缺省关。 */
16
16
  readonly VITE_PORTABLE?: string;
17
+ /** JSON 数组,产品声明的界面语言。缺省 `["en"]`。 */
18
+ readonly VITE_LOCALES?: string;
19
+ readonly VITE_DEFAULT_LOCALE?: string;
17
20
  }
18
21
 
19
22
  interface ImportMeta {
package/vite.base.js CHANGED
@@ -51,8 +51,8 @@ export function createProductViteConfig(options) {
51
51
  // 在注册时经 setBrandLogoUrl 注入(本包 src/brand.ts)。
52
52
  'import.meta.env.VITE_BRAND_NAME': JSON.stringify(brand?.displayName ?? 'Steerable Shell'),
53
53
  'import.meta.env.VITE_BRAND_TITLE': JSON.stringify(brand?.title ?? ''),
54
- 'import.meta.env.VITE_BRAND_TAGLINE': JSON.stringify(brand?.tagline ?? '一款本地桌面 AI 伙伴'),
55
- 'import.meta.env.VITE_BRAND_HOME_HINT': JSON.stringify(brand?.homeHint ?? '输入消息,直接开始一段新对话。'),
54
+ 'import.meta.env.VITE_BRAND_TAGLINE': JSON.stringify(brand?.tagline ?? 'A local desktop AI partner'),
55
+ 'import.meta.env.VITE_BRAND_HOME_HINT': JSON.stringify(brand?.homeHint ?? 'Type a message to start a new chat.'),
56
56
  'import.meta.env.VITE_DEFAULT_AGENT_ID': JSON.stringify(brand?.defaultAgentId ?? 'local-assistant'),
57
57
  // 宿主工具族 / 安全询问:与 node 侧 setProductConfig 读同一份 product.json。
58
58
  'import.meta.env.VITE_HOST_TOOLS': JSON.stringify(JSON.stringify(product.hostTools ?? {})),
@@ -60,6 +60,10 @@ export function createProductViteConfig(options) {
60
60
  'import.meta.env.VITE_CHAT_MODES': JSON.stringify(JSON.stringify(product.chatModes ?? ['agent', 'plan'])),
61
61
  'import.meta.env.VITE_SETTINGS': JSON.stringify(JSON.stringify(product.settings ?? {})),
62
62
  'import.meta.env.VITE_PORTABLE': JSON.stringify(product.portable === true ? 'true' : 'false'),
63
+ 'import.meta.env.VITE_LOCALES': JSON.stringify(JSON.stringify(Array.isArray(product.i18n?.locales) && product.i18n.locales.length > 0
64
+ ? product.i18n.locales
65
+ : ['en'])),
66
+ 'import.meta.env.VITE_DEFAULT_LOCALE': JSON.stringify(product.i18n?.defaultLocale ?? 'en'),
63
67
  },
64
68
  resolve: {
65
69
  alias: {