snow-ai 0.4.14 → 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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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(
|
|
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(
|
|
315
|
-
|
|
316
|
-
React.createElement(
|
|
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 {
|
|
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';
|
|
@@ -94,6 +95,8 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
94
95
|
useEffect(() => {
|
|
95
96
|
pendingMessagesRef.current = pendingMessages;
|
|
96
97
|
}, [pendingMessages]);
|
|
98
|
+
// Track if commands are loaded
|
|
99
|
+
const [commandsLoaded, setCommandsLoaded] = useState(false);
|
|
97
100
|
// Load commands dynamically to avoid blocking initial render
|
|
98
101
|
useEffect(() => {
|
|
99
102
|
// Use Promise.all to load all commands in parallel
|
|
@@ -113,8 +116,14 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
113
116
|
import('../../utils/commands/agent.js'),
|
|
114
117
|
import('../../utils/commands/todoPicker.js'),
|
|
115
118
|
import('../../utils/commands/help.js'),
|
|
116
|
-
])
|
|
119
|
+
])
|
|
120
|
+
.then(() => {
|
|
121
|
+
setCommandsLoaded(true);
|
|
122
|
+
})
|
|
123
|
+
.catch(error => {
|
|
117
124
|
console.error('Failed to load commands:', error);
|
|
125
|
+
// Still mark as loaded to allow app to continue
|
|
126
|
+
setCommandsLoaded(true);
|
|
118
127
|
});
|
|
119
128
|
}, []);
|
|
120
129
|
// Auto-start codebase indexing on mount if enabled
|
|
@@ -371,6 +380,10 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
371
380
|
processMessage: (message, images, useBasicModel, hideUserMessage) => processMessageRef.current?.(message, images, useBasicModel, hideUserMessage) || Promise.resolve(),
|
|
372
381
|
});
|
|
373
382
|
useEffect(() => {
|
|
383
|
+
// Wait for commands to be loaded before attempting auto-connect
|
|
384
|
+
if (!commandsLoaded) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
374
387
|
if (hasAttemptedAutoVscodeConnect.current) {
|
|
375
388
|
return;
|
|
376
389
|
}
|
|
@@ -379,22 +392,33 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
379
392
|
return;
|
|
380
393
|
}
|
|
381
394
|
hasAttemptedAutoVscodeConnect.current = true;
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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]);
|
|
398
422
|
// Pending messages are now handled inline during tool execution in useConversation
|
|
399
423
|
// Auto-send pending messages when streaming completely stops (as fallback)
|
|
400
424
|
useEffect(() => {
|
|
@@ -1111,7 +1135,11 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
1111
1135
|
}
|
|
1112
1136
|
};
|
|
1113
1137
|
if (showMcpInfo) {
|
|
1114
|
-
return (React.createElement(
|
|
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 })));
|
|
1115
1143
|
}
|
|
1116
1144
|
// Show warning if terminal is too small
|
|
1117
1145
|
if (terminalHeight < MIN_TERMINAL_HEIGHT) {
|
|
@@ -1404,17 +1432,33 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
1404
1432
|
? pendingToolConfirmation.tool.function.arguments
|
|
1405
1433
|
: undefined, allTools: pendingToolConfirmation.allTools, onConfirm: pendingToolConfirmation.resolve })),
|
|
1406
1434
|
showSessionPanel && (React.createElement(Box, { paddingX: 1, width: terminalWidth },
|
|
1407
|
-
React.createElement(
|
|
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) })))),
|
|
1408
1440
|
showMcpPanel && (React.createElement(Box, { paddingX: 1, flexDirection: "column", width: terminalWidth },
|
|
1409
|
-
React.createElement(
|
|
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)),
|
|
1410
1446
|
React.createElement(Box, { marginTop: 1 },
|
|
1411
1447
|
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, t.chatScreen.pressEscToClose)))),
|
|
1412
1448
|
showUsagePanel && (React.createElement(Box, { paddingX: 1, flexDirection: "column", width: terminalWidth },
|
|
1413
|
-
React.createElement(
|
|
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)),
|
|
1414
1454
|
React.createElement(Box, { marginTop: 1 },
|
|
1415
1455
|
React.createElement(Text, { color: theme.colors.menuSecondary, dimColor: true }, t.chatScreen.pressEscToClose)))),
|
|
1416
1456
|
showHelpPanel && (React.createElement(Box, { paddingX: 1, flexDirection: "column", width: terminalWidth },
|
|
1417
|
-
React.createElement(
|
|
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)))),
|
|
1418
1462
|
snapshotState.pendingRollback && (React.createElement(FileRollbackConfirmation, { fileCount: snapshotState.pendingRollback.fileCount, filePaths: snapshotState.pendingRollback.filePaths || [], onConfirm: handleRollbackConfirm })),
|
|
1419
1463
|
!pendingToolConfirmation &&
|
|
1420
1464
|
!isCompressing &&
|
|
@@ -1432,29 +1476,21 @@ export default function ChatScreen({ skipWelcome }) {
|
|
|
1432
1476
|
cachedTokens: streamingState.contextUsage.cached_tokens,
|
|
1433
1477
|
}
|
|
1434
1478
|
: undefined, initialContent: restoreInputContent, onContextPercentageChange: setCurrentContextPercentage }),
|
|
1435
|
-
vscodeState.vscodeConnectionStatus
|
|
1479
|
+
(vscodeState.vscodeConnectionStatus === 'connecting' ||
|
|
1480
|
+
vscodeState.vscodeConnectionStatus === 'connected') && (React.createElement(Box, { marginTop: 1, paddingX: 1 },
|
|
1436
1481
|
React.createElement(Text, { color: vscodeState.vscodeConnectionStatus === 'connecting'
|
|
1437
1482
|
? 'yellow'
|
|
1438
|
-
: vscodeState.vscodeConnectionStatus === '
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
: 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,
|
|
1443
1487
|
"\u25CF",
|
|
1444
1488
|
' ',
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
: vscodeState.vscodeConnectionStatus === 'connected'
|
|
1448
|
-
? t.chatScreen.ideConnected
|
|
1449
|
-
: vscodeState.vscodeConnectionStatus === 'error'
|
|
1450
|
-
? t.chatScreen.ideError
|
|
1451
|
-
: 'IDE',
|
|
1452
|
-
vscodeState.vscodeConnectionStatus === 'connected' &&
|
|
1453
|
-
vscodeState.editorContext.activeFile &&
|
|
1489
|
+
t.chatScreen.ideConnected,
|
|
1490
|
+
vscodeState.editorContext.activeFile &&
|
|
1454
1491
|
t.chatScreen.ideActiveFile.replace('{file}', vscodeState.editorContext.activeFile),
|
|
1455
|
-
vscodeState.
|
|
1456
|
-
vscodeState.editorContext.selectedText
|
|
1457
|
-
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())))))),
|
|
1458
1494
|
codebaseIndexing && codebaseProgress && (React.createElement(Box, { marginTop: 1, paddingX: 1 },
|
|
1459
1495
|
React.createElement(Text, { color: "cyan", dimColor: true },
|
|
1460
1496
|
React.createElement(Spinner, { type: "dots" }),
|