snow-ai 0.3.36 → 0.4.0

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 (97) hide show
  1. package/dist/agents/codebaseIndexAgent.js +1 -0
  2. package/dist/agents/codebaseReviewAgent.d.ts +61 -0
  3. package/dist/agents/codebaseReviewAgent.js +301 -0
  4. package/dist/agents/promptOptimizeAgent.d.ts +54 -0
  5. package/dist/agents/promptOptimizeAgent.js +268 -0
  6. package/dist/api/anthropic.js +1 -0
  7. package/dist/api/chat.js +1 -0
  8. package/dist/api/embedding.js +1 -0
  9. package/dist/api/gemini.js +2 -1
  10. package/dist/api/responses.js +1 -0
  11. package/dist/api/systemPrompt.d.ts +1 -5
  12. package/dist/api/systemPrompt.js +168 -100
  13. package/dist/app.js +14 -6
  14. package/dist/cli.js +1 -1
  15. package/dist/hooks/useCommandPanel.js +48 -46
  16. package/dist/hooks/useConversation.d.ts +2 -1
  17. package/dist/hooks/useConversation.js +116 -30
  18. package/dist/hooks/useGlobalExit.js +4 -2
  19. package/dist/hooks/useStreamingState.d.ts +9 -0
  20. package/dist/hooks/useStreamingState.js +3 -0
  21. package/dist/i18n/I18nContext.d.ts +14 -0
  22. package/dist/i18n/I18nContext.js +24 -0
  23. package/dist/i18n/index.d.ts +3 -0
  24. package/dist/i18n/index.js +2 -0
  25. package/dist/i18n/lang/en.d.ts +2 -0
  26. package/dist/i18n/lang/en.js +483 -0
  27. package/dist/i18n/lang/es.d.ts +2 -0
  28. package/dist/i18n/lang/es.js +483 -0
  29. package/dist/i18n/lang/ja.d.ts +2 -0
  30. package/dist/i18n/lang/ja.js +483 -0
  31. package/dist/i18n/lang/ko.d.ts +2 -0
  32. package/dist/i18n/lang/ko.js +483 -0
  33. package/dist/i18n/lang/zh-TW.d.ts +2 -0
  34. package/dist/i18n/lang/zh-TW.js +483 -0
  35. package/dist/i18n/lang/zh.d.ts +2 -0
  36. package/dist/i18n/lang/zh.js +483 -0
  37. package/dist/i18n/translations.d.ts +2 -0
  38. package/dist/i18n/translations.js +14 -0
  39. package/dist/i18n/types.d.ts +459 -0
  40. package/dist/i18n/types.js +1 -0
  41. package/dist/mcp/aceCodeSearch.d.ts +17 -48
  42. package/dist/mcp/aceCodeSearch.js +24 -56
  43. package/dist/mcp/bash.js +8 -1
  44. package/dist/mcp/codebaseSearch.d.ts +1 -1
  45. package/dist/mcp/codebaseSearch.js +159 -30
  46. package/dist/mcp/filesystem.d.ts +3 -80
  47. package/dist/mcp/filesystem.js +23 -103
  48. package/dist/mcp/subagent.d.ts +2 -1
  49. package/dist/mcp/subagent.js +54 -5
  50. package/dist/ui/components/ChatInput.js +22 -25
  51. package/dist/ui/components/CommandPanel.d.ts +1 -1
  52. package/dist/ui/components/CommandPanel.js +20 -13
  53. package/dist/ui/components/DiffViewer.d.ts +1 -1
  54. package/dist/ui/components/DiffViewer.js +101 -91
  55. package/dist/ui/components/FileList.js +22 -11
  56. package/dist/ui/components/HelpPanel.js +47 -21
  57. package/dist/ui/components/Menu.js +6 -2
  58. package/dist/ui/components/MessageList.d.ts +6 -0
  59. package/dist/ui/components/MessageList.js +1 -1
  60. package/dist/ui/components/ToolConfirmation.d.ts +4 -1
  61. package/dist/ui/components/ToolConfirmation.js +28 -2
  62. package/dist/ui/components/ToolResultPreview.d.ts +2 -1
  63. package/dist/ui/components/ToolResultPreview.js +41 -25
  64. package/dist/ui/pages/ChatScreen.js +177 -56
  65. package/dist/ui/pages/CodeBaseConfigScreen.js +54 -30
  66. package/dist/ui/pages/ConfigScreen.js +138 -98
  67. package/dist/ui/pages/CustomHeadersScreen.js +75 -69
  68. package/dist/ui/pages/LanguageSettingsScreen.d.ts +7 -0
  69. package/dist/ui/pages/LanguageSettingsScreen.js +89 -0
  70. package/dist/ui/pages/ProxyConfigScreen.js +27 -23
  71. package/dist/ui/pages/SensitiveCommandConfigScreen.js +32 -25
  72. package/dist/ui/pages/SubAgentConfigScreen.js +88 -75
  73. package/dist/ui/pages/SystemPromptConfigScreen.js +31 -26
  74. package/dist/ui/pages/WelcomeScreen.js +40 -26
  75. package/dist/utils/apiConfig.d.ts +2 -0
  76. package/dist/utils/codebaseConfig.d.ts +1 -5
  77. package/dist/utils/codebaseConfig.js +2 -10
  78. package/dist/utils/codebaseSearchEvents.d.ts +16 -0
  79. package/dist/utils/codebaseSearchEvents.js +13 -0
  80. package/dist/utils/commands/agent.js +2 -2
  81. package/dist/utils/commands/init.js +1 -1
  82. package/dist/utils/configManager.js +26 -5
  83. package/dist/utils/contextCompressor.js +1 -1
  84. package/dist/utils/languageConfig.d.ts +21 -0
  85. package/dist/utils/languageConfig.js +61 -0
  86. package/dist/utils/mcpToolsManager.js +0 -9
  87. package/dist/utils/notebookManager.js +11 -4
  88. package/dist/utils/sessionConverter.js +13 -3
  89. package/dist/utils/sessionManager.d.ts +1 -0
  90. package/dist/utils/subAgentConfig.d.ts +10 -5
  91. package/dist/utils/subAgentConfig.js +112 -19
  92. package/dist/utils/subAgentExecutor.d.ts +9 -1
  93. package/dist/utils/subAgentExecutor.js +122 -9
  94. package/dist/utils/toolExecutor.d.ts +2 -1
  95. package/dist/utils/toolExecutor.js +1 -2
  96. package/dist/utils/usageLogger.js +18 -3
  97. package/package.json +2 -1
@@ -4,7 +4,9 @@ import Gradient from 'ink-gradient';
4
4
  import { Alert } from '@inkjs/ui';
5
5
  import TextInput from 'ink-text-input';
6
6
  import { getCustomHeadersConfig, saveCustomHeadersConfig, } from '../../utils/apiConfig.js';
7
+ import { useI18n } from '../../i18n/index.js';
7
8
  export default function CustomHeadersScreen({ onBack }) {
9
+ const { t } = useI18n();
8
10
  const [config, setConfig] = useState(() => {
9
11
  return (getCustomHeadersConfig() || {
10
12
  active: '',
@@ -45,7 +47,7 @@ export default function CustomHeadersScreen({ onBack }) {
45
47
  return true;
46
48
  }
47
49
  catch (err) {
48
- setError(err instanceof Error ? err.message : 'Failed to save');
50
+ setError(err instanceof Error ? err.message : t.customHeaders.saveError);
49
51
  return false;
50
52
  }
51
53
  };
@@ -341,18 +343,18 @@ export default function CustomHeadersScreen({ onBack }) {
341
343
  return (React.createElement(Box, { flexDirection: "column", padding: 1 },
342
344
  React.createElement(Box, { marginBottom: 1, borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1 },
343
345
  React.createElement(Box, { flexDirection: "column" },
344
- React.createElement(Gradient, { name: "rainbow" }, "Custom Headers Management"),
345
- React.createElement(Text, { color: "gray", dimColor: true }, "Manage multiple header schemes and switch between them"))),
346
+ React.createElement(Gradient, { name: "rainbow" }, t.customHeaders.title),
347
+ React.createElement(Text, { color: "gray", dimColor: true }, t.customHeaders.subtitle))),
346
348
  error && (React.createElement(Box, { marginBottom: 1 },
347
349
  React.createElement(Alert, { variant: "error" }, error))),
348
350
  React.createElement(Box, { marginBottom: 1 },
349
351
  React.createElement(Text, { bold: true },
350
- "Active Scheme:",
352
+ t.customHeaders.activeScheme,
351
353
  ' ',
352
- React.createElement(Text, { color: "green" }, activeScheme?.name || 'None'))),
354
+ React.createElement(Text, { color: "green" }, activeScheme?.name || t.customHeaders.none))),
353
355
  config.schemes.length === 0 ? (React.createElement(Box, { marginBottom: 1 },
354
- React.createElement(Text, { color: "yellow" }, "No header schemes configured. Press Enter to add one."))) : (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
355
- React.createElement(Text, { bold: true, color: "cyan" }, "Available Schemes:"),
356
+ React.createElement(Text, { color: "yellow" }, t.customHeaders.noSchemesConfigured))) : (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
357
+ React.createElement(Text, { bold: true, color: "cyan" }, t.customHeaders.availableSchemes),
356
358
  config.schemes.map((scheme, index) => {
357
359
  const headerCount = Object.keys(scheme.headers).length;
358
360
  const headerPreview = headerCount > 0
@@ -377,17 +379,17 @@ export default function CustomHeadersScreen({ onBack }) {
377
379
  headerPreview.length > 50 ? '...' : '')))));
378
380
  }))),
379
381
  React.createElement(Box, { marginBottom: 1 },
380
- React.createElement(Text, { bold: true, color: "cyan" }, "Actions:")),
382
+ React.createElement(Text, { bold: true, color: "cyan" }, t.customHeaders.actions)),
381
383
  React.createElement(Box, { flexDirection: "column", marginBottom: 1, marginLeft: 2 }, actions.map(action => (React.createElement(Text, { key: action, color: currentAction === action ? 'green' : 'gray', bold: currentAction === action },
382
384
  currentAction === action ? '❯ ' : ' ',
383
- action === 'activate' && 'Activate',
384
- action === 'deactivate' && 'Deactivate',
385
- action === 'edit' && 'Edit',
386
- action === 'delete' && 'Delete',
387
- action === 'add' && 'Add New',
388
- action === 'back' && '[ESC] Back')))),
385
+ action === 'activate' && t.customHeaders.activate,
386
+ action === 'deactivate' && t.customHeaders.deactivate,
387
+ action === 'edit' && t.customHeaders.edit,
388
+ action === 'delete' && t.customHeaders.delete,
389
+ action === 'add' && t.customHeaders.addNew,
390
+ action === 'back' && t.customHeaders.escBack)))),
389
391
  React.createElement(Box, { marginTop: 1 },
390
- React.createElement(Text, { color: "gray", dimColor: true }, "Use \u2191\u2193 to select scheme, \u2190\u2192 to select action, Enter to confirm"))));
392
+ React.createElement(Text, { color: "gray", dimColor: true }, t.customHeaders.navigationHint))));
391
393
  }
392
394
  // Render add/edit view
393
395
  if (view === 'add' || view === 'edit') {
@@ -397,50 +399,54 @@ export default function CustomHeadersScreen({ onBack }) {
397
399
  .slice(0, 3)
398
400
  .map(([k, v]) => `${k}: ${v}`)
399
401
  .join(', ')
400
- : 'Not set';
402
+ : t.customHeaders.notSet;
401
403
  return (React.createElement(Box, { flexDirection: "column", padding: 1 },
402
404
  React.createElement(Box, { marginBottom: 1, borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1 },
403
- React.createElement(Gradient, { name: "rainbow" }, view === 'add' ? 'Add New Header Scheme' : 'Edit Header Scheme')),
405
+ React.createElement(Gradient, { name: "rainbow" }, view === 'add'
406
+ ? t.customHeaders.addNewTitle
407
+ : t.customHeaders.editTitle)),
404
408
  error && (React.createElement(Box, { marginBottom: 1 },
405
409
  React.createElement(Alert, { variant: "error" }, error))),
406
- React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
407
- React.createElement(Box, { marginBottom: 1 },
408
- React.createElement(Box, { flexDirection: "column" },
409
- React.createElement(Text, { color: editingField === 'name' ? 'green' : 'white' },
410
- editingField === 'name' ? '❯ ' : ' ',
411
- "Name:"),
412
- editingField === 'name' && isEditing && (React.createElement(Box, { marginLeft: 3 },
413
- React.createElement(TextInput, { value: editName, onChange: setEditName, placeholder: "Enter scheme name" }))),
414
- (!isEditing || editingField !== 'name') && (React.createElement(Box, { marginLeft: 3 },
415
- React.createElement(Text, { color: "gray" }, editName || 'Not set'))))),
416
- React.createElement(Box, { marginBottom: 1 },
417
- React.createElement(Box, { flexDirection: "column" },
418
- React.createElement(Text, { color: editingField === 'headers' ? 'green' : 'white' },
419
- editingField === 'headers' ? '❯ ' : ' ',
420
- "Headers (",
421
- headerCount,
422
- ' ',
423
- "configured):"),
424
- editingField === 'headers' && !isEditing ? (React.createElement(Box, { marginLeft: 3 },
425
- React.createElement(Text, { color: "cyan", dimColor: true }, "Press Enter to edit headers \u2192"))) : (React.createElement(Box, { marginLeft: 3 },
426
- React.createElement(Text, { color: "gray" },
427
- headerPreview.substring(0, 100),
428
- headerPreview.length > 100 ? '...' : '')))))),
410
+ React.createElement(Box, { marginBottom: 1 },
411
+ React.createElement(Box, { flexDirection: "column" },
412
+ React.createElement(Text, { color: editingField === 'name' ? 'green' : 'white' },
413
+ editingField === 'name' ? '' : ' ',
414
+ t.customHeaders.nameLabel),
415
+ editingField === 'name' && isEditing && (React.createElement(Box, { marginLeft: 3 },
416
+ React.createElement(TextInput, { value: editName, onChange: setEditName, placeholder: t.customHeaders.enterSchemeName }))),
417
+ (!isEditing || editingField !== 'name') && (React.createElement(Box, { marginLeft: 3 },
418
+ React.createElement(Text, { color: "gray" }, editName || t.customHeaders.notSet))))),
419
+ React.createElement(Box, { marginBottom: 1 },
420
+ React.createElement(Box, { flexDirection: "column" },
421
+ React.createElement(Text, { color: editingField === 'headers' ? 'green' : 'white' },
422
+ editingField === 'headers' ? '' : ' ',
423
+ t.customHeaders.headersLabel,
424
+ " (",
425
+ headerCount,
426
+ ' ',
427
+ t.customHeaders.headersConfigured,
428
+ "):"),
429
+ editingField === 'headers' && !isEditing ? (React.createElement(Box, { marginLeft: 3 },
430
+ React.createElement(Text, { color: "cyan", dimColor: true }, t.customHeaders.pressEnterToEdit))) : (React.createElement(Box, { marginLeft: 3 },
431
+ React.createElement(Text, { color: "gray" },
432
+ headerPreview.substring(0, 100),
433
+ headerPreview.length > 100 ? '...' : ''))))),
429
434
  React.createElement(Box, { marginTop: 1 },
430
- React.createElement(Text, { color: "gray", dimColor: true }, "\u2191\u2193: Navigate fields | Enter: Edit | Ctrl+S: Save | ESC: Cancel"))));
435
+ React.createElement(Text, { color: "gray", dimColor: true }, t.customHeaders.editingHint))));
431
436
  }
432
437
  // Render headers edit view
433
438
  if (view === 'editHeaders') {
434
439
  return (React.createElement(Box, { flexDirection: "column", padding: 1 },
435
440
  React.createElement(Box, { marginBottom: 1, borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1 },
436
441
  React.createElement(Gradient, { name: "rainbow" },
437
- "Edit Headers - ",
442
+ t.customHeaders.editHeadersTitle,
443
+ " - ",
438
444
  editName)),
439
445
  headerEditingIndex === -1 ? (React.createElement(React.Fragment, null,
440
446
  React.createElement(Box, { marginBottom: 1 },
441
- React.createElement(Text, { bold: true, color: "cyan" }, "Header List:")),
447
+ React.createElement(Text, { bold: true, color: "cyan" }, t.customHeaders.headerList)),
442
448
  headerKeys.length === 0 ? (React.createElement(Box, { marginBottom: 1 },
443
- React.createElement(Text, { color: "yellow" }, "No headers configured. Press Enter to add one."))) : (React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, headerKeys.map((key, index) => {
449
+ React.createElement(Text, { color: "yellow" }, t.customHeaders.noHeadersConfigured))) : (React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, headerKeys.map((key, index) => {
444
450
  const isSelected = index === headerSelectedIndex;
445
451
  return (React.createElement(Box, { key: index, marginLeft: 2 },
446
452
  React.createElement(Text, { color: isSelected ? 'green' : 'white', bold: isSelected },
@@ -452,43 +458,43 @@ export default function CustomHeadersScreen({ onBack }) {
452
458
  React.createElement(Box, { marginLeft: 2, marginBottom: 1 },
453
459
  React.createElement(Text, { color: headerSelectedIndex === headerKeys.length ? 'green' : 'gray', bold: headerSelectedIndex === headerKeys.length },
454
460
  headerSelectedIndex === headerKeys.length ? '❯ ' : ' ',
455
- "[+] Add new header")),
461
+ t.customHeaders.addNewHeader)),
456
462
  React.createElement(Box, { marginTop: 1 },
457
- React.createElement(Text, { color: "gray", dimColor: true }, "\u2191\u2193: Navigate | Enter: Edit/Add | D: Delete | ESC: Finish")))) : (React.createElement(React.Fragment, null,
458
- React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
459
- React.createElement(Box, { marginBottom: 1 },
460
- React.createElement(Box, { flexDirection: "column" },
461
- React.createElement(Text, { color: headerEditingField === 'key' ? 'green' : 'white' },
462
- headerEditingField === 'key' ? '❯ ' : ' ',
463
- "Key:"),
464
- headerEditingField === 'key' && isEditing && (React.createElement(Box, { marginLeft: 3 },
465
- React.createElement(TextInput, { value: headerEditKey, onChange: setHeaderEditKey, placeholder: "Header key (e.g., X-API-Key)" }))),
466
- (!isEditing || headerEditingField !== 'key') && (React.createElement(Box, { marginLeft: 3 },
467
- React.createElement(Text, { color: "gray" }, headerEditKey || 'Not set'))))),
468
- React.createElement(Box, { marginBottom: 1 },
469
- React.createElement(Box, { flexDirection: "column" },
470
- React.createElement(Text, { color: headerEditingField === 'value' ? 'green' : 'white' },
471
- headerEditingField === 'value' ? '❯ ' : ' ',
472
- "Value:"),
473
- headerEditingField === 'value' && isEditing && (React.createElement(Box, { marginLeft: 3 },
474
- React.createElement(TextInput, { value: headerEditValue, onChange: setHeaderEditValue, placeholder: "Header value" }))),
475
- (!isEditing || headerEditingField !== 'value') && (React.createElement(Box, { marginLeft: 3 },
476
- React.createElement(Text, { color: "gray" }, headerEditValue || 'Not set')))))),
463
+ React.createElement(Text, { color: "gray", dimColor: true }, t.customHeaders.headerNavigationHint)))) : (React.createElement(React.Fragment, null,
464
+ React.createElement(Box, { marginBottom: 1 },
465
+ React.createElement(Box, { flexDirection: "column" },
466
+ React.createElement(Text, { color: headerEditingField === 'key' ? 'green' : 'white' },
467
+ headerEditingField === 'key' ? '' : ' ',
468
+ t.customHeaders.keyLabel),
469
+ headerEditingField === 'key' && isEditing && (React.createElement(Box, { marginLeft: 3 },
470
+ React.createElement(TextInput, { value: headerEditKey, onChange: setHeaderEditKey, placeholder: t.customHeaders.headerKeyPlaceholder }))),
471
+ (!isEditing || headerEditingField !== 'key') && (React.createElement(Box, { marginLeft: 3 },
472
+ React.createElement(Text, { color: "gray" }, headerEditKey || t.customHeaders.notSet))))),
473
+ React.createElement(Box, { marginBottom: 1 },
474
+ React.createElement(Box, { flexDirection: "column" },
475
+ React.createElement(Text, { color: headerEditingField === 'value' ? 'green' : 'white' },
476
+ headerEditingField === 'value' ? '' : ' ',
477
+ t.customHeaders.valueLabel),
478
+ headerEditingField === 'value' && isEditing && (React.createElement(Box, { marginLeft: 3 },
479
+ React.createElement(TextInput, { value: headerEditValue, onChange: setHeaderEditValue, placeholder: t.customHeaders.headerValuePlaceholder }))),
480
+ (!isEditing || headerEditingField !== 'value') && (React.createElement(Box, { marginLeft: 3 },
481
+ React.createElement(Text, { color: "gray" }, headerEditValue || t.customHeaders.notSet))))),
477
482
  React.createElement(Box, { marginTop: 1 },
478
- React.createElement(Text, { color: "gray", dimColor: true }, "\u2191\u2193: Navigate fields | Enter: Edit | Ctrl+S: Save | ESC: Cancel"))))));
483
+ React.createElement(Text, { color: "gray", dimColor: true }, t.customHeaders.headerEditingHint))))));
479
484
  }
480
485
  // Render delete confirmation
481
486
  if (view === 'confirmDelete') {
482
487
  const schemeToDelete = config.schemes.length > 0 ? config.schemes[selectedIndex] : null;
483
488
  return (React.createElement(Box, { flexDirection: "column", padding: 1 },
484
- React.createElement(Alert, { variant: "warning" }, "Confirm Delete"),
489
+ React.createElement(Alert, { variant: "warning" }, t.customHeaders.confirmDelete),
485
490
  React.createElement(Box, { marginBottom: 1 },
486
491
  React.createElement(Text, null,
487
- "Are you sure you want to delete \"",
492
+ t.customHeaders.deleteConfirmMessage,
493
+ " \"",
488
494
  React.createElement(Text, { bold: true, color: "yellow" }, schemeToDelete?.name),
489
495
  "\"?")),
490
496
  React.createElement(Box, { marginTop: 1 },
491
- React.createElement(Text, { color: "gray", dimColor: true }, "Press Y to confirm, N or ESC to cancel"))));
497
+ React.createElement(Text, { color: "gray", dimColor: true }, t.customHeaders.confirmHint))));
492
498
  }
493
499
  return null;
494
500
  }
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ onBack: () => void;
4
+ inlineMode?: boolean;
5
+ };
6
+ export default function LanguageSettingsScreen({ onBack, inlineMode, }: Props): React.JSX.Element;
7
+ export {};
@@ -0,0 +1,89 @@
1
+ import React, { useState, useCallback } from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+ import Menu from '../components/Menu.js';
4
+ import { useI18n } from '../../i18n/index.js';
5
+ export default function LanguageSettingsScreen({ onBack, inlineMode = false, }) {
6
+ const { language, setLanguage } = useI18n();
7
+ const [selectedLanguage, setSelectedLanguage] = useState(language);
8
+ const languageOptions = [
9
+ {
10
+ label: 'English',
11
+ value: 'en',
12
+ infoText: 'Switch to English',
13
+ },
14
+ {
15
+ label: '简体中文',
16
+ value: 'zh',
17
+ infoText: '切换到简体中文',
18
+ },
19
+ {
20
+ label: '繁體中文',
21
+ value: 'zh-TW',
22
+ infoText: '切換到繁體中文',
23
+ },
24
+ {
25
+ label: '日本語',
26
+ value: 'ja',
27
+ infoText: '日本語に切り替え',
28
+ },
29
+ {
30
+ label: '한국어',
31
+ value: 'ko',
32
+ infoText: '한국어로 전환',
33
+ },
34
+ {
35
+ label: 'Español',
36
+ value: 'es',
37
+ infoText: 'Cambiar a Español',
38
+ },
39
+ {
40
+ label: '← Back',
41
+ value: 'back',
42
+ color: 'gray',
43
+ infoText: 'Return to main menu',
44
+ },
45
+ ];
46
+ const handleSelect = useCallback((value) => {
47
+ if (value === 'back') {
48
+ onBack();
49
+ }
50
+ else {
51
+ const newLang = value;
52
+ setSelectedLanguage(newLang);
53
+ setLanguage(newLang);
54
+ // Auto return to menu after selection
55
+ setTimeout(() => {
56
+ onBack();
57
+ }, 300);
58
+ }
59
+ }, [onBack, setLanguage]);
60
+ const handleSelectionChange = useCallback((_infoText) => {
61
+ // Could update some info display here if needed
62
+ }, []);
63
+ useInput((_input, key) => {
64
+ if (key.escape) {
65
+ onBack();
66
+ }
67
+ });
68
+ return (React.createElement(Box, { flexDirection: "column", paddingX: 1 },
69
+ !inlineMode && (React.createElement(Box, { borderStyle: "round", borderColor: "cyan", paddingX: 1, marginBottom: 1 },
70
+ React.createElement(Box, { flexDirection: "column" },
71
+ React.createElement(Text, { bold: true, color: "cyan" }, "Language Settings / \u8BED\u8A00\u8BBE\u7F6E")))),
72
+ React.createElement(Box, { flexDirection: "column" },
73
+ React.createElement(Box, { paddingX: 1 },
74
+ React.createElement(Text, { color: "gray", dimColor: true },
75
+ "Current:",
76
+ ' ',
77
+ selectedLanguage === 'en'
78
+ ? 'English'
79
+ : selectedLanguage === 'zh'
80
+ ? '简体中文'
81
+ : selectedLanguage === 'zh-TW'
82
+ ? '繁體中文'
83
+ : selectedLanguage === 'ja'
84
+ ? '日本語'
85
+ : selectedLanguage === 'ko'
86
+ ? '한국어'
87
+ : 'Español')),
88
+ React.createElement(Menu, { options: languageOptions, onSelect: handleSelect, onSelectionChange: handleSelectionChange }))));
89
+ }
@@ -4,7 +4,9 @@ import Gradient from 'ink-gradient';
4
4
  import { Alert } from '@inkjs/ui';
5
5
  import TextInput from 'ink-text-input';
6
6
  import { getProxyConfig, updateProxyConfig, } from '../../utils/apiConfig.js';
7
+ import { useI18n } from '../../i18n/index.js';
7
8
  export default function ProxyConfigScreen({ onBack, onSave, inlineMode = false, }) {
9
+ const { t } = useI18n();
8
10
  const [enabled, setEnabled] = useState(false);
9
11
  const [port, setPort] = useState('7890');
10
12
  const [browserPath, setBrowserPath] = useState('');
@@ -21,7 +23,7 @@ export default function ProxyConfigScreen({ onBack, onSave, inlineMode = false,
21
23
  const validationErrors = [];
22
24
  const portNum = parseInt(port, 10);
23
25
  if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
24
- validationErrors.push('Port must be a number between 1 and 65535');
26
+ validationErrors.push(t.proxyConfig.portValidationError);
25
27
  }
26
28
  return validationErrors;
27
29
  };
@@ -88,56 +90,58 @@ export default function ProxyConfigScreen({ onBack, onSave, inlineMode = false,
88
90
  return (React.createElement(Box, { flexDirection: "column", padding: 1 },
89
91
  !inlineMode && (React.createElement(Box, { marginBottom: 1, borderStyle: "double", borderColor: 'cyan', paddingX: 2, paddingY: 1 },
90
92
  React.createElement(Box, { flexDirection: "column" },
91
- React.createElement(Gradient, { name: "rainbow" }, "Proxy Configuration"),
92
- React.createElement(Text, { color: "gray", dimColor: true }, "Configure system proxy for web search and fetch")))),
93
+ React.createElement(Gradient, { name: "rainbow" }, t.proxyConfig.title),
94
+ React.createElement(Text, { color: "gray", dimColor: true }, t.proxyConfig.subtitle)))),
93
95
  React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
94
96
  React.createElement(Box, { marginBottom: 1 },
95
97
  React.createElement(Box, { flexDirection: "column" },
96
98
  React.createElement(Text, { color: currentField === 'enabled' ? 'green' : 'white' },
97
99
  currentField === 'enabled' ? '❯ ' : ' ',
98
- "Enable Proxy:"),
100
+ t.proxyConfig.enableProxy),
99
101
  React.createElement(Box, { marginLeft: 3 },
100
102
  React.createElement(Text, { color: "gray" },
101
- enabled ? '[✓] Enabled' : '[ ] Disabled',
102
- " (Press Enter to toggle)")))),
103
+ enabled ? t.proxyConfig.enabled : t.proxyConfig.disabled,
104
+ ' ',
105
+ t.proxyConfig.toggleHint)))),
103
106
  React.createElement(Box, { marginBottom: 1 },
104
107
  React.createElement(Box, { flexDirection: "column" },
105
108
  React.createElement(Text, { color: currentField === 'port' ? 'green' : 'white' },
106
109
  currentField === 'port' ? '❯ ' : ' ',
107
- "Proxy Port:"),
110
+ t.proxyConfig.proxyPort),
108
111
  currentField === 'port' && isEditing && (React.createElement(Box, { marginLeft: 3 },
109
- React.createElement(TextInput, { value: port, onChange: setPort, placeholder: "7890" }))),
112
+ React.createElement(TextInput, { value: port, onChange: setPort, placeholder: t.proxyConfig.portPlaceholder }))),
110
113
  (!isEditing || currentField !== 'port') && (React.createElement(Box, { marginLeft: 3 },
111
- React.createElement(Text, { color: "gray" }, port || 'Not set'))))),
114
+ React.createElement(Text, { color: "gray" }, port || t.proxyConfig.notSet))))),
112
115
  React.createElement(Box, { marginBottom: 1 },
113
116
  React.createElement(Box, { flexDirection: "column" },
114
117
  React.createElement(Text, { color: currentField === 'browserPath' ? 'green' : 'white' },
115
118
  currentField === 'browserPath' ? '❯ ' : ' ',
116
- "Browser Path (Optional):"),
119
+ t.proxyConfig.browserPath),
117
120
  currentField === 'browserPath' && isEditing && (React.createElement(Box, { marginLeft: 3 },
118
- React.createElement(TextInput, { value: browserPath, onChange: setBrowserPath, placeholder: "Leave empty for auto-detect" }))),
121
+ React.createElement(TextInput, { value: browserPath, onChange: setBrowserPath, placeholder: t.proxyConfig.browserPathPlaceholder }))),
119
122
  (!isEditing || currentField !== 'browserPath') && (React.createElement(Box, { marginLeft: 3 },
120
- React.createElement(Text, { color: "gray" }, browserPath || 'Auto-detect')))))),
123
+ React.createElement(Text, { color: "gray" }, browserPath || t.proxyConfig.autoDetect)))))),
121
124
  errors.length > 0 && (React.createElement(Box, { flexDirection: "column", marginBottom: 2 },
122
- React.createElement(Text, { color: "red", bold: true }, "Errors:"),
125
+ React.createElement(Text, { color: "red", bold: true }, t.proxyConfig.errors),
123
126
  errors.map((error, index) => (React.createElement(Text, { key: index, color: "red" },
124
127
  "\u2022 ",
125
128
  error))))),
126
129
  React.createElement(Box, { flexDirection: "column" }, isEditing ? (React.createElement(React.Fragment, null,
127
- React.createElement(Alert, { variant: "info" }, "Editing mode: Press Enter to save and exit editing (Make your changes and press Enter when done)"))) : (React.createElement(React.Fragment, null,
128
- React.createElement(Alert, { variant: "info" }, "Use \u2191\u2193 to navigate between fields, press Enter to edit/toggle, and press Ctrl+S or Esc to save and return")))),
130
+ React.createElement(Alert, { variant: "info" }, t.proxyConfig.editingHint))) : (React.createElement(React.Fragment, null,
131
+ React.createElement(Alert, { variant: "info" }, t.proxyConfig.navigationHint)))),
129
132
  React.createElement(Box, { flexDirection: "column", marginTop: 1 },
130
133
  React.createElement(Alert, { variant: "info" },
131
- "Browser Path Examples: ",
134
+ t.proxyConfig.browserExamplesTitle,
135
+ " ",
132
136
  React.createElement(Newline, null),
133
- React.createElement(Text, { color: 'blue' }, "\u2022 Windows: C:\\Program Files(x86)\\Microsoft\\Edge\\Application\\msedge.exe"),
134
- ' ',
137
+ React.createElement(Text, { color: 'blue' }, t.proxyConfig.windowsExample),
138
+ " ",
135
139
  React.createElement(Newline, null),
136
- React.createElement(Text, { color: 'green' }, "\u2022 macOS: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome"),
137
- ' ',
140
+ React.createElement(Text, { color: 'green' }, t.proxyConfig.macosExample),
141
+ " ",
138
142
  React.createElement(Newline, null),
139
- React.createElement(Text, { color: 'yellow' }, "\u2022 Linux: /usr/bin/chromium-browser"),
140
- ' ',
143
+ React.createElement(Text, { color: 'yellow' }, t.proxyConfig.linuxExample),
144
+ " ",
141
145
  React.createElement(Newline, null),
142
- "Leave empty to auto-detect system browser (Edge/Chrome)"))));
146
+ t.proxyConfig.browserExamplesFooter))));
143
147
  }
@@ -3,6 +3,7 @@ import { Box, Text, useInput } from 'ink';
3
3
  import TextInput from 'ink-text-input';
4
4
  import { Alert } from '@inkjs/ui';
5
5
  import { getAllSensitiveCommands, toggleSensitiveCommand, addSensitiveCommand, removeSensitiveCommand, resetToDefaults, } from '../../utils/sensitiveCommandManager.js';
6
+ import { useI18n } from '../../i18n/index.js';
6
7
  // Focus event handling
7
8
  const focusEventTokenRegex = /(?:\x1b)?\[[0-9;]*[IO]/g;
8
9
  const isFocusEventInput = (value) => {
@@ -33,6 +34,7 @@ const stripFocusArtifacts = (value) => {
33
34
  .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
34
35
  };
35
36
  export default function SensitiveCommandConfigScreen({ onBack, inlineMode = false, }) {
37
+ const { t } = useI18n();
36
38
  const [commands, setCommands] = useState([]);
37
39
  const [selectedIndex, setSelectedIndex] = useState(0);
38
40
  const [viewMode, setViewMode] = useState('list');
@@ -83,9 +85,10 @@ export default function SensitiveCommandConfigScreen({ onBack, inlineMode = fals
83
85
  if (cmd) {
84
86
  toggleSensitiveCommand(cmd.id);
85
87
  loadCommands();
86
- setSuccessMessage(cmd.enabled
87
- ? `Disabled: ${cmd.pattern}`
88
- : `Enabled: ${cmd.pattern}`);
88
+ const message = cmd.enabled
89
+ ? t.sensitiveCommandConfig.disabledMessage
90
+ : t.sensitiveCommandConfig.enabledMessage;
91
+ setSuccessMessage(message.replace('{pattern}', cmd.pattern));
89
92
  setShowSuccess(true);
90
93
  setTimeout(() => setShowSuccess(false), 2000);
91
94
  }
@@ -111,7 +114,7 @@ export default function SensitiveCommandConfigScreen({ onBack, inlineMode = fals
111
114
  removeSensitiveCommand(cmd.id);
112
115
  loadCommands();
113
116
  setSelectedIndex(prev => Math.min(prev, commands.length - 2));
114
- setSuccessMessage(`Deleted: ${cmd.pattern}`);
117
+ setSuccessMessage(t.sensitiveCommandConfig.deletedMessage.replace('{pattern}', cmd.pattern));
115
118
  setShowSuccess(true);
116
119
  setTimeout(() => setShowSuccess(false), 2000);
117
120
  setConfirmDelete(false);
@@ -130,7 +133,7 @@ export default function SensitiveCommandConfigScreen({ onBack, inlineMode = fals
130
133
  resetToDefaults();
131
134
  loadCommands();
132
135
  setSelectedIndex(0);
133
- setSuccessMessage('Reset to default commands');
136
+ setSuccessMessage(t.sensitiveCommandConfig.resetMessage);
134
137
  setShowSuccess(true);
135
138
  setTimeout(() => setShowSuccess(false), 2000);
136
139
  setConfirmReset(false);
@@ -143,6 +146,7 @@ export default function SensitiveCommandConfigScreen({ onBack, inlineMode = fals
143
146
  loadCommands,
144
147
  confirmDelete,
145
148
  confirmReset,
149
+ t,
146
150
  ]);
147
151
  // Handle add view input
148
152
  const handleAddInput = useCallback((_input, key) => {
@@ -187,7 +191,7 @@ export default function SensitiveCommandConfigScreen({ onBack, inlineMode = fals
187
191
  addSensitiveCommand(customPattern.trim(), customDescription.trim());
188
192
  loadCommands();
189
193
  setViewMode('list');
190
- setSuccessMessage(`Added: ${customPattern}`);
194
+ setSuccessMessage(t.sensitiveCommandConfig.addedMessage.replace('{pattern}', customPattern));
191
195
  setShowSuccess(true);
192
196
  setTimeout(() => setShowSuccess(false), 2000);
193
197
  }
@@ -196,22 +200,22 @@ export default function SensitiveCommandConfigScreen({ onBack, inlineMode = fals
196
200
  }
197
201
  }
198
202
  }
199
- }, [addField, customPattern, customDescription, loadCommands]);
203
+ }, [addField, customPattern, customDescription, loadCommands, t]);
200
204
  if (viewMode === 'add') {
201
205
  return (React.createElement(Box, { flexDirection: "column", paddingX: inlineMode ? 0 : 2, paddingY: 1 },
202
- React.createElement(Text, { bold: true, color: "cyan" }, "Add Custom Sensitive Command"),
206
+ React.createElement(Text, { bold: true, color: "cyan" }, t.sensitiveCommandConfig.addTitle),
203
207
  React.createElement(Box, { marginTop: 1 }),
204
- React.createElement(Text, { dimColor: true }, "Pattern (supports wildcards, e.g., \"rm*\"):"),
208
+ React.createElement(Text, { dimColor: true }, t.sensitiveCommandConfig.patternLabel),
205
209
  React.createElement(Box, null,
206
210
  React.createElement(Text, { color: addField === 'pattern' ? 'cyan' : 'gray' }, "\u276F "),
207
- React.createElement(TextInput, { value: customPattern, onChange: handlePatternChange, onSubmit: handleAddSubmit, focus: addField === 'pattern' })),
211
+ React.createElement(TextInput, { value: customPattern, onChange: handlePatternChange, onSubmit: handleAddSubmit, focus: addField === 'pattern', placeholder: t.sensitiveCommandConfig.patternPlaceholder })),
208
212
  React.createElement(Box, { marginTop: 1 }),
209
- React.createElement(Text, { dimColor: true }, "Description:"),
213
+ React.createElement(Text, { dimColor: true }, t.sensitiveCommandConfig.descriptionLabel),
210
214
  React.createElement(Box, null,
211
215
  React.createElement(Text, { color: addField === 'description' ? 'cyan' : 'gray' }, "\u276F "),
212
216
  React.createElement(TextInput, { value: customDescription, onChange: handleDescriptionChange, onSubmit: handleAddSubmit, focus: addField === 'description' })),
213
217
  React.createElement(Box, { marginTop: 1 }),
214
- React.createElement(Text, { dimColor: true }, "Tab: Switch \u2022 Enter: Submit \u2022 Esc: Cancel")));
218
+ React.createElement(Text, { dimColor: true }, t.sensitiveCommandConfig.addEditingHint)));
215
219
  }
216
220
  // Calculate visible range for scrolling
217
221
  const viewportHeight = 13;
@@ -220,12 +224,12 @@ export default function SensitiveCommandConfigScreen({ onBack, inlineMode = fals
220
224
  const adjustedStart = Math.max(0, endIndex - viewportHeight);
221
225
  const selectedCmd = commands[selectedIndex];
222
226
  return (React.createElement(Box, { flexDirection: "column", paddingX: inlineMode ? 0 : 2, paddingY: 1 },
223
- React.createElement(Text, { bold: true, color: "cyan" }, "Sensitive Command Protection"),
224
- React.createElement(Text, { dimColor: true }, "Configure commands that require confirmation even in YOLO/Always-Approved mode"),
227
+ React.createElement(Text, { bold: true, color: "cyan" }, t.sensitiveCommandConfig.title),
228
+ React.createElement(Text, { dimColor: true }, t.sensitiveCommandConfig.subtitle),
225
229
  showSuccess && (React.createElement(Box, { marginTop: 1 },
226
230
  React.createElement(Alert, { variant: "success" }, successMessage))),
227
231
  React.createElement(Box, { marginTop: 1 }),
228
- commands.length === 0 ? (React.createElement(Text, { dimColor: true }, "No commands configured")) : (commands.map((cmd, index) => {
232
+ commands.length === 0 ? (React.createElement(Text, { dimColor: true }, t.sensitiveCommandConfig.noCommands)) : (commands.map((cmd, index) => {
229
233
  // Only render items in the visible viewport
230
234
  if (index < adjustedStart || index >= endIndex) {
231
235
  return null;
@@ -241,22 +245,25 @@ export default function SensitiveCommandConfigScreen({ onBack, inlineMode = fals
241
245
  "]",
242
246
  ' ',
243
247
  cmd.pattern,
244
- !cmd.isPreset && React.createElement(Text, { color: "yellow" }, " (custom)")));
248
+ !cmd.isPreset && (React.createElement(Text, { color: "yellow" },
249
+ " (",
250
+ t.sensitiveCommandConfig.custom,
251
+ ")"))));
245
252
  })),
246
253
  React.createElement(Box, { marginTop: 1 }),
247
254
  selectedCmd && !confirmDelete && !confirmReset && (React.createElement(Text, { dimColor: true },
248
255
  selectedCmd.description,
249
256
  " (",
250
- selectedCmd.enabled ? 'Enabled' : 'Disabled',
257
+ selectedCmd.enabled
258
+ ? t.sensitiveCommandConfig.enabled
259
+ : t.sensitiveCommandConfig.disabled,
251
260
  ")",
252
- !selectedCmd.isPreset && ' [Custom]')),
253
- confirmDelete && selectedCmd && (React.createElement(Text, { bold: true, color: "yellow" },
254
- "\u26A0\uFE0F Press D again to confirm deletion of \"",
255
- selectedCmd.pattern,
256
- "\"")),
257
- confirmReset && (React.createElement(Text, { bold: true, color: "yellow" }, "\u26A0\uFE0F Press R again to confirm reset to default commands")),
261
+ !selectedCmd.isPreset &&
262
+ ` [${t.sensitiveCommandConfig.customLabel}]`)),
263
+ confirmDelete && selectedCmd && (React.createElement(Text, { bold: true, color: "yellow" }, t.sensitiveCommandConfig.confirmDeleteMessage.replace('{pattern}', selectedCmd.pattern))),
264
+ confirmReset && (React.createElement(Text, { bold: true, color: "yellow" }, t.sensitiveCommandConfig.confirmResetMessage)),
258
265
  React.createElement(Box, { marginTop: 1 }),
259
266
  React.createElement(Text, { dimColor: true }, confirmDelete || confirmReset
260
- ? 'Press the same key again to confirm • Esc: Cancel'
261
- : '↑↓: Navigate • Space: Toggle • A: Add • D: Delete • R: Reset • Esc: Back')));
267
+ ? t.sensitiveCommandConfig.confirmHint
268
+ : t.sensitiveCommandConfig.listNavigationHint)));
262
269
  }