snow-ai 0.4.10 → 0.4.11

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 CHANGED
@@ -2,8 +2,9 @@ import React, { useState, useEffect, Suspense } from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  import { Alert } from '@inkjs/ui';
4
4
  import Spinner from 'ink-spinner';
5
- import WelcomeScreen from './ui/pages/WelcomeScreen.js';
6
- // Lazy load heavy components to improve startup time
5
+ // Lazy load all page components to improve startup time
6
+ // Only load components when they are actually needed
7
+ const WelcomeScreen = React.lazy(() => import('./ui/pages/WelcomeScreen.js'));
7
8
  const ChatScreen = React.lazy(() => import('./ui/pages/ChatScreen.js'));
8
9
  const HeadlessModeScreen = React.lazy(() => import('./ui/pages/HeadlessModeScreen.js'));
9
10
  const MCPConfigScreen = React.lazy(() => import('./ui/pages/MCPConfigScreen.js'));
@@ -64,7 +65,8 @@ function AppContent({ version, skipWelcome, }) {
64
65
  React.createElement(Text, null, " Loading...")));
65
66
  switch (currentView) {
66
67
  case 'welcome':
67
- return (React.createElement(WelcomeScreen, { version: version, onMenuSelect: handleMenuSelect }));
68
+ return (React.createElement(Suspense, { fallback: loadingFallback },
69
+ React.createElement(WelcomeScreen, { version: version, onMenuSelect: handleMenuSelect })));
68
70
  case 'chat':
69
71
  return (React.createElement(Suspense, { fallback: loadingFallback },
70
72
  React.createElement(ChatScreen, { key: chatScreenKey, skipWelcome: skipWelcome })));
@@ -82,7 +84,8 @@ function AppContent({ version, skipWelcome, }) {
82
84
  return (React.createElement(Suspense, { fallback: loadingFallback },
83
85
  React.createElement(CustomHeadersScreen, { onBack: () => setCurrentView('welcome') })));
84
86
  default:
85
- return (React.createElement(WelcomeScreen, { version: version, onMenuSelect: handleMenuSelect }));
87
+ return (React.createElement(Suspense, { fallback: loadingFallback },
88
+ React.createElement(WelcomeScreen, { version: version, onMenuSelect: handleMenuSelect })));
86
89
  }
87
90
  };
88
91
  return (React.createElement(Box, { flexDirection: "column", width: terminalWidth },
@@ -1,21 +1,23 @@
1
- import React, { useState, useMemo, useCallback, useEffect, useRef } from 'react';
1
+ import React, { useState, useMemo, useCallback, useEffect, useRef, Suspense } from 'react';
2
2
  import { Box, Text, useStdout, Static } from 'ink';
3
3
  import { Alert } from '@inkjs/ui';
4
4
  import Gradient from 'ink-gradient';
5
5
  import ansiEscapes from 'ansi-escapes';
6
+ import Spinner from 'ink-spinner';
6
7
  import Menu from '../components/Menu.js';
7
8
  import { useTerminalSize } from '../../hooks/useTerminalSize.js';
8
- import ConfigScreen from './ConfigScreen.js';
9
- import ProxyConfigScreen from './ProxyConfigScreen.js';
10
- import SubAgentConfigScreen from './SubAgentConfigScreen.js';
11
- import SubAgentListScreen from './SubAgentListScreen.js';
12
- import SensitiveCommandConfigScreen from './SensitiveCommandConfigScreen.js';
13
- import CodeBaseConfigScreen from './CodeBaseConfigScreen.js';
14
- import SystemPromptConfigScreen from './SystemPromptConfigScreen.js';
15
- import CustomHeadersScreen from './CustomHeadersScreen.js';
16
- import LanguageSettingsScreen from './LanguageSettingsScreen.js';
17
- import ThemeSettingsScreen from './ThemeSettingsScreen.js';
18
9
  import { useI18n } from '../../i18n/index.js';
10
+ // Lazy load all configuration screens for better startup performance
11
+ const ConfigScreen = React.lazy(() => import('./ConfigScreen.js'));
12
+ const ProxyConfigScreen = React.lazy(() => import('./ProxyConfigScreen.js'));
13
+ const SubAgentConfigScreen = React.lazy(() => import('./SubAgentConfigScreen.js'));
14
+ const SubAgentListScreen = React.lazy(() => import('./SubAgentListScreen.js'));
15
+ const SensitiveCommandConfigScreen = React.lazy(() => import('./SensitiveCommandConfigScreen.js'));
16
+ const CodeBaseConfigScreen = React.lazy(() => import('./CodeBaseConfigScreen.js'));
17
+ const SystemPromptConfigScreen = React.lazy(() => import('./SystemPromptConfigScreen.js'));
18
+ const CustomHeadersScreen = React.lazy(() => import('./CustomHeadersScreen.js'));
19
+ const LanguageSettingsScreen = React.lazy(() => import('./LanguageSettingsScreen.js'));
20
+ const ThemeSettingsScreen = React.lazy(() => import('./ThemeSettingsScreen.js'));
19
21
  export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
20
22
  const { t } = useI18n();
21
23
  const [infoText, setInfoText] = useState(t.welcome.startChatInfo);
@@ -158,6 +160,11 @@ export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
158
160
  clearTimeout(handler);
159
161
  };
160
162
  }, [terminalWidth]); // Remove stdout from dependencies to avoid loops
163
+ // Loading fallback component for lazy-loaded screens
164
+ const loadingFallback = (React.createElement(Box, { paddingX: 1 },
165
+ React.createElement(Text, { color: "cyan" },
166
+ React.createElement(Spinner, { type: "dots" })),
167
+ React.createElement(Text, null, " Loading...")));
161
168
  return (React.createElement(Box, { flexDirection: "column", width: terminalWidth },
162
169
  React.createElement(Static, { key: remountKey, items: [
163
170
  React.createElement(Box, { key: "welcome-header", flexDirection: "row", paddingLeft: 2, paddingTop: 1, paddingBottom: 0, width: terminalWidth },
@@ -175,25 +182,36 @@ export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
175
182
  React.createElement(Menu, { options: menuOptions, onSelect: handleInlineMenuSelect, onSelectionChange: handleSelectionChange })))),
176
183
  inlineView === 'menu' && (React.createElement(Box, { paddingX: 1 },
177
184
  React.createElement(Alert, { variant: "info" }, infoText))),
178
- inlineView === 'config' && (React.createElement(Box, { paddingX: 1 },
179
- React.createElement(ConfigScreen, { onBack: handleBackToMenu, onSave: handleConfigSave, inlineMode: true }))),
180
- inlineView === 'proxy-config' && (React.createElement(Box, { paddingX: 1 },
181
- React.createElement(ProxyConfigScreen, { onBack: handleBackToMenu, onSave: handleConfigSave, inlineMode: true }))),
182
- inlineView === 'codebase-config' && (React.createElement(Box, { paddingX: 1 },
183
- React.createElement(CodeBaseConfigScreen, { onBack: handleBackToMenu, onSave: handleConfigSave, inlineMode: true }))),
184
- inlineView === 'subagent-list' && (React.createElement(Box, { paddingX: 1 },
185
- React.createElement(SubAgentListScreen, { onBack: handleBackToMenu, onAdd: handleSubAgentAdd, onEdit: handleSubAgentEdit, inlineMode: true }))),
186
- inlineView === 'subagent-add' && (React.createElement(Box, { paddingX: 1 },
187
- React.createElement(SubAgentConfigScreen, { onBack: () => setInlineView('subagent-list'), onSave: handleSubAgentSave, inlineMode: true }))),
188
- inlineView === 'subagent-edit' && (React.createElement(Box, { paddingX: 1 },
189
- React.createElement(SubAgentConfigScreen, { onBack: () => setInlineView('subagent-list'), onSave: handleSubAgentSave, agentId: editingAgentId, inlineMode: true }))),
190
- inlineView === 'sensitive-commands' && (React.createElement(Box, { paddingX: 1 },
191
- React.createElement(SensitiveCommandConfigScreen, { onBack: handleBackToMenu, inlineMode: true }))),
192
- inlineView === 'systemprompt' && (React.createElement(Box, { paddingX: 1 },
193
- React.createElement(SystemPromptConfigScreen, { onBack: handleBackToMenu }))),
194
- inlineView === 'customheaders' && (React.createElement(Box, { paddingX: 1 },
195
- React.createElement(CustomHeadersScreen, { onBack: handleBackToMenu }))),
196
- inlineView === 'language-settings' && (React.createElement(Box, { paddingX: 1 },
197
- React.createElement(LanguageSettingsScreen, { onBack: handleBackToMenu, inlineMode: true }))),
198
- inlineView === 'theme-settings' && (React.createElement(ThemeSettingsScreen, { onBack: handleBackToMenu, inlineMode: true }))));
185
+ inlineView === 'config' && (React.createElement(Suspense, { fallback: loadingFallback },
186
+ React.createElement(Box, { paddingX: 1 },
187
+ React.createElement(ConfigScreen, { onBack: handleBackToMenu, onSave: handleConfigSave, inlineMode: true })))),
188
+ inlineView === 'proxy-config' && (React.createElement(Suspense, { fallback: loadingFallback },
189
+ React.createElement(Box, { paddingX: 1 },
190
+ React.createElement(ProxyConfigScreen, { onBack: handleBackToMenu, onSave: handleConfigSave, inlineMode: true })))),
191
+ inlineView === 'codebase-config' && (React.createElement(Suspense, { fallback: loadingFallback },
192
+ React.createElement(Box, { paddingX: 1 },
193
+ React.createElement(CodeBaseConfigScreen, { onBack: handleBackToMenu, onSave: handleConfigSave, inlineMode: true })))),
194
+ inlineView === 'subagent-list' && (React.createElement(Suspense, { fallback: loadingFallback },
195
+ React.createElement(Box, { paddingX: 1 },
196
+ React.createElement(SubAgentListScreen, { onBack: handleBackToMenu, onAdd: handleSubAgentAdd, onEdit: handleSubAgentEdit, inlineMode: true })))),
197
+ inlineView === 'subagent-add' && (React.createElement(Suspense, { fallback: loadingFallback },
198
+ React.createElement(Box, { paddingX: 1 },
199
+ React.createElement(SubAgentConfigScreen, { onBack: () => setInlineView('subagent-list'), onSave: handleSubAgentSave, inlineMode: true })))),
200
+ inlineView === 'subagent-edit' && (React.createElement(Suspense, { fallback: loadingFallback },
201
+ React.createElement(Box, { paddingX: 1 },
202
+ React.createElement(SubAgentConfigScreen, { onBack: () => setInlineView('subagent-list'), onSave: handleSubAgentSave, agentId: editingAgentId, inlineMode: true })))),
203
+ inlineView === 'sensitive-commands' && (React.createElement(Suspense, { fallback: loadingFallback },
204
+ React.createElement(Box, { paddingX: 1 },
205
+ React.createElement(SensitiveCommandConfigScreen, { onBack: handleBackToMenu, inlineMode: true })))),
206
+ inlineView === 'systemprompt' && (React.createElement(Suspense, { fallback: loadingFallback },
207
+ React.createElement(Box, { paddingX: 1 },
208
+ React.createElement(SystemPromptConfigScreen, { onBack: handleBackToMenu })))),
209
+ inlineView === 'customheaders' && (React.createElement(Suspense, { fallback: loadingFallback },
210
+ React.createElement(Box, { paddingX: 1 },
211
+ React.createElement(CustomHeadersScreen, { onBack: handleBackToMenu })))),
212
+ inlineView === 'language-settings' && (React.createElement(Suspense, { fallback: loadingFallback },
213
+ React.createElement(Box, { paddingX: 1 },
214
+ React.createElement(LanguageSettingsScreen, { onBack: handleBackToMenu, inlineMode: true })))),
215
+ inlineView === 'theme-settings' && (React.createElement(Suspense, { fallback: loadingFallback },
216
+ React.createElement(ThemeSettingsScreen, { onBack: handleBackToMenu, inlineMode: true })))));
199
217
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.4.10",
3
+ "version": "0.4.11",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -32,12 +32,14 @@
32
32
  "dev": "tsc --watch",
33
33
  "start": "node dist/cli.js",
34
34
  "prepublishOnly": "npm run build",
35
+ "postinstall": "node scripts/postinstall.cjs",
35
36
  "test": "prettier --check . && xo && ava",
36
37
  "lint": "xo",
37
38
  "format": "prettier --write ."
38
39
  },
39
40
  "files": [
40
- "dist"
41
+ "dist",
42
+ "scripts"
41
43
  ],
42
44
  "dependencies": {
43
45
  "@inkjs/ui": "^2.0.0",
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Post-install script to provide installation optimization tips for users
5
+ */
6
+
7
+ const https = require('https');
8
+ const { execSync } = require('child_process');
9
+
10
+ // ANSI color codes
11
+ const colors = {
12
+ reset: '\x1b[0m',
13
+ bright: '\x1b[1m',
14
+ cyan: '\x1b[36m',
15
+ yellow: '\x1b[33m',
16
+ green: '\x1b[32m',
17
+ };
18
+
19
+ /**
20
+ * Detect if user is in China based on IP geolocation
21
+ */
22
+ function detectRegion() {
23
+ return new Promise((resolve) => {
24
+ const timeout = setTimeout(() => resolve('unknown'), 3000);
25
+
26
+ https.get('https://ipapi.co/json/', (res) => {
27
+ let data = '';
28
+ res.on('data', (chunk) => data += chunk);
29
+ res.on('end', () => {
30
+ clearTimeout(timeout);
31
+ try {
32
+ const info = JSON.parse(data);
33
+ resolve(info.country_code === 'CN' ? 'china' : 'other');
34
+ } catch {
35
+ resolve('unknown');
36
+ }
37
+ });
38
+ }).on('error', () => {
39
+ clearTimeout(timeout);
40
+ resolve('unknown');
41
+ });
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Check current npm registry
47
+ */
48
+ function getCurrentRegistry() {
49
+ try {
50
+ const registry = execSync('npm config get registry', { encoding: 'utf8' }).trim();
51
+ return registry;
52
+ } catch {
53
+ return 'https://registry.npmjs.org';
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Main function
59
+ */
60
+ async function main() {
61
+ // Skip if running in CI environment
62
+ if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) {
63
+ return;
64
+ }
65
+
66
+ const currentRegistry = getCurrentRegistry();
67
+ const isUsingMirror = currentRegistry.includes('npmmirror.com') ||
68
+ currentRegistry.includes('taobao.org');
69
+
70
+ // If already using a mirror, skip the tips
71
+ if (isUsingMirror) {
72
+ return;
73
+ }
74
+
75
+ console.log(`\n${colors.cyan}${colors.bright}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}`);
76
+ console.log(`${colors.cyan}${colors.bright} Snow AI - Installation Optimization Tips${colors.reset}`);
77
+ console.log(`${colors.cyan}${colors.bright}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}\n`);
78
+
79
+ const region = await detectRegion();
80
+
81
+ if (region === 'china') {
82
+ console.log(`${colors.yellow}检测到您在中国大陆地区,建议配置 npm 镜像源以加速安装:${colors.reset}\n`);
83
+ console.log(`${colors.green}# 方案 1: 使用淘宝镜像 (推荐)${colors.reset}`);
84
+ console.log(` npm config set registry https://registry.npmmirror.com\n`);
85
+ console.log(`${colors.green}# 方案 2: 临时使用镜像安装${colors.reset}`);
86
+ console.log(` npm install -g snow-ai --registry=https://registry.npmmirror.com\n`);
87
+ console.log(`${colors.green}# 恢复官方源${colors.reset}`);
88
+ console.log(` npm config set registry https://registry.npmjs.org\n`);
89
+ } else {
90
+ console.log(`${colors.yellow}To speed up npm installation, you can:${colors.reset}\n`);
91
+ console.log(`${colors.green}# Enable parallel downloads${colors.reset}`);
92
+ console.log(` npm config set maxsockets 10\n`);
93
+ console.log(`${colors.green}# Use offline cache when possible${colors.reset}`);
94
+ console.log(` npm config set prefer-offline true\n`);
95
+ console.log(`${colors.green}# Skip unnecessary checks${colors.reset}`);
96
+ console.log(` npm config set audit false\n`);
97
+ console.log(` npm config set fund false\n`);
98
+ }
99
+
100
+ console.log(`${colors.cyan}Current registry: ${currentRegistry}${colors.reset}`);
101
+ console.log(`${colors.cyan}${colors.bright}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}\n`);
102
+ }
103
+
104
+ main().catch(() => {
105
+ // Silently fail - don't break installation
106
+ });