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
|
@@ -5,8 +5,10 @@ import { Alert } from '@inkjs/ui';
|
|
|
5
5
|
import TextInput from 'ink-text-input';
|
|
6
6
|
import { getSystemPromptConfig, saveSystemPromptConfig, } from '../../utils/apiConfig.js';
|
|
7
7
|
import { useI18n } from '../../i18n/index.js';
|
|
8
|
+
import { useTheme } from '../contexts/ThemeContext.js';
|
|
8
9
|
export default function SystemPromptConfigScreen({ onBack }) {
|
|
9
10
|
const { t } = useI18n();
|
|
11
|
+
const { theme } = useTheme();
|
|
10
12
|
const [config, setConfig] = useState(() => {
|
|
11
13
|
return (getSystemPromptConfig() || {
|
|
12
14
|
active: '',
|
|
@@ -231,26 +233,26 @@ export default function SystemPromptConfigScreen({ onBack }) {
|
|
|
231
233
|
if (view === 'list') {
|
|
232
234
|
const activePrompt = config.prompts.find(p => p.id === config.active);
|
|
233
235
|
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
234
|
-
React.createElement(Box, { marginBottom: 1, borderStyle: "round", borderColor:
|
|
236
|
+
React.createElement(Box, { marginBottom: 1, borderStyle: "round", borderColor: theme.colors.menuInfo, paddingX: 2, paddingY: 1 },
|
|
235
237
|
React.createElement(Box, { flexDirection: "column" },
|
|
236
238
|
React.createElement(Gradient, { name: "rainbow" }, t.systemPromptConfig.title),
|
|
237
|
-
React.createElement(Text, { color:
|
|
239
|
+
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, t.systemPromptConfig.subtitle))),
|
|
238
240
|
error && (React.createElement(Box, { marginBottom: 1 },
|
|
239
241
|
React.createElement(Alert, { variant: "error" }, error))),
|
|
240
242
|
React.createElement(Box, { marginBottom: 1 },
|
|
241
243
|
React.createElement(Text, { bold: true },
|
|
242
244
|
t.systemPromptConfig.activePrompt,
|
|
243
245
|
' ',
|
|
244
|
-
React.createElement(Text, { color:
|
|
246
|
+
React.createElement(Text, { color: theme.colors.success }, activePrompt?.name || t.systemPromptConfig.none))),
|
|
245
247
|
config.prompts.length === 0 ? (React.createElement(Box, { marginBottom: 1 },
|
|
246
|
-
React.createElement(Text, { color:
|
|
247
|
-
React.createElement(Text, { bold: true, color:
|
|
248
|
+
React.createElement(Text, { color: theme.colors.warning }, t.systemPromptConfig.noPromptsConfigured))) : (React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
249
|
+
React.createElement(Text, { bold: true, color: theme.colors.menuInfo }, t.systemPromptConfig.availablePrompts),
|
|
248
250
|
config.prompts.map((prompt, index) => (React.createElement(Box, { key: prompt.id, marginLeft: 2 },
|
|
249
251
|
React.createElement(Text, { color: index === selectedIndex
|
|
250
|
-
?
|
|
252
|
+
? theme.colors.menuSelected
|
|
251
253
|
: prompt.id === config.active
|
|
252
|
-
?
|
|
253
|
-
:
|
|
254
|
+
? theme.colors.menuInfo
|
|
255
|
+
: theme.colors.menuNormal },
|
|
254
256
|
index === selectedIndex ? '❯ ' : ' ',
|
|
255
257
|
prompt.id === config.active ? '✓ ' : ' ',
|
|
256
258
|
prompt.name,
|
|
@@ -260,8 +262,8 @@ export default function SystemPromptConfigScreen({ onBack }) {
|
|
|
260
262
|
prompt.content.substring(0, 50),
|
|
261
263
|
prompt.content.length > 50 ? '...' : '')))))))),
|
|
262
264
|
React.createElement(Box, { marginBottom: 1 },
|
|
263
|
-
React.createElement(Text, { bold: true, color:
|
|
264
|
-
React.createElement(Box, { flexDirection: "column", marginBottom: 1, marginLeft: 2 }, actions.map(action => (React.createElement(Text, { key: action, color: currentAction === action ?
|
|
265
|
+
React.createElement(Text, { bold: true, color: theme.colors.menuInfo }, t.systemPromptConfig.actions)),
|
|
266
|
+
React.createElement(Box, { flexDirection: "column", marginBottom: 1, marginLeft: 2 }, actions.map(action => (React.createElement(Text, { key: action, color: currentAction === action ? theme.colors.menuSelected : theme.colors.menuSecondary, bold: currentAction === action },
|
|
265
267
|
currentAction === action ? '❯ ' : ' ',
|
|
266
268
|
action === 'activate' && t.systemPromptConfig.activate,
|
|
267
269
|
action === 'deactivate' && t.systemPromptConfig.deactivate,
|
|
@@ -270,12 +272,12 @@ export default function SystemPromptConfigScreen({ onBack }) {
|
|
|
270
272
|
action === 'add' && t.systemPromptConfig.addNew,
|
|
271
273
|
action === 'back' && t.systemPromptConfig.escBack)))),
|
|
272
274
|
React.createElement(Box, { marginTop: 1 },
|
|
273
|
-
React.createElement(Text, { color:
|
|
275
|
+
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, t.systemPromptConfig.navigationHint))));
|
|
274
276
|
}
|
|
275
277
|
// Render add/edit view
|
|
276
278
|
if (view === 'add' || view === 'edit') {
|
|
277
279
|
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
278
|
-
React.createElement(Box, { marginBottom: 1, borderStyle: "round", borderColor:
|
|
280
|
+
React.createElement(Box, { marginBottom: 1, borderStyle: "round", borderColor: theme.colors.menuInfo, paddingX: 2, paddingY: 1 },
|
|
279
281
|
React.createElement(Gradient, { name: "rainbow" }, view === 'add'
|
|
280
282
|
? t.systemPromptConfig.addNewTitle
|
|
281
283
|
: t.systemPromptConfig.editTitle)),
|
|
@@ -284,27 +286,27 @@ export default function SystemPromptConfigScreen({ onBack }) {
|
|
|
284
286
|
React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
285
287
|
React.createElement(Box, { marginBottom: 1 },
|
|
286
288
|
React.createElement(Box, { flexDirection: "column" },
|
|
287
|
-
React.createElement(Text, { color: editingField === 'name' ?
|
|
289
|
+
React.createElement(Text, { color: editingField === 'name' ? theme.colors.menuSelected : theme.colors.menuNormal },
|
|
288
290
|
editingField === 'name' ? '❯ ' : ' ',
|
|
289
291
|
t.systemPromptConfig.nameLabel),
|
|
290
292
|
editingField === 'name' && isEditing && (React.createElement(Box, { marginLeft: 3 },
|
|
291
293
|
React.createElement(TextInput, { value: editName, onChange: setEditName, placeholder: t.systemPromptConfig.enterPromptName }))),
|
|
292
294
|
(!isEditing || editingField !== 'name') && (React.createElement(Box, { marginLeft: 3 },
|
|
293
|
-
React.createElement(Text, { color:
|
|
295
|
+
React.createElement(Text, { color: theme.colors.menuSecondary }, editName || t.systemPromptConfig.notSet))))),
|
|
294
296
|
React.createElement(Box, { marginBottom: 1 },
|
|
295
297
|
React.createElement(Box, { flexDirection: "column" },
|
|
296
|
-
React.createElement(Text, { color: editingField === 'content' ?
|
|
298
|
+
React.createElement(Text, { color: editingField === 'content' ? theme.colors.menuSelected : theme.colors.menuNormal },
|
|
297
299
|
editingField === 'content' ? '❯ ' : ' ',
|
|
298
300
|
t.systemPromptConfig.contentLabel),
|
|
299
301
|
editingField === 'content' && isEditing && (React.createElement(Box, { marginLeft: 3 },
|
|
300
302
|
React.createElement(TextInput, { value: editContent, onChange: setEditContent, placeholder: t.systemPromptConfig.enterPromptContent }))),
|
|
301
303
|
(!isEditing || editingField !== 'content') && (React.createElement(Box, { marginLeft: 3 },
|
|
302
|
-
React.createElement(Text, { color:
|
|
304
|
+
React.createElement(Text, { color: theme.colors.menuSecondary }, editContent
|
|
303
305
|
? editContent.substring(0, 100) +
|
|
304
306
|
(editContent.length > 100 ? '...' : '')
|
|
305
307
|
: t.systemPromptConfig.notSet)))))),
|
|
306
308
|
React.createElement(Box, { marginTop: 1 },
|
|
307
|
-
React.createElement(Text, { color:
|
|
309
|
+
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, t.systemPromptConfig.editingHint))));
|
|
308
310
|
}
|
|
309
311
|
// Render delete confirmation
|
|
310
312
|
if (view === 'confirmDelete') {
|
|
@@ -315,10 +317,10 @@ export default function SystemPromptConfigScreen({ onBack }) {
|
|
|
315
317
|
React.createElement(Text, null,
|
|
316
318
|
t.systemPromptConfig.deleteConfirmMessage,
|
|
317
319
|
" \"",
|
|
318
|
-
React.createElement(Text, { bold: true, color:
|
|
320
|
+
React.createElement(Text, { bold: true, color: theme.colors.warning }, promptToDelete?.name),
|
|
319
321
|
"\"?")),
|
|
320
322
|
React.createElement(Box, { marginTop: 1 },
|
|
321
|
-
React.createElement(Text, { color:
|
|
323
|
+
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, t.systemPromptConfig.confirmHint))));
|
|
322
324
|
}
|
|
323
325
|
return null;
|
|
324
326
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React, { useMemo, useCallback, useState } from 'react';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import { Alert } from '@inkjs/ui';
|
|
4
|
+
import Menu from '../components/Menu.js';
|
|
5
|
+
import DiffViewer from '../components/DiffViewer.js';
|
|
6
|
+
import { useTheme } from '../contexts/ThemeContext.js';
|
|
7
|
+
import { useI18n } from '../../i18n/index.js';
|
|
8
|
+
const sampleOldCode = `function greet(name) {
|
|
9
|
+
console.log("Hello " + name);
|
|
10
|
+
return "Welcome!";
|
|
11
|
+
}`;
|
|
12
|
+
const sampleNewCode = `function greet(name: string): string {
|
|
13
|
+
console.log(\`Hello \${name}\`);
|
|
14
|
+
return \`Welcome, \${name}!\`;
|
|
15
|
+
}`;
|
|
16
|
+
export default function ThemeSettingsScreen({ onBack, inlineMode = false, }) {
|
|
17
|
+
const { themeType, setThemeType } = useTheme();
|
|
18
|
+
const { t } = useI18n();
|
|
19
|
+
// Use themeType from context which is already loaded from config
|
|
20
|
+
const [selectedTheme, setSelectedTheme] = useState(themeType);
|
|
21
|
+
const [infoText, setInfoText] = useState('');
|
|
22
|
+
const themeOptions = useMemo(() => [
|
|
23
|
+
{
|
|
24
|
+
label: selectedTheme === 'dark' ? `✓ ${t.themeSettings.darkTheme}` : t.themeSettings.darkTheme,
|
|
25
|
+
value: 'dark',
|
|
26
|
+
infoText: t.themeSettings.darkThemeInfo,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: selectedTheme === 'light' ? `✓ ${t.themeSettings.lightTheme}` : t.themeSettings.lightTheme,
|
|
30
|
+
value: 'light',
|
|
31
|
+
infoText: t.themeSettings.lightThemeInfo,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: selectedTheme === 'github-dark' ? `✓ ${t.themeSettings.githubDark}` : t.themeSettings.githubDark,
|
|
35
|
+
value: 'github-dark',
|
|
36
|
+
infoText: t.themeSettings.githubDarkInfo,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: selectedTheme === 'rainbow' ? `✓ ${t.themeSettings.rainbow}` : t.themeSettings.rainbow,
|
|
40
|
+
value: 'rainbow',
|
|
41
|
+
infoText: t.themeSettings.rainbowInfo,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: selectedTheme === 'solarized-dark' ? `✓ ${t.themeSettings.solarizedDark}` : t.themeSettings.solarizedDark,
|
|
45
|
+
value: 'solarized-dark',
|
|
46
|
+
infoText: t.themeSettings.solarizedDarkInfo,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: selectedTheme === 'nord' ? `✓ ${t.themeSettings.nord}` : t.themeSettings.nord,
|
|
50
|
+
value: 'nord',
|
|
51
|
+
infoText: t.themeSettings.nordInfo,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
label: t.themeSettings.back,
|
|
55
|
+
value: 'back',
|
|
56
|
+
color: 'gray',
|
|
57
|
+
infoText: t.themeSettings.backInfo,
|
|
58
|
+
},
|
|
59
|
+
], [selectedTheme, t]);
|
|
60
|
+
const handleSelect = useCallback((value) => {
|
|
61
|
+
if (value === 'back') {
|
|
62
|
+
// Restore original theme if cancelled
|
|
63
|
+
setThemeType(selectedTheme);
|
|
64
|
+
onBack();
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Confirm and apply the theme (Enter pressed)
|
|
68
|
+
const newTheme = value;
|
|
69
|
+
setSelectedTheme(newTheme);
|
|
70
|
+
setThemeType(newTheme);
|
|
71
|
+
}
|
|
72
|
+
}, [onBack, setThemeType, selectedTheme]);
|
|
73
|
+
const handleSelectionChange = useCallback((newInfoText, value) => {
|
|
74
|
+
setInfoText(newInfoText);
|
|
75
|
+
// Preview theme on selection change (navigation)
|
|
76
|
+
if (value === 'back') {
|
|
77
|
+
// Restore to selected theme when hovering on "Back"
|
|
78
|
+
setThemeType(selectedTheme);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Preview the theme
|
|
82
|
+
setThemeType(value);
|
|
83
|
+
}
|
|
84
|
+
}, [setThemeType, selectedTheme]);
|
|
85
|
+
useInput((_input, key) => {
|
|
86
|
+
if (key.escape) {
|
|
87
|
+
// Restore original theme on ESC
|
|
88
|
+
setThemeType(selectedTheme);
|
|
89
|
+
onBack();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
93
|
+
!inlineMode && (React.createElement(Box, { borderStyle: "round", borderColor: "cyan", paddingX: 1 },
|
|
94
|
+
React.createElement(Text, { bold: true, color: "cyan" }, t.themeSettings.title))),
|
|
95
|
+
React.createElement(Box, { flexDirection: "column", paddingX: 1 },
|
|
96
|
+
React.createElement(Text, { color: "gray", dimColor: true },
|
|
97
|
+
t.themeSettings.current,
|
|
98
|
+
" ",
|
|
99
|
+
themeOptions.find(opt => opt.value === selectedTheme)?.label.replace('✓ ', '') || selectedTheme)),
|
|
100
|
+
React.createElement(Menu, { options: themeOptions, onSelect: handleSelect, onSelectionChange: handleSelectionChange }),
|
|
101
|
+
React.createElement(Box, { flexDirection: "column", paddingX: 1 },
|
|
102
|
+
React.createElement(Text, { color: "gray", dimColor: true }, t.themeSettings.preview),
|
|
103
|
+
React.createElement(DiffViewer, { oldContent: sampleOldCode, newContent: sampleNewCode, filename: "example.ts" })),
|
|
104
|
+
infoText && (React.createElement(Box, { paddingX: 1 },
|
|
105
|
+
React.createElement(Alert, { variant: "info" }, infoText)))));
|
|
106
|
+
}
|
|
@@ -14,6 +14,7 @@ import CodeBaseConfigScreen from './CodeBaseConfigScreen.js';
|
|
|
14
14
|
import SystemPromptConfigScreen from './SystemPromptConfigScreen.js';
|
|
15
15
|
import CustomHeadersScreen from './CustomHeadersScreen.js';
|
|
16
16
|
import LanguageSettingsScreen from './LanguageSettingsScreen.js';
|
|
17
|
+
import ThemeSettingsScreen from './ThemeSettingsScreen.js';
|
|
17
18
|
import { useI18n } from '../../i18n/index.js';
|
|
18
19
|
export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
|
|
19
20
|
const { t } = useI18n();
|
|
@@ -75,6 +76,11 @@ export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
|
|
|
75
76
|
value: 'language',
|
|
76
77
|
infoText: t.welcome.languageSettingsInfo,
|
|
77
78
|
},
|
|
79
|
+
{
|
|
80
|
+
label: t.welcome.themeSettings,
|
|
81
|
+
value: 'theme',
|
|
82
|
+
infoText: t.welcome.themeSettingsInfo,
|
|
83
|
+
},
|
|
78
84
|
{
|
|
79
85
|
label: t.welcome.exit,
|
|
80
86
|
value: 'exit',
|
|
@@ -112,6 +118,9 @@ export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
|
|
|
112
118
|
else if (value === 'language') {
|
|
113
119
|
setInlineView('language-settings');
|
|
114
120
|
}
|
|
121
|
+
else if (value === 'theme') {
|
|
122
|
+
setInlineView('theme-settings');
|
|
123
|
+
}
|
|
115
124
|
else {
|
|
116
125
|
// Pass through to parent for other actions (chat, exit, etc.)
|
|
117
126
|
onMenuSelect?.(value);
|
|
@@ -185,5 +194,6 @@ export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
|
|
|
185
194
|
inlineView === 'customheaders' && (React.createElement(Box, { paddingX: 1 },
|
|
186
195
|
React.createElement(CustomHeadersScreen, { onBack: handleBackToMenu }))),
|
|
187
196
|
inlineView === 'language-settings' && (React.createElement(Box, { paddingX: 1 },
|
|
188
|
-
React.createElement(LanguageSettingsScreen, { onBack: handleBackToMenu, inlineMode: true })))
|
|
197
|
+
React.createElement(LanguageSettingsScreen, { onBack: handleBackToMenu, inlineMode: true }))),
|
|
198
|
+
inlineView === 'theme-settings' && (React.createElement(ThemeSettingsScreen, { onBack: handleBackToMenu, inlineMode: true }))));
|
|
189
199
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type ThemeType = 'dark' | 'light' | 'github-dark' | 'rainbow' | 'solarized-dark' | 'nord';
|
|
2
|
+
export interface Theme {
|
|
3
|
+
name: string;
|
|
4
|
+
type: ThemeType;
|
|
5
|
+
colors: {
|
|
6
|
+
background: string;
|
|
7
|
+
text: string;
|
|
8
|
+
border: string;
|
|
9
|
+
diffAdded: string;
|
|
10
|
+
diffRemoved: string;
|
|
11
|
+
diffModified: string;
|
|
12
|
+
lineNumber: string;
|
|
13
|
+
lineNumberBorder: string;
|
|
14
|
+
menuSelected: string;
|
|
15
|
+
menuNormal: string;
|
|
16
|
+
menuInfo: string;
|
|
17
|
+
menuSecondary: string;
|
|
18
|
+
error: string;
|
|
19
|
+
warning: string;
|
|
20
|
+
success: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare const themes: Record<ThemeType, Theme>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
export const themes = {
|
|
2
|
+
dark: {
|
|
3
|
+
name: 'Dark',
|
|
4
|
+
type: 'dark',
|
|
5
|
+
colors: {
|
|
6
|
+
background: '#1e1e1e',
|
|
7
|
+
text: '#d4d4d4',
|
|
8
|
+
border: '#3e3e3e',
|
|
9
|
+
diffAdded: '#0d4d3d',
|
|
10
|
+
diffRemoved: '#5a1f1f',
|
|
11
|
+
diffModified: '#dcdcaa',
|
|
12
|
+
lineNumber: '#858585',
|
|
13
|
+
lineNumberBorder: '#3e3e3e',
|
|
14
|
+
// Menu colors
|
|
15
|
+
menuSelected: 'green',
|
|
16
|
+
menuNormal: 'white',
|
|
17
|
+
menuInfo: 'cyan',
|
|
18
|
+
menuSecondary: 'gray',
|
|
19
|
+
// Status colors
|
|
20
|
+
error: 'red',
|
|
21
|
+
warning: 'yellow',
|
|
22
|
+
success: 'green',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
light: {
|
|
26
|
+
name: 'Light',
|
|
27
|
+
type: 'light',
|
|
28
|
+
colors: {
|
|
29
|
+
background: '#ffffff',
|
|
30
|
+
text: '#ffffff',
|
|
31
|
+
border: '#e0e0e0',
|
|
32
|
+
diffAdded: '#006400',
|
|
33
|
+
diffRemoved: '#8B0000',
|
|
34
|
+
diffModified: '#0000ff',
|
|
35
|
+
lineNumber: '#6e6e6e',
|
|
36
|
+
lineNumberBorder: '#e0e0e0',
|
|
37
|
+
// Menu colors - darker for better visibility
|
|
38
|
+
menuSelected: '#006400',
|
|
39
|
+
menuNormal: '#000000',
|
|
40
|
+
menuInfo: '#0066cc',
|
|
41
|
+
menuSecondary: '#666666',
|
|
42
|
+
// Status colors - darker for better visibility on white background
|
|
43
|
+
error: '#cc0000',
|
|
44
|
+
warning: '#cc6600',
|
|
45
|
+
success: '#006400',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
'github-dark': {
|
|
49
|
+
name: 'GitHub Dark',
|
|
50
|
+
type: 'github-dark',
|
|
51
|
+
colors: {
|
|
52
|
+
background: '#0d1117',
|
|
53
|
+
text: '#c9d1d9',
|
|
54
|
+
border: '#30363d',
|
|
55
|
+
diffAdded: '#1a4d2e',
|
|
56
|
+
diffRemoved: '#6e1a1a',
|
|
57
|
+
diffModified: '#9e6a03',
|
|
58
|
+
lineNumber: '#6e7681',
|
|
59
|
+
lineNumberBorder: '#21262d',
|
|
60
|
+
// Menu colors
|
|
61
|
+
menuSelected: '#58a6ff',
|
|
62
|
+
menuNormal: '#c9d1d9',
|
|
63
|
+
menuInfo: '#58a6ff',
|
|
64
|
+
menuSecondary: '#8b949e',
|
|
65
|
+
// Status colors
|
|
66
|
+
error: '#f85149',
|
|
67
|
+
warning: '#d29922',
|
|
68
|
+
success: '#3fb950',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
rainbow: {
|
|
72
|
+
name: 'Rainbow',
|
|
73
|
+
type: 'rainbow',
|
|
74
|
+
colors: {
|
|
75
|
+
background: '#1a1a2e',
|
|
76
|
+
text: '#ffffff',
|
|
77
|
+
border: '#ff6b9d',
|
|
78
|
+
diffAdded: '#16697a',
|
|
79
|
+
diffRemoved: '#82204a',
|
|
80
|
+
diffModified: '#5f4b8b',
|
|
81
|
+
lineNumber: '#ffa07a',
|
|
82
|
+
lineNumberBorder: '#ff6b9d',
|
|
83
|
+
// Menu colors - vibrant rainbow colors
|
|
84
|
+
menuSelected: '#ff006e',
|
|
85
|
+
menuNormal: '#00f5ff',
|
|
86
|
+
menuInfo: '#ffbe0b',
|
|
87
|
+
menuSecondary: '#8338ec',
|
|
88
|
+
// Status colors - bright and colorful
|
|
89
|
+
error: '#ff006e',
|
|
90
|
+
warning: '#ffbe0b',
|
|
91
|
+
success: '#06ffa5',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
'solarized-dark': {
|
|
95
|
+
name: 'Solarized Dark',
|
|
96
|
+
type: 'solarized-dark',
|
|
97
|
+
colors: {
|
|
98
|
+
background: '#002b36',
|
|
99
|
+
text: '#839496',
|
|
100
|
+
border: '#073642',
|
|
101
|
+
diffAdded: '#0a3d2c',
|
|
102
|
+
diffRemoved: '#5c1f1f',
|
|
103
|
+
diffModified: '#5d4f1a',
|
|
104
|
+
lineNumber: '#586e75',
|
|
105
|
+
lineNumberBorder: '#073642',
|
|
106
|
+
// Menu colors
|
|
107
|
+
menuSelected: '#2aa198',
|
|
108
|
+
menuNormal: '#93a1a1',
|
|
109
|
+
menuInfo: '#268bd2',
|
|
110
|
+
menuSecondary: '#657b83',
|
|
111
|
+
// Status colors
|
|
112
|
+
error: '#dc322f',
|
|
113
|
+
warning: '#b58900',
|
|
114
|
+
success: '#859900',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
nord: {
|
|
118
|
+
name: 'Nord',
|
|
119
|
+
type: 'nord',
|
|
120
|
+
colors: {
|
|
121
|
+
background: '#2e3440',
|
|
122
|
+
text: '#d8dee9',
|
|
123
|
+
border: '#3b4252',
|
|
124
|
+
diffAdded: '#1d3a2f',
|
|
125
|
+
diffRemoved: '#5c2a2a',
|
|
126
|
+
diffModified: '#5a4d2f',
|
|
127
|
+
lineNumber: '#4c566a',
|
|
128
|
+
lineNumberBorder: '#3b4252',
|
|
129
|
+
// Menu colors
|
|
130
|
+
menuSelected: '#88c0d0',
|
|
131
|
+
menuNormal: '#d8dee9',
|
|
132
|
+
menuInfo: '#81a1c1',
|
|
133
|
+
menuSecondary: '#616e88',
|
|
134
|
+
// Status colors
|
|
135
|
+
error: '#bf616a',
|
|
136
|
+
warning: '#ebcb8b',
|
|
137
|
+
success: '#a3be8c',
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
@@ -283,13 +283,21 @@ export function initializeProfiles() {
|
|
|
283
283
|
migrateLegacyConfig();
|
|
284
284
|
// Ensure the active profile exists and is loaded to config.json
|
|
285
285
|
const activeProfile = getActiveProfileName();
|
|
286
|
-
|
|
286
|
+
let profileConfig = loadProfile(activeProfile);
|
|
287
287
|
if (profileConfig) {
|
|
288
288
|
// Sync the active profile to config.json
|
|
289
289
|
saveConfig(profileConfig);
|
|
290
290
|
}
|
|
291
291
|
else {
|
|
292
|
-
// If active profile doesn't exist,
|
|
293
|
-
|
|
292
|
+
// If active profile doesn't exist, create it first
|
|
293
|
+
// This is especially important for first-time installations
|
|
294
|
+
const defaultConfig = loadConfig();
|
|
295
|
+
saveProfile(activeProfile, defaultConfig);
|
|
296
|
+
setActiveProfileName(activeProfile);
|
|
297
|
+
// Now load and sync the newly created profile
|
|
298
|
+
profileConfig = loadProfile(activeProfile);
|
|
299
|
+
if (profileConfig) {
|
|
300
|
+
saveConfig(profileConfig);
|
|
301
|
+
}
|
|
294
302
|
}
|
|
295
303
|
}
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -26,6 +26,12 @@ export declare class Logger {
|
|
|
26
26
|
debug(message: string, meta?: any): void;
|
|
27
27
|
log(level: LogLevel, message: string, meta?: any): void;
|
|
28
28
|
}
|
|
29
|
-
declare const
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
declare const logger: {
|
|
30
|
+
error(message: string, meta?: any): void;
|
|
31
|
+
warn(message: string, meta?: any): void;
|
|
32
|
+
info(message: string, meta?: any): void;
|
|
33
|
+
debug(message: string, meta?: any): void;
|
|
34
|
+
log(level: LogLevel, message: string, meta?: any): void;
|
|
35
|
+
};
|
|
36
|
+
export default logger;
|
|
37
|
+
export { logger };
|
package/dist/utils/logger.js
CHANGED
|
@@ -92,6 +92,31 @@ export class Logger {
|
|
|
92
92
|
this.writeLog(level, message, meta);
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
// Lazy initialization to avoid blocking startup
|
|
96
|
+
let _defaultLogger = null;
|
|
97
|
+
function getDefaultLogger() {
|
|
98
|
+
if (!_defaultLogger) {
|
|
99
|
+
_defaultLogger = new Logger();
|
|
100
|
+
}
|
|
101
|
+
return _defaultLogger;
|
|
102
|
+
}
|
|
103
|
+
// Create a proxy object that lazily initializes the logger
|
|
104
|
+
const logger = {
|
|
105
|
+
error(message, meta) {
|
|
106
|
+
getDefaultLogger().error(message, meta);
|
|
107
|
+
},
|
|
108
|
+
warn(message, meta) {
|
|
109
|
+
getDefaultLogger().warn(message, meta);
|
|
110
|
+
},
|
|
111
|
+
info(message, meta) {
|
|
112
|
+
getDefaultLogger().info(message, meta);
|
|
113
|
+
},
|
|
114
|
+
debug(message, meta) {
|
|
115
|
+
getDefaultLogger().debug(message, meta);
|
|
116
|
+
},
|
|
117
|
+
log(level, message, meta) {
|
|
118
|
+
getDefaultLogger().log(level, message, meta);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
export default logger;
|
|
122
|
+
export { logger };
|
|
@@ -19,15 +19,18 @@ import os from 'os';
|
|
|
19
19
|
import path from 'path';
|
|
20
20
|
let toolsCache = null;
|
|
21
21
|
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
const session = sessionManager.getCurrentSession();
|
|
25
|
-
return session ? session.id : null;
|
|
26
|
-
});
|
|
22
|
+
// Lazy initialization of TODO service to avoid circular dependencies
|
|
23
|
+
let todoService = null;
|
|
27
24
|
/**
|
|
28
|
-
* Get the TODO service instance
|
|
25
|
+
* Get the TODO service instance (lazy initialization)
|
|
29
26
|
*/
|
|
30
27
|
export function getTodoService() {
|
|
28
|
+
if (!todoService) {
|
|
29
|
+
todoService = new TodoService(path.join(os.homedir(), '.snow'), () => {
|
|
30
|
+
const session = sessionManager.getCurrentSession();
|
|
31
|
+
return session ? session.id : null;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
31
34
|
return todoService;
|
|
32
35
|
}
|
|
33
36
|
/**
|
|
@@ -123,8 +126,9 @@ async function refreshToolsCache() {
|
|
|
123
126
|
});
|
|
124
127
|
}
|
|
125
128
|
// Add built-in TODO tools (always available)
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
const todoSvc = getTodoService(); // This will never return null after lazy init
|
|
130
|
+
await todoSvc.initialize();
|
|
131
|
+
const todoTools = todoSvc.getTools();
|
|
128
132
|
const todoServiceTools = todoTools.map(tool => ({
|
|
129
133
|
name: tool.name.replace('todo-', ''),
|
|
130
134
|
description: tool.description || '',
|
|
@@ -689,7 +693,7 @@ export async function executeMCPTool(toolName, args, abortSignal, onTokenUpdate)
|
|
|
689
693
|
}
|
|
690
694
|
if (serviceName === 'todo') {
|
|
691
695
|
// Handle built-in TODO tools (no connection needed)
|
|
692
|
-
return await
|
|
696
|
+
return await getTodoService().executeTool(actualToolName, args);
|
|
693
697
|
}
|
|
694
698
|
else if (serviceName === 'notebook') {
|
|
695
699
|
// Handle built-in Notebook tools (no connection needed)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ThemeType } from '../ui/themes/index.js';
|
|
2
|
+
interface ThemeConfig {
|
|
3
|
+
theme: ThemeType;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Load theme configuration from file system
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadThemeConfig(): ThemeConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Save theme configuration to file system
|
|
11
|
+
*/
|
|
12
|
+
export declare function saveThemeConfig(config: ThemeConfig): void;
|
|
13
|
+
/**
|
|
14
|
+
* Get current theme setting
|
|
15
|
+
*/
|
|
16
|
+
export declare function getCurrentTheme(): ThemeType;
|
|
17
|
+
/**
|
|
18
|
+
* Set theme and persist to file system
|
|
19
|
+
*/
|
|
20
|
+
export declare function setCurrentTheme(theme: ThemeType): void;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
4
|
+
const CONFIG_DIR = join(homedir(), '.snow');
|
|
5
|
+
const THEME_CONFIG_FILE = join(CONFIG_DIR, 'theme.json');
|
|
6
|
+
const DEFAULT_CONFIG = {
|
|
7
|
+
theme: 'dark',
|
|
8
|
+
};
|
|
9
|
+
function ensureConfigDirectory() {
|
|
10
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
11
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Load theme configuration from file system
|
|
16
|
+
*/
|
|
17
|
+
export function loadThemeConfig() {
|
|
18
|
+
ensureConfigDirectory();
|
|
19
|
+
if (!existsSync(THEME_CONFIG_FILE)) {
|
|
20
|
+
saveThemeConfig(DEFAULT_CONFIG);
|
|
21
|
+
return DEFAULT_CONFIG;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const configData = readFileSync(THEME_CONFIG_FILE, 'utf-8');
|
|
25
|
+
const config = JSON.parse(configData);
|
|
26
|
+
return {
|
|
27
|
+
...DEFAULT_CONFIG,
|
|
28
|
+
...config,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
// If config file is corrupted, return default config
|
|
33
|
+
return DEFAULT_CONFIG;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Save theme configuration to file system
|
|
38
|
+
*/
|
|
39
|
+
export function saveThemeConfig(config) {
|
|
40
|
+
ensureConfigDirectory();
|
|
41
|
+
try {
|
|
42
|
+
const configData = JSON.stringify(config, null, 2);
|
|
43
|
+
writeFileSync(THEME_CONFIG_FILE, configData, 'utf-8');
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('Failed to save theme config:', error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get current theme setting
|
|
51
|
+
*/
|
|
52
|
+
export function getCurrentTheme() {
|
|
53
|
+
const config = loadThemeConfig();
|
|
54
|
+
return config.theme;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Set theme and persist to file system
|
|
58
|
+
*/
|
|
59
|
+
export function setCurrentTheme(theme) {
|
|
60
|
+
saveThemeConfig({ theme });
|
|
61
|
+
}
|