snow-ai 0.4.8 → 0.4.10
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/dist/app.js +30 -12
- package/dist/cli.js +6 -0
- package/dist/i18n/lang/en.js +21 -0
- package/dist/i18n/lang/es.js +21 -0
- package/dist/i18n/lang/ja.js +21 -0
- package/dist/i18n/lang/ko.js +21 -0
- package/dist/i18n/lang/zh-TW.js +21 -0
- package/dist/i18n/lang/zh.js +21 -0
- package/dist/i18n/types.d.ts +21 -0
- package/dist/mcp/todo.js +1 -1
- package/dist/ui/components/AgentPickerPanel.js +8 -6
- package/dist/ui/components/ChatInput.js +23 -21
- package/dist/ui/components/CommandPanel.js +7 -5
- package/dist/ui/components/DiffViewer.js +6 -4
- package/dist/ui/components/FileList.js +8 -6
- package/dist/ui/components/Menu.d.ts +1 -1
- package/dist/ui/components/Menu.js +8 -6
- package/dist/ui/components/PendingMessages.js +7 -5
- package/dist/ui/components/TodoPickerPanel.js +12 -10
- package/dist/ui/components/TodoTree.js +7 -5
- package/dist/ui/components/ToolConfirmation.js +14 -12
- package/dist/ui/components/ToolResultPreview.js +17 -3
- package/dist/ui/contexts/ThemeContext.d.ts +13 -0
- package/dist/ui/contexts/ThemeContext.js +28 -0
- package/dist/ui/pages/ChatScreen.js +21 -19
- package/dist/ui/pages/CodeBaseConfigScreen.js +30 -28
- package/dist/ui/pages/ConfigScreen.js +76 -74
- package/dist/ui/pages/CustomHeadersScreen.js +33 -31
- package/dist/ui/pages/LanguageSettingsScreen.js +6 -4
- package/dist/ui/pages/ProxyConfigScreen.js +15 -13
- package/dist/ui/pages/SensitiveCommandConfigScreen.js +12 -10
- package/dist/ui/pages/SubAgentConfigScreen.js +12 -10
- package/dist/ui/pages/SubAgentListScreen.js +11 -9
- package/dist/ui/pages/SystemPromptConfigScreen.js +21 -19
- package/dist/ui/pages/ThemeSettingsScreen.d.ts +7 -0
- package/dist/ui/pages/ThemeSettingsScreen.js +106 -0
- package/dist/ui/pages/WelcomeScreen.js +11 -1
- package/dist/ui/themes/index.d.ts +23 -0
- package/dist/ui/themes/index.js +140 -0
- package/dist/utils/configManager.js +11 -3
- package/dist/utils/logger.d.ts +9 -3
- package/dist/utils/logger.js +28 -3
- package/dist/utils/mcpToolsManager.d.ts +1 -1
- package/dist/utils/mcpToolsManager.js +13 -9
- package/dist/utils/themeConfig.d.ts +21 -0
- package/dist/utils/themeConfig.js +61 -0
- package/dist/utils/toolExecutor.js +11 -1
- package/package.json +3 -2
package/dist/app.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
1
|
+
import React, { useState, useEffect, Suspense } from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { Alert } from '@inkjs/ui';
|
|
4
|
+
import Spinner from 'ink-spinner';
|
|
4
5
|
import WelcomeScreen from './ui/pages/WelcomeScreen.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
// Lazy load heavy components to improve startup time
|
|
7
|
+
const ChatScreen = React.lazy(() => import('./ui/pages/ChatScreen.js'));
|
|
8
|
+
const HeadlessModeScreen = React.lazy(() => import('./ui/pages/HeadlessModeScreen.js'));
|
|
9
|
+
const MCPConfigScreen = React.lazy(() => import('./ui/pages/MCPConfigScreen.js'));
|
|
10
|
+
const SystemPromptConfigScreen = React.lazy(() => import('./ui/pages/SystemPromptConfigScreen.js'));
|
|
11
|
+
const CustomHeadersScreen = React.lazy(() => import('./ui/pages/CustomHeadersScreen.js'));
|
|
10
12
|
import { useGlobalExit, } from './hooks/useGlobalExit.js';
|
|
11
13
|
import { onNavigate } from './hooks/useGlobalNavigation.js';
|
|
12
14
|
import { useTerminalSize } from './hooks/useTerminalSize.js';
|
|
13
15
|
import { I18nProvider } from './i18n/index.js';
|
|
16
|
+
import { ThemeProvider } from './ui/contexts/ThemeContext.js';
|
|
14
17
|
// Inner component that uses I18n context
|
|
15
18
|
function AppContent({ version, skipWelcome, }) {
|
|
16
19
|
const [currentView, setCurrentView] = useState(skipWelcome ? 'chat' : 'welcome');
|
|
@@ -55,21 +58,29 @@ function AppContent({ version, skipWelcome, }) {
|
|
|
55
58
|
}
|
|
56
59
|
};
|
|
57
60
|
const renderView = () => {
|
|
61
|
+
const loadingFallback = (React.createElement(Box, null,
|
|
62
|
+
React.createElement(Text, { color: "cyan" },
|
|
63
|
+
React.createElement(Spinner, { type: "dots" })),
|
|
64
|
+
React.createElement(Text, null, " Loading...")));
|
|
58
65
|
switch (currentView) {
|
|
59
66
|
case 'welcome':
|
|
60
67
|
return (React.createElement(WelcomeScreen, { version: version, onMenuSelect: handleMenuSelect }));
|
|
61
68
|
case 'chat':
|
|
62
|
-
return React.createElement(
|
|
69
|
+
return (React.createElement(Suspense, { fallback: loadingFallback },
|
|
70
|
+
React.createElement(ChatScreen, { key: chatScreenKey, skipWelcome: skipWelcome })));
|
|
63
71
|
case 'settings':
|
|
64
72
|
return (React.createElement(Box, { flexDirection: "column" },
|
|
65
73
|
React.createElement(Text, { color: "blue" }, "Settings"),
|
|
66
74
|
React.createElement(Text, { color: "gray" }, "Settings interface would be implemented here")));
|
|
67
75
|
case 'mcp':
|
|
68
|
-
return (React.createElement(
|
|
76
|
+
return (React.createElement(Suspense, { fallback: loadingFallback },
|
|
77
|
+
React.createElement(MCPConfigScreen, { onBack: () => setCurrentView('welcome'), onSave: () => setCurrentView('welcome') })));
|
|
69
78
|
case 'systemprompt':
|
|
70
|
-
return (React.createElement(
|
|
79
|
+
return (React.createElement(Suspense, { fallback: loadingFallback },
|
|
80
|
+
React.createElement(SystemPromptConfigScreen, { onBack: () => setCurrentView('welcome') })));
|
|
71
81
|
case 'customheaders':
|
|
72
|
-
return React.createElement(
|
|
82
|
+
return (React.createElement(Suspense, { fallback: loadingFallback },
|
|
83
|
+
React.createElement(CustomHeadersScreen, { onBack: () => setCurrentView('welcome') })));
|
|
73
84
|
default:
|
|
74
85
|
return (React.createElement(WelcomeScreen, { version: version, onMenuSelect: handleMenuSelect }));
|
|
75
86
|
}
|
|
@@ -83,9 +94,16 @@ export default function App({ version, skipWelcome, headlessPrompt }) {
|
|
|
83
94
|
// If headless prompt is provided, use headless mode
|
|
84
95
|
// Wrap in I18nProvider since HeadlessModeScreen might use hooks that depend on it
|
|
85
96
|
if (headlessPrompt) {
|
|
97
|
+
const loadingFallback = (React.createElement(Box, null,
|
|
98
|
+
React.createElement(Text, { color: "cyan" },
|
|
99
|
+
React.createElement(Spinner, { type: "dots" })),
|
|
100
|
+
React.createElement(Text, null, " Loading...")));
|
|
86
101
|
return (React.createElement(I18nProvider, null,
|
|
87
|
-
React.createElement(
|
|
102
|
+
React.createElement(ThemeProvider, null,
|
|
103
|
+
React.createElement(Suspense, { fallback: loadingFallback },
|
|
104
|
+
React.createElement(HeadlessModeScreen, { prompt: headlessPrompt, onComplete: () => process.exit(0) })))));
|
|
88
105
|
}
|
|
89
106
|
return (React.createElement(I18nProvider, null,
|
|
90
|
-
React.createElement(
|
|
107
|
+
React.createElement(ThemeProvider, null,
|
|
108
|
+
React.createElement(AppContent, { version: version, skipWelcome: skipWelcome }))));
|
|
91
109
|
}
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// Show loading indicator immediately before any imports
|
|
3
|
+
process.stdout.write('\x1b[?25l'); // Hide cursor
|
|
4
|
+
process.stdout.write('⠋ Loading...\r');
|
|
2
5
|
import React from 'react';
|
|
3
6
|
import { render, Text, Box } from 'ink';
|
|
4
7
|
import Spinner from 'ink-spinner';
|
|
@@ -137,6 +140,9 @@ const Startup = ({ version, skipWelcome, headlessPrompt, }) => {
|
|
|
137
140
|
};
|
|
138
141
|
// Disable bracketed paste mode on startup
|
|
139
142
|
process.stdout.write('\x1b[?2004l');
|
|
143
|
+
// Clear the early loading indicator and show cursor
|
|
144
|
+
process.stdout.write('\x1b[2K\r'); // Clear line
|
|
145
|
+
process.stdout.write('\x1b[?25h'); // Show cursor
|
|
140
146
|
// Re-enable on exit to avoid polluting parent shell
|
|
141
147
|
const cleanup = () => {
|
|
142
148
|
process.stdout.write('\x1b[?2004l');
|
package/dist/i18n/lang/en.js
CHANGED
|
@@ -22,6 +22,8 @@ export const en = {
|
|
|
22
22
|
sensitiveCommandsInfo: 'Configure commands that require confirmation even in YOLO mode',
|
|
23
23
|
languageSettings: 'Language Settings',
|
|
24
24
|
languageSettingsInfo: 'Switch application language',
|
|
25
|
+
themeSettings: 'Theme Settings',
|
|
26
|
+
themeSettingsInfo: 'Configure theme and preview DiffViewer',
|
|
25
27
|
exit: 'Exit',
|
|
26
28
|
exitInfo: 'Exit the application',
|
|
27
29
|
},
|
|
@@ -274,6 +276,25 @@ export const en = {
|
|
|
274
276
|
// Navigation hints
|
|
275
277
|
listNavigationHint: '↑↓: Navigate • Space: Toggle • A: Add • D: Delete • R: Reset • Esc: Back',
|
|
276
278
|
},
|
|
279
|
+
themeSettings: {
|
|
280
|
+
title: 'Theme Settings',
|
|
281
|
+
current: 'Current:',
|
|
282
|
+
preview: 'Preview:',
|
|
283
|
+
back: '← Back',
|
|
284
|
+
backInfo: 'Return to main menu',
|
|
285
|
+
darkTheme: 'Dark Theme',
|
|
286
|
+
darkThemeInfo: 'Classic dark color scheme',
|
|
287
|
+
lightTheme: 'Light Theme',
|
|
288
|
+
lightThemeInfo: 'Classic light color scheme',
|
|
289
|
+
githubDark: 'GitHub Dark',
|
|
290
|
+
githubDarkInfo: 'GitHub inspired dark theme',
|
|
291
|
+
rainbow: 'Rainbow',
|
|
292
|
+
rainbowInfo: 'Vibrant rainbow colors for a fun experience',
|
|
293
|
+
solarizedDark: 'Solarized Dark',
|
|
294
|
+
solarizedDarkInfo: 'Solarized dark theme with precision colors',
|
|
295
|
+
nord: 'Nord',
|
|
296
|
+
nordInfo: 'Arctic, north-bluish color palette',
|
|
297
|
+
},
|
|
277
298
|
helpPanel: {
|
|
278
299
|
title: '🔰 Keyboard Shortcuts & Help',
|
|
279
300
|
textEditingTitle: '📝 Text Editing:',
|
package/dist/i18n/lang/es.js
CHANGED
|
@@ -22,6 +22,8 @@ export const es = {
|
|
|
22
22
|
sensitiveCommandsInfo: 'Configurar comandos que requieren confirmación incluso en modo YOLO',
|
|
23
23
|
languageSettings: 'Configuración de Idioma',
|
|
24
24
|
languageSettingsInfo: 'Cambiar el idioma de la aplicación',
|
|
25
|
+
themeSettings: 'Configuración de Tema',
|
|
26
|
+
themeSettingsInfo: 'Configurar tema y vista previa del visor de diferencias',
|
|
25
27
|
exit: 'Salir',
|
|
26
28
|
exitInfo: 'Salir de la aplicación',
|
|
27
29
|
},
|
|
@@ -274,6 +276,25 @@ export const es = {
|
|
|
274
276
|
// Navigation hints
|
|
275
277
|
listNavigationHint: '↑↓: Navegar • Espacio: Alternar • A: Agregar • D: Eliminar • R: Restablecer • Esc: Volver',
|
|
276
278
|
},
|
|
279
|
+
themeSettings: {
|
|
280
|
+
title: 'Configuración de Tema',
|
|
281
|
+
current: 'Actual:',
|
|
282
|
+
preview: 'Vista previa:',
|
|
283
|
+
back: '← Atrás',
|
|
284
|
+
backInfo: 'Volver al menú principal',
|
|
285
|
+
darkTheme: 'Tema Oscuro',
|
|
286
|
+
darkThemeInfo: 'Esquema de colores oscuros clásico',
|
|
287
|
+
lightTheme: 'Tema Claro',
|
|
288
|
+
lightThemeInfo: 'Esquema de colores claros clásico',
|
|
289
|
+
githubDark: 'GitHub Oscuro',
|
|
290
|
+
githubDarkInfo: 'Tema oscuro inspirado en GitHub',
|
|
291
|
+
rainbow: 'Arcoíris',
|
|
292
|
+
rainbowInfo: 'Colores vibrantes del arcoíris para una experiencia divertida',
|
|
293
|
+
solarizedDark: 'Solarized Oscuro',
|
|
294
|
+
solarizedDarkInfo: 'Tema oscuro Solarized con colores precisos',
|
|
295
|
+
nord: 'Nord',
|
|
296
|
+
nordInfo: 'Paleta de colores ártica y azulada del norte',
|
|
297
|
+
},
|
|
277
298
|
helpPanel: {
|
|
278
299
|
title: '🔰 Atajos de Teclado y Ayuda',
|
|
279
300
|
textEditingTitle: '📝 Edición de Texto:',
|
package/dist/i18n/lang/ja.js
CHANGED
|
@@ -22,6 +22,8 @@ export const ja = {
|
|
|
22
22
|
sensitiveCommandsInfo: 'YOLOモードでも確認が必要なコマンドを構成',
|
|
23
23
|
languageSettings: '言語設定',
|
|
24
24
|
languageSettingsInfo: 'アプリケーションの言語を切り替え',
|
|
25
|
+
themeSettings: 'テーマ設定',
|
|
26
|
+
themeSettingsInfo: 'テーマを設定して差分ビューワーをプレビュー',
|
|
25
27
|
exit: '終了',
|
|
26
28
|
exitInfo: 'アプリケーションを終了',
|
|
27
29
|
},
|
|
@@ -274,6 +276,25 @@ export const ja = {
|
|
|
274
276
|
// Navigation hints
|
|
275
277
|
listNavigationHint: '↑↓: 移動 • Space: 切替 • A: 追加 • D: 削除 • R: リセット • Esc: 戻る',
|
|
276
278
|
},
|
|
279
|
+
themeSettings: {
|
|
280
|
+
title: 'テーマ設定',
|
|
281
|
+
current: '現在:',
|
|
282
|
+
preview: 'プレビュー:',
|
|
283
|
+
back: '← 戻る',
|
|
284
|
+
backInfo: 'メインメニューに戻る',
|
|
285
|
+
darkTheme: 'ダークテーマ',
|
|
286
|
+
darkThemeInfo: 'クラシックダーク配色',
|
|
287
|
+
lightTheme: 'ライトテーマ',
|
|
288
|
+
lightThemeInfo: 'クラシックライト配色',
|
|
289
|
+
githubDark: 'GitHub ダーク',
|
|
290
|
+
githubDarkInfo: 'GitHubにインスパイアされたダークテーマ',
|
|
291
|
+
rainbow: 'レインボー',
|
|
292
|
+
rainbowInfo: '鮮やかな虹色で楽しい体験',
|
|
293
|
+
solarizedDark: 'Solarized ダーク',
|
|
294
|
+
solarizedDarkInfo: '精密な色を持つ Solarized ダークテーマ',
|
|
295
|
+
nord: 'Nord',
|
|
296
|
+
nordInfo: '北極、北方の青みがかった色パレット',
|
|
297
|
+
},
|
|
277
298
|
helpPanel: {
|
|
278
299
|
title: '🔰 キーボードショートカットとヘルプ',
|
|
279
300
|
textEditingTitle: '📝 テキスト編集:',
|
package/dist/i18n/lang/ko.js
CHANGED
|
@@ -22,6 +22,8 @@ export const ko = {
|
|
|
22
22
|
sensitiveCommandsInfo: 'YOLO 모드에서도 확인이 필요한 명령 구성',
|
|
23
23
|
languageSettings: '언어 설정',
|
|
24
24
|
languageSettingsInfo: '애플리케이션 언어 전환',
|
|
25
|
+
themeSettings: '테마 설정',
|
|
26
|
+
themeSettingsInfo: '테마를 구성하고 차이점 뷰어를 미리 보기',
|
|
25
27
|
exit: '종료',
|
|
26
28
|
exitInfo: '애플리케이션 종료',
|
|
27
29
|
},
|
|
@@ -274,6 +276,25 @@ export const ko = {
|
|
|
274
276
|
// Navigation hints
|
|
275
277
|
listNavigationHint: '↑↓: 이동 • 스페이스: 토글 • A: 추가 • D: 삭제 • R: 재설정 • Esc: 뒤로',
|
|
276
278
|
},
|
|
279
|
+
themeSettings: {
|
|
280
|
+
title: '테마 설정',
|
|
281
|
+
current: '현재:',
|
|
282
|
+
preview: '미리 보기:',
|
|
283
|
+
back: '← 뒤로',
|
|
284
|
+
backInfo: '메인 메뉴로 돌아가기',
|
|
285
|
+
darkTheme: '다크 테마',
|
|
286
|
+
darkThemeInfo: '클래식 다크 컬러 스키마',
|
|
287
|
+
lightTheme: '라이트 테마',
|
|
288
|
+
lightThemeInfo: '클래식 라이트 컬러 스키마',
|
|
289
|
+
githubDark: 'GitHub 다크',
|
|
290
|
+
githubDarkInfo: 'GitHub에서 영감을 받은 다크 테마',
|
|
291
|
+
rainbow: '레인보우',
|
|
292
|
+
rainbowInfo: '재미있는 경험을 위한 생동감 있는 무지개 색상',
|
|
293
|
+
solarizedDark: 'Solarized 다크',
|
|
294
|
+
solarizedDarkInfo: '정밀한 색상을 가진 Solarized 다크 테마',
|
|
295
|
+
nord: 'Nord',
|
|
296
|
+
nordInfo: '북극, 북부 청량 컬러 팔레트',
|
|
297
|
+
},
|
|
277
298
|
helpPanel: {
|
|
278
299
|
title: '🔰 키보드 단축키 및 도움말',
|
|
279
300
|
textEditingTitle: '📝 텍스트 편집:',
|
package/dist/i18n/lang/zh-TW.js
CHANGED
|
@@ -22,6 +22,8 @@ export const zhTW = {
|
|
|
22
22
|
sensitiveCommandsInfo: '配置即使在 YOLO 模式下也需要確認的命令',
|
|
23
23
|
languageSettings: '語言設定',
|
|
24
24
|
languageSettingsInfo: '切換應用語言',
|
|
25
|
+
themeSettings: '主題設定',
|
|
26
|
+
themeSettingsInfo: '設定主題並預覽差異檢視器',
|
|
25
27
|
exit: '退出',
|
|
26
28
|
exitInfo: '退出應用程式',
|
|
27
29
|
},
|
|
@@ -274,6 +276,25 @@ export const zhTW = {
|
|
|
274
276
|
// Navigation hints
|
|
275
277
|
listNavigationHint: '↑↓: 導航 • 空格: 切換 • A: 新增 • D: 刪除 • R: 重設 • Esc: 返回',
|
|
276
278
|
},
|
|
279
|
+
themeSettings: {
|
|
280
|
+
title: '主題設定',
|
|
281
|
+
current: '目前:',
|
|
282
|
+
preview: '預覽:',
|
|
283
|
+
back: '← 返回',
|
|
284
|
+
backInfo: '返回主選單',
|
|
285
|
+
darkTheme: '深色主題',
|
|
286
|
+
darkThemeInfo: '經典深色配色方案',
|
|
287
|
+
lightTheme: '淺色主題',
|
|
288
|
+
lightThemeInfo: '經典淺色配色方案',
|
|
289
|
+
githubDark: 'GitHub 深色',
|
|
290
|
+
githubDarkInfo: '受 GitHub 啟發的深色主題',
|
|
291
|
+
rainbow: '彩虹',
|
|
292
|
+
rainbowInfo: '生動的彩虹色彩,帶來有趣的體驗',
|
|
293
|
+
solarizedDark: 'Solarized 深色',
|
|
294
|
+
solarizedDarkInfo: '具有精確色彩的 Solarized 深色主題',
|
|
295
|
+
nord: 'Nord',
|
|
296
|
+
nordInfo: '北極、北方藍調色板',
|
|
297
|
+
},
|
|
277
298
|
helpPanel: {
|
|
278
299
|
title: '🔰 鍵盤快捷鍵和說明',
|
|
279
300
|
textEditingTitle: '📝 文字編輯:',
|
package/dist/i18n/lang/zh.js
CHANGED
|
@@ -22,6 +22,8 @@ export const zh = {
|
|
|
22
22
|
sensitiveCommandsInfo: '配置即使在 YOLO 模式下也需要确认的命令',
|
|
23
23
|
languageSettings: '语言设置',
|
|
24
24
|
languageSettingsInfo: '切换应用语言',
|
|
25
|
+
themeSettings: '主题设置',
|
|
26
|
+
themeSettingsInfo: '配置主题并预览差异查看器',
|
|
25
27
|
exit: '退出',
|
|
26
28
|
exitInfo: '退出应用程序',
|
|
27
29
|
},
|
|
@@ -274,6 +276,25 @@ export const zh = {
|
|
|
274
276
|
// Navigation hints
|
|
275
277
|
listNavigationHint: '↑↓: 导航 • 空格: 切换 • A: 添加 • D: 删除 • R: 重置 • Esc: 返回',
|
|
276
278
|
},
|
|
279
|
+
themeSettings: {
|
|
280
|
+
title: '主题设置',
|
|
281
|
+
current: '当前:',
|
|
282
|
+
preview: '预览:',
|
|
283
|
+
back: '← 返回',
|
|
284
|
+
backInfo: '返回主菜单',
|
|
285
|
+
darkTheme: '深色主题',
|
|
286
|
+
darkThemeInfo: '经典深色配色方案',
|
|
287
|
+
lightTheme: '浅色主题',
|
|
288
|
+
lightThemeInfo: '经典浅色配色方案',
|
|
289
|
+
githubDark: 'GitHub 深色',
|
|
290
|
+
githubDarkInfo: '受 GitHub 启发的深色主题',
|
|
291
|
+
rainbow: '彩虹',
|
|
292
|
+
rainbowInfo: '生动的彩虹色彩,带来有趣的体验',
|
|
293
|
+
solarizedDark: 'Solarized 深色',
|
|
294
|
+
solarizedDarkInfo: '具有精确色彩的 Solarized 深色主题',
|
|
295
|
+
nord: 'Nord',
|
|
296
|
+
nordInfo: '北极、北方蓝调色板',
|
|
297
|
+
},
|
|
277
298
|
helpPanel: {
|
|
278
299
|
title: '🔰 键盘快捷键和帮助',
|
|
279
300
|
textEditingTitle: '📝 文本编辑:',
|
package/dist/i18n/types.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export type TranslationKeys = {
|
|
|
23
23
|
sensitiveCommandsInfo: string;
|
|
24
24
|
languageSettings: string;
|
|
25
25
|
languageSettingsInfo: string;
|
|
26
|
+
themeSettings: string;
|
|
27
|
+
themeSettingsInfo: string;
|
|
26
28
|
exit: string;
|
|
27
29
|
exitInfo: string;
|
|
28
30
|
};
|
|
@@ -271,6 +273,25 @@ export type TranslationKeys = {
|
|
|
271
273
|
confirmHint: string;
|
|
272
274
|
listNavigationHint: string;
|
|
273
275
|
};
|
|
276
|
+
themeSettings: {
|
|
277
|
+
title: string;
|
|
278
|
+
current: string;
|
|
279
|
+
preview: string;
|
|
280
|
+
back: string;
|
|
281
|
+
backInfo: string;
|
|
282
|
+
darkTheme: string;
|
|
283
|
+
darkThemeInfo: string;
|
|
284
|
+
lightTheme: string;
|
|
285
|
+
lightThemeInfo: string;
|
|
286
|
+
githubDark: string;
|
|
287
|
+
githubDarkInfo: string;
|
|
288
|
+
rainbow: string;
|
|
289
|
+
rainbowInfo: string;
|
|
290
|
+
solarizedDark: string;
|
|
291
|
+
solarizedDarkInfo: string;
|
|
292
|
+
nord: string;
|
|
293
|
+
nordInfo: string;
|
|
294
|
+
};
|
|
274
295
|
helpPanel: {
|
|
275
296
|
title: string;
|
|
276
297
|
textEditingTitle: string;
|
package/dist/mcp/todo.js
CHANGED
|
@@ -267,7 +267,7 @@ This REPLACES the entire TODO list. For adding tasks to existing list, use "todo
|
|
|
267
267
|
description: 'Parent TODO ID (optional, for creating subtasks in hierarchical structure)',
|
|
268
268
|
},
|
|
269
269
|
},
|
|
270
|
-
required: ['content'],
|
|
270
|
+
required: ['content', 'parentId'],
|
|
271
271
|
},
|
|
272
272
|
description: 'Complete list of TODO items. Each item must represent a discrete, verifiable unit of work. For programming tasks, typical structure: analyze code → implement changes → test functionality → verify build → commit (if requested).',
|
|
273
273
|
},
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React, { memo, useMemo } from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { Alert } from '@inkjs/ui';
|
|
4
|
+
import { useTheme } from '../contexts/ThemeContext.js';
|
|
4
5
|
const AgentPickerPanel = memo(({ agents, selectedIndex, visible, maxHeight }) => {
|
|
6
|
+
const { theme } = useTheme();
|
|
5
7
|
// Fixed maximum display items to prevent rendering issues
|
|
6
8
|
const MAX_DISPLAY_ITEMS = 5;
|
|
7
9
|
const effectiveMaxItems = maxHeight
|
|
@@ -39,7 +41,7 @@ const AgentPickerPanel = memo(({ agents, selectedIndex, visible, maxHeight }) =>
|
|
|
39
41
|
React.createElement(Box, { width: "100%" },
|
|
40
42
|
React.createElement(Box, { flexDirection: "column", width: "100%" },
|
|
41
43
|
React.createElement(Box, null,
|
|
42
|
-
React.createElement(Text, { color:
|
|
44
|
+
React.createElement(Text, { color: theme.colors.warning, bold: true }, "Sub-Agent Selection")),
|
|
43
45
|
React.createElement(Box, { marginTop: 1 },
|
|
44
46
|
React.createElement(Alert, { variant: "warning" }, "No sub-agents configured. Please configure sub-agents first."))))));
|
|
45
47
|
}
|
|
@@ -47,23 +49,23 @@ const AgentPickerPanel = memo(({ agents, selectedIndex, visible, maxHeight }) =>
|
|
|
47
49
|
React.createElement(Box, { width: "100%" },
|
|
48
50
|
React.createElement(Box, { flexDirection: "column", width: "100%" },
|
|
49
51
|
React.createElement(Box, null,
|
|
50
|
-
React.createElement(Text, { color:
|
|
52
|
+
React.createElement(Text, { color: theme.colors.warning, bold: true },
|
|
51
53
|
"Select Sub-Agent",
|
|
52
54
|
' ',
|
|
53
55
|
agents.length > effectiveMaxItems &&
|
|
54
56
|
`(${selectedIndex + 1}/${agents.length})`),
|
|
55
|
-
React.createElement(Text, { color:
|
|
57
|
+
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, "(Press ESC to close)")),
|
|
56
58
|
displayedAgents.map((agent, index) => (React.createElement(Box, { key: agent.id, flexDirection: "column", width: "100%" },
|
|
57
|
-
React.createElement(Text, { color: index === displayedSelectedIndex ?
|
|
59
|
+
React.createElement(Text, { color: index === displayedSelectedIndex ? theme.colors.success : theme.colors.menuSecondary, bold: true },
|
|
58
60
|
index === displayedSelectedIndex ? '❯ ' : ' ',
|
|
59
61
|
"#",
|
|
60
62
|
agent.name),
|
|
61
63
|
React.createElement(Box, { marginLeft: 3 },
|
|
62
|
-
React.createElement(Text, { color: index === displayedSelectedIndex ?
|
|
64
|
+
React.createElement(Text, { color: index === displayedSelectedIndex ? theme.colors.success : theme.colors.menuSecondary, dimColor: true },
|
|
63
65
|
"\u2514\u2500 ",
|
|
64
66
|
agent.description || 'No description'))))),
|
|
65
67
|
agents.length > effectiveMaxItems && (React.createElement(Box, { marginTop: 1 },
|
|
66
|
-
React.createElement(Text, { color:
|
|
68
|
+
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true },
|
|
67
69
|
"\u2191\u2193 to scroll \u00B7 ",
|
|
68
70
|
agents.length - effectiveMaxItems,
|
|
69
71
|
" more hidden")))))));
|
|
@@ -16,6 +16,7 @@ import { useTerminalFocus } from '../../hooks/useTerminalFocus.js';
|
|
|
16
16
|
import { useAgentPicker } from '../../hooks/useAgentPicker.js';
|
|
17
17
|
import { useTodoPicker } from '../../hooks/useTodoPicker.js';
|
|
18
18
|
import { useI18n } from '../../i18n/index.js';
|
|
19
|
+
import { useTheme } from '../contexts/ThemeContext.js';
|
|
19
20
|
/**
|
|
20
21
|
* Calculate context usage percentage
|
|
21
22
|
* This is the same logic used in ChatInput to display usage
|
|
@@ -36,6 +37,7 @@ export function calculateContextPercentage(contextUsage) {
|
|
|
36
37
|
export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type your message...', disabled = false, isProcessing = false, chatHistory = [], onHistorySelect, yoloMode = false, contextUsage, initialContent = null, onContextPercentageChange, }) {
|
|
37
38
|
// Use i18n hook for translations
|
|
38
39
|
const { t } = useI18n();
|
|
40
|
+
const { theme } = useTheme();
|
|
39
41
|
// Use terminal size hook to listen for resize events
|
|
40
42
|
const { columns: terminalWidth } = useTerminalSize();
|
|
41
43
|
const prevTerminalWidthRef = useRef(terminalWidth);
|
|
@@ -206,14 +208,14 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
206
208
|
// Render cursor based on focus state
|
|
207
209
|
const renderCursor = useCallback((char) => {
|
|
208
210
|
if (hasFocus) {
|
|
209
|
-
// Focused: solid block cursor
|
|
210
|
-
return (React.createElement(Text, { backgroundColor:
|
|
211
|
+
// Focused: solid block cursor (use inverted colors)
|
|
212
|
+
return (React.createElement(Text, { backgroundColor: theme.colors.menuNormal, color: theme.colors.background }, char));
|
|
211
213
|
}
|
|
212
214
|
else {
|
|
213
215
|
// Unfocused: no cursor, just render the character normally
|
|
214
216
|
return React.createElement(Text, null, char);
|
|
215
217
|
}
|
|
216
|
-
}, [hasFocus]);
|
|
218
|
+
}, [hasFocus, theme]);
|
|
217
219
|
// Render content with cursor (treat all text including placeholders as plain text)
|
|
218
220
|
const renderContent = () => {
|
|
219
221
|
if (buffer.text.length > 0) {
|
|
@@ -243,7 +245,7 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
243
245
|
else {
|
|
244
246
|
return (React.createElement(React.Fragment, null,
|
|
245
247
|
renderCursor(' '),
|
|
246
|
-
React.createElement(Text, { color:
|
|
248
|
+
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, disabled ? t.chatScreen.waitingForResponse : placeholder)));
|
|
247
249
|
}
|
|
248
250
|
};
|
|
249
251
|
return (React.createElement(Box, { flexDirection: "column", paddingX: 1, width: terminalWidth },
|
|
@@ -264,7 +266,7 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
264
266
|
const hasMoreAbove = startIndex > 0;
|
|
265
267
|
const hasMoreBelow = endIndex < userMessages.length;
|
|
266
268
|
return (React.createElement(React.Fragment, null,
|
|
267
|
-
React.createElement(Box, { height: 1 }, hasMoreAbove ? (React.createElement(Text, { color:
|
|
269
|
+
React.createElement(Box, { height: 1 }, hasMoreAbove ? (React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, t.chatScreen.moreAbove.replace('{count}', startIndex.toString()))) : (React.createElement(Text, null, " "))),
|
|
268
270
|
visibleMessages.map((message, displayIndex) => {
|
|
269
271
|
const actualIndex = startIndex + displayIndex;
|
|
270
272
|
// Ensure single line by removing all newlines and control characters
|
|
@@ -280,24 +282,24 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
280
282
|
: singleLineLabel;
|
|
281
283
|
return (React.createElement(Box, { key: message.value, height: 1 },
|
|
282
284
|
React.createElement(Text, { color: actualIndex === historySelectedIndex
|
|
283
|
-
?
|
|
284
|
-
:
|
|
285
|
+
? theme.colors.menuSelected
|
|
286
|
+
: theme.colors.menuNormal, bold: true, wrap: "truncate" },
|
|
285
287
|
actualIndex === historySelectedIndex ? '❯ ' : ' ',
|
|
286
288
|
truncatedLabel)));
|
|
287
289
|
}),
|
|
288
|
-
React.createElement(Box, { height: 1 }, hasMoreBelow ? (React.createElement(Text, { color:
|
|
290
|
+
React.createElement(Box, { height: 1 }, hasMoreBelow ? (React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, t.chatScreen.moreBelow.replace('{count}', (userMessages.length - endIndex).toString()))) : (React.createElement(Text, null, " ")))));
|
|
289
291
|
})()),
|
|
290
292
|
React.createElement(Box, { marginBottom: 1 },
|
|
291
|
-
React.createElement(Text, { color:
|
|
293
|
+
React.createElement(Text, { color: theme.colors.menuInfo, dimColor: true }, t.chatScreen.historyNavigateHint)))),
|
|
292
294
|
!showHistoryMenu && (React.createElement(React.Fragment, null,
|
|
293
295
|
React.createElement(Box, { flexDirection: "column", width: terminalWidth - 2 },
|
|
294
|
-
React.createElement(Text, { color:
|
|
296
|
+
React.createElement(Text, { color: theme.colors.menuSecondary }, '─'.repeat(terminalWidth - 2)),
|
|
295
297
|
React.createElement(Box, { flexDirection: "row" },
|
|
296
|
-
React.createElement(Text, { color:
|
|
298
|
+
React.createElement(Text, { color: theme.colors.menuInfo, bold: true },
|
|
297
299
|
"\u276F",
|
|
298
300
|
' '),
|
|
299
301
|
React.createElement(Box, { flexGrow: 1 }, renderContent())),
|
|
300
|
-
React.createElement(Text, { color:
|
|
302
|
+
React.createElement(Text, { color: theme.colors.menuSecondary }, '─'.repeat(terminalWidth - 2))),
|
|
301
303
|
(showCommands && getFilteredCommands().length > 0) ||
|
|
302
304
|
showFilePicker ? (React.createElement(Box, { marginTop: 1 },
|
|
303
305
|
React.createElement(Text, null, showCommands && getFilteredCommands().length > 0
|
|
@@ -313,9 +315,9 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
313
315
|
React.createElement(AgentPickerPanel, { agents: getFilteredAgents(), selectedIndex: agentSelectedIndex, visible: showAgentPicker, maxHeight: 5 }),
|
|
314
316
|
React.createElement(TodoPickerPanel, { todos: todos, selectedIndex: todoSelectedIndex, selectedTodos: selectedTodos, visible: showTodoPicker, maxHeight: 5, isLoading: todoIsLoading, searchQuery: todoSearchQuery, totalCount: totalTodoCount }),
|
|
315
317
|
yoloMode && (React.createElement(Box, { marginTop: 1 },
|
|
316
|
-
React.createElement(Text, { color:
|
|
318
|
+
React.createElement(Text, { color: theme.colors.warning, dimColor: true }, t.chatScreen.yoloModeActive))),
|
|
317
319
|
contextUsage && (React.createElement(Box, { marginTop: 1 },
|
|
318
|
-
React.createElement(Text, { color:
|
|
320
|
+
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, (() => {
|
|
319
321
|
// Determine which caching system is being used
|
|
320
322
|
const isAnthropic = (contextUsage.cacheCreationTokens || 0) > 0 ||
|
|
321
323
|
(contextUsage.cacheReadTokens || 0) > 0;
|
|
@@ -330,13 +332,13 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
330
332
|
: contextUsage.inputTokens;
|
|
331
333
|
let color;
|
|
332
334
|
if (percentage < 50)
|
|
333
|
-
color =
|
|
335
|
+
color = theme.colors.success;
|
|
334
336
|
else if (percentage < 75)
|
|
335
|
-
color =
|
|
337
|
+
color = theme.colors.warning;
|
|
336
338
|
else if (percentage < 90)
|
|
337
|
-
color =
|
|
339
|
+
color = theme.colors.warning;
|
|
338
340
|
else
|
|
339
|
-
color =
|
|
341
|
+
color = theme.colors.error;
|
|
340
342
|
const formatNumber = (num) => {
|
|
341
343
|
if (num >= 1000)
|
|
342
344
|
return `${(num / 1000).toFixed(1)}k`;
|
|
@@ -354,7 +356,7 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
354
356
|
React.createElement(Text, null, " \u00B7 "),
|
|
355
357
|
isAnthropic && (React.createElement(React.Fragment, null,
|
|
356
358
|
(contextUsage.cacheReadTokens || 0) > 0 && (React.createElement(React.Fragment, null,
|
|
357
|
-
React.createElement(Text, { color:
|
|
359
|
+
React.createElement(Text, { color: theme.colors.menuInfo },
|
|
358
360
|
"\u21AF",
|
|
359
361
|
' ',
|
|
360
362
|
formatNumber(contextUsage.cacheReadTokens || 0),
|
|
@@ -362,13 +364,13 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
362
364
|
t.chatScreen.cached))),
|
|
363
365
|
(contextUsage.cacheCreationTokens || 0) > 0 && (React.createElement(React.Fragment, null,
|
|
364
366
|
(contextUsage.cacheReadTokens || 0) > 0 && (React.createElement(Text, null, " \u00B7 ")),
|
|
365
|
-
React.createElement(Text, { color:
|
|
367
|
+
React.createElement(Text, { color: theme.colors.warning },
|
|
366
368
|
"\u25C6",
|
|
367
369
|
' ',
|
|
368
370
|
formatNumber(contextUsage.cacheCreationTokens || 0),
|
|
369
371
|
' ',
|
|
370
372
|
t.chatScreen.newCache))))),
|
|
371
|
-
isOpenAI && (React.createElement(Text, { color:
|
|
373
|
+
isOpenAI && (React.createElement(Text, { color: theme.colors.menuInfo },
|
|
372
374
|
"\u21AF ",
|
|
373
375
|
formatNumber(contextUsage.cachedTokens || 0),
|
|
374
376
|
' ',
|
|
@@ -2,8 +2,10 @@ import React, { memo, useMemo } from 'react';
|
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { Alert } from '@inkjs/ui';
|
|
4
4
|
import { useI18n } from '../../i18n/index.js';
|
|
5
|
+
import { useTheme } from '../contexts/ThemeContext.js';
|
|
5
6
|
const CommandPanel = memo(({ commands, selectedIndex, visible, maxHeight, isProcessing = false, }) => {
|
|
6
7
|
const { t } = useI18n();
|
|
8
|
+
const { theme } = useTheme();
|
|
7
9
|
// Fixed maximum display items to prevent rendering issues
|
|
8
10
|
const MAX_DISPLAY_ITEMS = 5;
|
|
9
11
|
const effectiveMaxItems = maxHeight
|
|
@@ -41,7 +43,7 @@ const CommandPanel = memo(({ commands, selectedIndex, visible, maxHeight, isProc
|
|
|
41
43
|
React.createElement(Box, { width: "100%" },
|
|
42
44
|
React.createElement(Box, { flexDirection: "column", width: "100%" },
|
|
43
45
|
React.createElement(Box, null,
|
|
44
|
-
React.createElement(Text, { color:
|
|
46
|
+
React.createElement(Text, { color: theme.colors.warning, bold: true }, t.commandPanel.title)),
|
|
45
47
|
React.createElement(Box, { marginTop: 1 },
|
|
46
48
|
React.createElement(Alert, { variant: "info" }, t.commandPanel.processingMessage))))));
|
|
47
49
|
}
|
|
@@ -53,22 +55,22 @@ const CommandPanel = memo(({ commands, selectedIndex, visible, maxHeight, isProc
|
|
|
53
55
|
React.createElement(Box, { width: "100%" },
|
|
54
56
|
React.createElement(Box, { flexDirection: "column", width: "100%" },
|
|
55
57
|
React.createElement(Box, null,
|
|
56
|
-
React.createElement(Text, { color:
|
|
58
|
+
React.createElement(Text, { color: theme.colors.warning, bold: true },
|
|
57
59
|
t.commandPanel.availableCommands,
|
|
58
60
|
' ',
|
|
59
61
|
commands.length > effectiveMaxItems &&
|
|
60
62
|
`(${selectedIndex + 1}/${commands.length})`)),
|
|
61
63
|
displayedCommands.map((command, index) => (React.createElement(Box, { key: command.name, flexDirection: "column", width: "100%" },
|
|
62
|
-
React.createElement(Text, { color: index === displayedSelectedIndex ?
|
|
64
|
+
React.createElement(Text, { color: index === displayedSelectedIndex ? theme.colors.success : theme.colors.menuSecondary, bold: true },
|
|
63
65
|
index === displayedSelectedIndex ? '❯ ' : ' ',
|
|
64
66
|
"/",
|
|
65
67
|
command.name),
|
|
66
68
|
React.createElement(Box, { marginLeft: 3 },
|
|
67
|
-
React.createElement(Text, { color: index === displayedSelectedIndex ?
|
|
69
|
+
React.createElement(Text, { color: index === displayedSelectedIndex ? theme.colors.success : theme.colors.menuSecondary, dimColor: true },
|
|
68
70
|
"\u2514\u2500 ",
|
|
69
71
|
command.description))))),
|
|
70
72
|
commands.length > effectiveMaxItems && (React.createElement(Box, { marginTop: 1 },
|
|
71
|
-
React.createElement(Text, { color:
|
|
73
|
+
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true },
|
|
72
74
|
t.commandPanel.scrollHint,
|
|
73
75
|
" \u00B7",
|
|
74
76
|
' ',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import * as Diff from 'diff';
|
|
4
|
+
import { useTheme } from '../contexts/ThemeContext.js';
|
|
4
5
|
// Helper function to strip line numbers from content (format: "123→content")
|
|
5
6
|
function stripLineNumbers(content) {
|
|
6
7
|
return content
|
|
@@ -13,6 +14,7 @@ function stripLineNumbers(content) {
|
|
|
13
14
|
.join('\n');
|
|
14
15
|
}
|
|
15
16
|
export default function DiffViewer({ oldContent = '', newContent, filename, completeOldContent, completeNewContent, startLineNumber = 1, }) {
|
|
17
|
+
const { theme } = useTheme();
|
|
16
18
|
// If complete file contents are provided, use them for intelligent diff
|
|
17
19
|
const useCompleteContent = completeOldContent && completeNewContent;
|
|
18
20
|
const diffOldContent = useCompleteContent
|
|
@@ -34,10 +36,10 @@ export default function DiffViewer({ oldContent = '', newContent, filename, comp
|
|
|
34
36
|
filename && React.createElement(Text, { color: "cyan" },
|
|
35
37
|
" ",
|
|
36
38
|
filename)),
|
|
37
|
-
React.createElement(Box, { flexDirection: "column" }, allLines.map((line, index) => (React.createElement(Text, { key: index, color: "white", backgroundColor:
|
|
39
|
+
React.createElement(Box, { flexDirection: "column" }, allLines.map((line, index) => (React.createElement(Text, { key: index, color: "white", backgroundColor: theme.colors.diffAdded },
|
|
38
40
|
"+ ",
|
|
39
41
|
line))))));
|
|
40
|
-
}, [isNewFile, diffNewContent, filename]);
|
|
42
|
+
}, [isNewFile, diffNewContent, filename, theme.colors.text, theme.colors.diffAdded]);
|
|
41
43
|
if (isNewFile) {
|
|
42
44
|
return newFileContent;
|
|
43
45
|
}
|
|
@@ -151,13 +153,13 @@ export default function DiffViewer({ oldContent = '', newContent, filename, comp
|
|
|
151
153
|
? String(lineNum).padStart(4, ' ')
|
|
152
154
|
: ' ';
|
|
153
155
|
if (change.type === 'added') {
|
|
154
|
-
return (React.createElement(Text, { key: changeIndex, color: "white", backgroundColor:
|
|
156
|
+
return (React.createElement(Text, { key: changeIndex, color: "white", backgroundColor: theme.colors.diffAdded },
|
|
155
157
|
lineNumStr,
|
|
156
158
|
" + ",
|
|
157
159
|
change.content));
|
|
158
160
|
}
|
|
159
161
|
if (change.type === 'removed') {
|
|
160
|
-
return (React.createElement(Text, { key: changeIndex, color: "white", backgroundColor:
|
|
162
|
+
return (React.createElement(Text, { key: changeIndex, color: "white", backgroundColor: theme.colors.diffRemoved },
|
|
161
163
|
lineNumStr,
|
|
162
164
|
" - ",
|
|
163
165
|
change.content));
|