snow-ai 0.4.15 → 0.4.16

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.
@@ -1,10 +1,11 @@
1
- import React, { useCallback, useEffect, useRef, useMemo } from 'react';
1
+ import React, { useCallback, useEffect, useRef, useMemo, lazy, Suspense } from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  import { cpSlice } from '../../utils/textUtils.js';
4
- import CommandPanel from './CommandPanel.js';
5
- import FileList from './FileList.js';
6
- import AgentPickerPanel from './AgentPickerPanel.js';
7
- import TodoPickerPanel from './TodoPickerPanel.js';
4
+ // Lazy load panel components to reduce initial bundle size
5
+ const CommandPanel = lazy(() => import('./CommandPanel.js'));
6
+ const FileList = lazy(() => import('./FileList.js'));
7
+ const AgentPickerPanel = lazy(() => import('./AgentPickerPanel.js'));
8
+ const TodoPickerPanel = lazy(() => import('./TodoPickerPanel.js'));
8
9
  import { useInputBuffer } from '../../hooks/useInputBuffer.js';
9
10
  import { useCommandPanel } from '../../hooks/useCommandPanel.js';
10
11
  import { useFilePicker } from '../../hooks/useFilePicker.js';
@@ -309,11 +310,15 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
309
310
  ? t.chatScreen.contentSearchHint
310
311
  : t.chatScreen.fileSearchHint
311
312
  : ''))) : null,
312
- React.createElement(CommandPanel, { commands: getFilteredCommands(), selectedIndex: commandSelectedIndex, query: buffer.getFullText().slice(1), visible: showCommands, isProcessing: commandPanelIsProcessing }),
313
+ React.createElement(Suspense, { fallback: null },
314
+ React.createElement(CommandPanel, { commands: getFilteredCommands(), selectedIndex: commandSelectedIndex, query: buffer.getFullText().slice(1), visible: showCommands, isProcessing: commandPanelIsProcessing })),
313
315
  React.createElement(Box, null,
314
- React.createElement(FileList, { ref: fileListRef, query: fileQuery, selectedIndex: fileSelectedIndex, visible: showFilePicker, maxItems: 10, rootPath: process.cwd(), onFilteredCountChange: handleFilteredCountChange, searchMode: searchMode })),
315
- React.createElement(AgentPickerPanel, { agents: getFilteredAgents(), selectedIndex: agentSelectedIndex, visible: showAgentPicker, maxHeight: 5 }),
316
- React.createElement(TodoPickerPanel, { todos: todos, selectedIndex: todoSelectedIndex, selectedTodos: selectedTodos, visible: showTodoPicker, maxHeight: 5, isLoading: todoIsLoading, searchQuery: todoSearchQuery, totalCount: totalTodoCount }),
316
+ React.createElement(Suspense, { fallback: null },
317
+ React.createElement(FileList, { ref: fileListRef, query: fileQuery, selectedIndex: fileSelectedIndex, visible: showFilePicker, maxItems: 10, rootPath: process.cwd(), onFilteredCountChange: handleFilteredCountChange, searchMode: searchMode }))),
318
+ React.createElement(Suspense, { fallback: null },
319
+ React.createElement(AgentPickerPanel, { agents: getFilteredAgents(), selectedIndex: agentSelectedIndex, visible: showAgentPicker, maxHeight: 5 })),
320
+ React.createElement(Suspense, { fallback: null },
321
+ React.createElement(TodoPickerPanel, { todos: todos, selectedIndex: todoSelectedIndex, selectedTodos: selectedTodos, visible: showTodoPicker, maxHeight: 5, isLoading: todoIsLoading, searchQuery: todoSearchQuery, totalCount: totalTodoCount })),
317
322
  yoloMode && (React.createElement(Box, { marginTop: 1 },
318
323
  React.createElement(Text, { color: theme.colors.warning, dimColor: true }, t.chatScreen.yoloModeActive))),
319
324
  contextUsage && (React.createElement(Box, { marginTop: 1 },
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useRef } from 'react';
1
+ import React, { useState, useEffect, useRef, lazy, Suspense } from 'react';
2
2
  import { Box, Text, useInput, Static, useStdout } from 'ink';
3
3
  import Spinner from 'ink-spinner';
4
4
  import Gradient from 'ink-gradient';
@@ -7,17 +7,18 @@ import { useI18n } from '../../i18n/I18nContext.js';
7
7
  import { useTheme } from '../contexts/ThemeContext.js';
8
8
  import ChatInput from '../components/ChatInput.js';
9
9
  import PendingMessages from '../components/PendingMessages.js';
10
- import MCPInfoScreen from '../components/MCPInfoScreen.js';
11
- import MCPInfoPanel from '../components/MCPInfoPanel.js';
12
- import SessionListPanel from '../components/SessionListPanel.js';
13
- import UsagePanel from '../components/UsagePanel.js';
14
- import HelpPanel from '../components/HelpPanel.js';
15
10
  import MarkdownRenderer from '../components/MarkdownRenderer.js';
16
11
  import ToolConfirmation from '../components/ToolConfirmation.js';
17
12
  import DiffViewer from '../components/DiffViewer.js';
18
13
  import ToolResultPreview from '../components/ToolResultPreview.js';
19
14
  import FileRollbackConfirmation from '../components/FileRollbackConfirmation.js';
20
15
  import ShimmerText from '../components/ShimmerText.js';
16
+ // Lazy load panel components to reduce initial bundle size
17
+ const MCPInfoScreen = lazy(() => import('../components/MCPInfoScreen.js'));
18
+ const MCPInfoPanel = lazy(() => import('../components/MCPInfoPanel.js'));
19
+ const SessionListPanel = lazy(() => import('../components/SessionListPanel.js'));
20
+ const UsagePanel = lazy(() => import('../components/UsagePanel.js'));
21
+ const HelpPanel = lazy(() => import('../components/HelpPanel.js'));
21
22
  import { getOpenAiConfig } from '../../utils/apiConfig.js';
22
23
  import { sessionManager } from '../../utils/sessionManager.js';
23
24
  import { useSessionSave } from '../../hooks/useSessionSave.js';
@@ -30,7 +31,7 @@ import { useStreamingState } from '../../hooks/useStreamingState.js';
30
31
  import { useCommandHandler } from '../../hooks/useCommandHandler.js';
31
32
  import { useTerminalSize } from '../../hooks/useTerminalSize.js';
32
33
  import { parseAndValidateFileReferences, createMessageWithFileInstructions, } from '../../utils/fileUtils.js';
33
- import { executeCommand } from '../../utils/commandExecutor.js';
34
+ import { vscodeConnection } from '../../utils/vscodeConnection.js';
34
35
  import { convertSessionMessagesToUI } from '../../utils/sessionConverter.js';
35
36
  import { incrementalSnapshotManager } from '../../utils/incrementalSnapshot.js';
36
37
  import { formatElapsedTime } from '../../utils/textUtils.js';
@@ -391,22 +392,33 @@ export default function ChatScreen({ skipWelcome }) {
391
392
  return;
392
393
  }
393
394
  hasAttemptedAutoVscodeConnect.current = true;
394
- (async () => {
395
- try {
396
- const result = await executeCommand('ide');
397
- await handleCommandExecution('ide', result);
398
- }
399
- catch (error) {
400
- console.error('Failed to auto-connect VSCode:', error);
401
- await handleCommandExecution('ide', {
402
- success: false,
403
- message: error instanceof Error
404
- ? error.message
405
- : 'Failed to start VSCode connection',
406
- });
407
- }
408
- })();
409
- }, [commandsLoaded, handleCommandExecution, vscodeState.vscodeConnectionStatus]);
395
+ // Auto-connect IDE in background without blocking UI
396
+ // Use setTimeout to defer execution and make it fully async
397
+ const timer = setTimeout(() => {
398
+ // Fire and forget - don't wait for result
399
+ (async () => {
400
+ try {
401
+ // Clean up any existing connection state first (like manual /ide does)
402
+ if (vscodeConnection.isConnected() || vscodeConnection.isClientRunning()) {
403
+ vscodeConnection.stop();
404
+ vscodeConnection.resetReconnectAttempts();
405
+ await new Promise(resolve => setTimeout(resolve, 100));
406
+ }
407
+ // Set connecting status after cleanup
408
+ vscodeState.setVscodeConnectionStatus('connecting');
409
+ // Now try to connect
410
+ await vscodeConnection.start();
411
+ // If we get here, connection succeeded
412
+ // Status will be updated by useVSCodeState hook monitoring
413
+ }
414
+ catch (error) {
415
+ console.error('Background VSCode auto-connect failed:', error);
416
+ // Let useVSCodeState handle the timeout and error state
417
+ }
418
+ })();
419
+ }, 0);
420
+ return () => clearTimeout(timer);
421
+ }, [commandsLoaded, vscodeState]);
410
422
  // Pending messages are now handled inline during tool execution in useConversation
411
423
  // Auto-send pending messages when streaming completely stops (as fallback)
412
424
  useEffect(() => {
@@ -1123,7 +1135,11 @@ export default function ChatScreen({ skipWelcome }) {
1123
1135
  }
1124
1136
  };
1125
1137
  if (showMcpInfo) {
1126
- return (React.createElement(MCPInfoScreen, { onClose: () => setShowMcpInfo(false), panelKey: mcpPanelKey }));
1138
+ return (React.createElement(Suspense, { fallback: React.createElement(Box, null,
1139
+ React.createElement(Text, null,
1140
+ React.createElement(Spinner, { type: "dots" }),
1141
+ " Loading...")) },
1142
+ React.createElement(MCPInfoScreen, { onClose: () => setShowMcpInfo(false), panelKey: mcpPanelKey })));
1127
1143
  }
1128
1144
  // Show warning if terminal is too small
1129
1145
  if (terminalHeight < MIN_TERMINAL_HEIGHT) {
@@ -1416,17 +1432,33 @@ export default function ChatScreen({ skipWelcome }) {
1416
1432
  ? pendingToolConfirmation.tool.function.arguments
1417
1433
  : undefined, allTools: pendingToolConfirmation.allTools, onConfirm: pendingToolConfirmation.resolve })),
1418
1434
  showSessionPanel && (React.createElement(Box, { paddingX: 1, width: terminalWidth },
1419
- React.createElement(SessionListPanel, { onSelectSession: handleSessionPanelSelect, onClose: () => setShowSessionPanel(false) }))),
1435
+ React.createElement(Suspense, { fallback: React.createElement(Box, null,
1436
+ React.createElement(Text, null,
1437
+ React.createElement(Spinner, { type: "dots" }),
1438
+ " Loading...")) },
1439
+ React.createElement(SessionListPanel, { onSelectSession: handleSessionPanelSelect, onClose: () => setShowSessionPanel(false) })))),
1420
1440
  showMcpPanel && (React.createElement(Box, { paddingX: 1, flexDirection: "column", width: terminalWidth },
1421
- React.createElement(MCPInfoPanel, null),
1441
+ React.createElement(Suspense, { fallback: React.createElement(Box, null,
1442
+ React.createElement(Text, null,
1443
+ React.createElement(Spinner, { type: "dots" }),
1444
+ " Loading...")) },
1445
+ React.createElement(MCPInfoPanel, null)),
1422
1446
  React.createElement(Box, { marginTop: 1 },
1423
1447
  React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, t.chatScreen.pressEscToClose)))),
1424
1448
  showUsagePanel && (React.createElement(Box, { paddingX: 1, flexDirection: "column", width: terminalWidth },
1425
- React.createElement(UsagePanel, null),
1449
+ React.createElement(Suspense, { fallback: React.createElement(Box, null,
1450
+ React.createElement(Text, null,
1451
+ React.createElement(Spinner, { type: "dots" }),
1452
+ " Loading...")) },
1453
+ React.createElement(UsagePanel, null)),
1426
1454
  React.createElement(Box, { marginTop: 1 },
1427
1455
  React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, t.chatScreen.pressEscToClose)))),
1428
1456
  showHelpPanel && (React.createElement(Box, { paddingX: 1, flexDirection: "column", width: terminalWidth },
1429
- React.createElement(HelpPanel, null))),
1457
+ React.createElement(Suspense, { fallback: React.createElement(Box, null,
1458
+ React.createElement(Text, null,
1459
+ React.createElement(Spinner, { type: "dots" }),
1460
+ " Loading...")) },
1461
+ React.createElement(HelpPanel, null)))),
1430
1462
  snapshotState.pendingRollback && (React.createElement(FileRollbackConfirmation, { fileCount: snapshotState.pendingRollback.fileCount, filePaths: snapshotState.pendingRollback.filePaths || [], onConfirm: handleRollbackConfirm })),
1431
1463
  !pendingToolConfirmation &&
1432
1464
  !isCompressing &&
@@ -1444,29 +1476,21 @@ export default function ChatScreen({ skipWelcome }) {
1444
1476
  cachedTokens: streamingState.contextUsage.cached_tokens,
1445
1477
  }
1446
1478
  : undefined, initialContent: restoreInputContent, onContextPercentageChange: setCurrentContextPercentage }),
1447
- vscodeState.vscodeConnectionStatus !== 'disconnected' && (React.createElement(Box, { marginTop: 1, paddingX: 1 },
1479
+ (vscodeState.vscodeConnectionStatus === 'connecting' ||
1480
+ vscodeState.vscodeConnectionStatus === 'connected') && (React.createElement(Box, { marginTop: 1, paddingX: 1 },
1448
1481
  React.createElement(Text, { color: vscodeState.vscodeConnectionStatus === 'connecting'
1449
1482
  ? 'yellow'
1450
- : vscodeState.vscodeConnectionStatus === 'connected'
1451
- ? 'green'
1452
- : vscodeState.vscodeConnectionStatus === 'error'
1453
- ? 'red'
1454
- : theme.colors.menuSecondary, dimColor: vscodeState.vscodeConnectionStatus !== 'error' },
1483
+ : 'green', dimColor: true }, vscodeState.vscodeConnectionStatus === 'connecting' ? (React.createElement(React.Fragment, null,
1484
+ React.createElement(Spinner, { type: "dots" }),
1485
+ " ",
1486
+ t.chatScreen.ideConnecting)) : (React.createElement(React.Fragment, null,
1455
1487
  "\u25CF",
1456
1488
  ' ',
1457
- vscodeState.vscodeConnectionStatus === 'connecting'
1458
- ? t.chatScreen.ideConnecting
1459
- : vscodeState.vscodeConnectionStatus === 'connected'
1460
- ? t.chatScreen.ideConnected
1461
- : vscodeState.vscodeConnectionStatus === 'error'
1462
- ? t.chatScreen.ideError
1463
- : 'IDE',
1464
- vscodeState.vscodeConnectionStatus === 'connected' &&
1465
- vscodeState.editorContext.activeFile &&
1489
+ t.chatScreen.ideConnected,
1490
+ vscodeState.editorContext.activeFile &&
1466
1491
  t.chatScreen.ideActiveFile.replace('{file}', vscodeState.editorContext.activeFile),
1467
- vscodeState.vscodeConnectionStatus === 'connected' &&
1468
- vscodeState.editorContext.selectedText &&
1469
- t.chatScreen.ideSelectedText.replace('{count}', vscodeState.editorContext.selectedText.length.toString())))),
1492
+ vscodeState.editorContext.selectedText &&
1493
+ t.chatScreen.ideSelectedText.replace('{count}', vscodeState.editorContext.selectedText.length.toString())))))),
1470
1494
  codebaseIndexing && codebaseProgress && (React.createElement(Box, { marginTop: 1, paddingX: 1 },
1471
1495
  React.createElement(Text, { color: "cyan", dimColor: true },
1472
1496
  React.createElement(Spinner, { type: "dots" }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.4.15",
3
+ "version": "0.4.16",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {