wave-code 0.4.0 → 0.6.1

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.
Files changed (105) hide show
  1. package/dist/commands/plugin/uninstall.js +1 -1
  2. package/dist/components/App.d.ts.map +1 -1
  3. package/dist/components/App.js +38 -2
  4. package/dist/components/BackgroundTaskManager.d.ts +6 -0
  5. package/dist/components/BackgroundTaskManager.d.ts.map +1 -0
  6. package/dist/components/BackgroundTaskManager.js +114 -0
  7. package/dist/components/ChatInterface.d.ts.map +1 -1
  8. package/dist/components/ChatInterface.js +39 -5
  9. package/dist/components/CommandSelector.d.ts.map +1 -1
  10. package/dist/components/CommandSelector.js +13 -5
  11. package/dist/components/CompressDisplay.d.ts.map +1 -1
  12. package/dist/components/CompressDisplay.js +6 -10
  13. package/dist/components/ConfirmationDetails.d.ts +9 -0
  14. package/dist/components/ConfirmationDetails.d.ts.map +1 -0
  15. package/dist/components/ConfirmationDetails.js +53 -0
  16. package/dist/components/{Confirmation.d.ts → ConfirmationSelector.d.ts} +3 -3
  17. package/dist/components/ConfirmationSelector.d.ts.map +1 -0
  18. package/dist/components/{Confirmation.js → ConfirmationSelector.js} +92 -101
  19. package/dist/components/DiffDisplay.d.ts +0 -1
  20. package/dist/components/DiffDisplay.d.ts.map +1 -1
  21. package/dist/components/DiffDisplay.js +82 -60
  22. package/dist/components/FileSelector.d.ts.map +1 -1
  23. package/dist/components/FileSelector.js +2 -2
  24. package/dist/components/HistorySearch.d.ts.map +1 -1
  25. package/dist/components/HistorySearch.js +12 -4
  26. package/dist/components/InputBox.d.ts +1 -3
  27. package/dist/components/InputBox.d.ts.map +1 -1
  28. package/dist/components/InputBox.js +9 -18
  29. package/dist/components/LoadingIndicator.d.ts +11 -0
  30. package/dist/components/LoadingIndicator.d.ts.map +1 -0
  31. package/dist/components/LoadingIndicator.js +6 -0
  32. package/dist/components/Markdown.d.ts.map +1 -1
  33. package/dist/components/Markdown.js +114 -120
  34. package/dist/components/MessageItem.d.ts.map +1 -1
  35. package/dist/components/MessageItem.js +1 -2
  36. package/dist/components/MessageList.d.ts +2 -3
  37. package/dist/components/MessageList.d.ts.map +1 -1
  38. package/dist/components/MessageList.js +7 -7
  39. package/dist/components/PlanDisplay.d.ts.map +1 -1
  40. package/dist/components/PlanDisplay.js +4 -12
  41. package/dist/components/PluginDetail.js +1 -1
  42. package/dist/components/RewindCommand.d.ts +4 -0
  43. package/dist/components/RewindCommand.d.ts.map +1 -1
  44. package/dist/components/RewindCommand.js +19 -2
  45. package/dist/components/SubagentBlock.d.ts.map +1 -1
  46. package/dist/components/SubagentBlock.js +12 -5
  47. package/dist/components/TaskList.d.ts +3 -0
  48. package/dist/components/TaskList.d.ts.map +1 -0
  49. package/dist/components/TaskList.js +49 -0
  50. package/dist/components/ToolResultDisplay.d.ts.map +1 -1
  51. package/dist/components/ToolResultDisplay.js +2 -1
  52. package/dist/contexts/useChat.d.ts +15 -6
  53. package/dist/contexts/useChat.d.ts.map +1 -1
  54. package/dist/contexts/useChat.js +52 -43
  55. package/dist/hooks/useInputManager.d.ts +2 -13
  56. package/dist/hooks/useInputManager.d.ts.map +1 -1
  57. package/dist/hooks/useInputManager.js +8 -57
  58. package/dist/hooks/usePluginManager.d.ts.map +1 -1
  59. package/dist/hooks/usePluginManager.js +8 -4
  60. package/dist/hooks/useTasks.d.ts +2 -0
  61. package/dist/hooks/useTasks.d.ts.map +1 -0
  62. package/dist/hooks/useTasks.js +5 -0
  63. package/dist/managers/InputManager.d.ts +5 -28
  64. package/dist/managers/InputManager.d.ts.map +1 -1
  65. package/dist/managers/InputManager.js +26 -127
  66. package/package.json +9 -10
  67. package/src/commands/plugin/uninstall.ts +1 -1
  68. package/src/components/App.tsx +50 -3
  69. package/src/components/{BashShellManager.tsx → BackgroundTaskManager.tsx} +79 -73
  70. package/src/components/ChatInterface.tsx +79 -23
  71. package/src/components/CommandSelector.tsx +38 -20
  72. package/src/components/CompressDisplay.tsx +5 -22
  73. package/src/components/ConfirmationDetails.tsx +108 -0
  74. package/src/components/{Confirmation.tsx → ConfirmationSelector.tsx} +162 -187
  75. package/src/components/DiffDisplay.tsx +122 -107
  76. package/src/components/FileSelector.tsx +0 -2
  77. package/src/components/HistorySearch.tsx +45 -21
  78. package/src/components/InputBox.tsx +14 -34
  79. package/src/components/LoadingIndicator.tsx +56 -0
  80. package/src/components/Markdown.tsx +126 -318
  81. package/src/components/MessageItem.tsx +1 -3
  82. package/src/components/MessageList.tsx +10 -67
  83. package/src/components/PlanDisplay.tsx +5 -33
  84. package/src/components/PluginDetail.tsx +1 -1
  85. package/src/components/RewindCommand.tsx +38 -1
  86. package/src/components/SubagentBlock.tsx +28 -14
  87. package/src/components/TaskList.tsx +70 -0
  88. package/src/components/ToolResultDisplay.tsx +6 -2
  89. package/src/contexts/useChat.tsx +82 -60
  90. package/src/hooks/useInputManager.ts +9 -73
  91. package/src/hooks/usePluginManager.ts +10 -4
  92. package/src/hooks/useTasks.ts +6 -0
  93. package/src/managers/InputManager.ts +30 -157
  94. package/dist/components/BashShellManager.d.ts +0 -6
  95. package/dist/components/BashShellManager.d.ts.map +0 -1
  96. package/dist/components/BashShellManager.js +0 -116
  97. package/dist/components/Confirmation.d.ts.map +0 -1
  98. package/dist/components/MemoryDisplay.d.ts +0 -8
  99. package/dist/components/MemoryDisplay.d.ts.map +0 -1
  100. package/dist/components/MemoryDisplay.js +0 -25
  101. package/dist/components/MemoryTypeSelector.d.ts +0 -8
  102. package/dist/components/MemoryTypeSelector.d.ts.map +0 -1
  103. package/dist/components/MemoryTypeSelector.js +0 -38
  104. package/src/components/MemoryDisplay.tsx +0 -62
  105. package/src/components/MemoryTypeSelector.tsx +0 -98
@@ -1,5 +1,5 @@
1
1
  import React, { useMemo } from "react";
2
- import { Box, Text, useStdout } from "ink";
2
+ import { Box, Text } from "ink";
3
3
  import {
4
4
  WRITE_TOOL_NAME,
5
5
  EDIT_TOOL_NAME,
@@ -11,19 +11,12 @@ import { diffLines, diffWords } from "diff";
11
11
  interface DiffDisplayProps {
12
12
  toolName?: string;
13
13
  parameters?: string;
14
- isExpanded?: boolean;
15
14
  }
16
15
 
17
16
  export const DiffDisplay: React.FC<DiffDisplayProps> = ({
18
17
  toolName,
19
18
  parameters,
20
- isExpanded = false,
21
19
  }) => {
22
- const { stdout } = useStdout();
23
- const maxHeight = useMemo(() => {
24
- return Math.max(5, (stdout?.rows || 24) - 20);
25
- }, [stdout?.rows]);
26
-
27
20
  const showDiff =
28
21
  toolName &&
29
22
  [WRITE_TOOL_NAME, EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME].includes(toolName);
@@ -107,7 +100,7 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
107
100
  }
108
101
  };
109
102
 
110
- // Render expanded diff display using word-level diff for all changes
103
+ // Render expanded diff display
111
104
  const renderExpandedDiff = () => {
112
105
  try {
113
106
  if (changes.length === 0) return null;
@@ -122,9 +115,8 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
122
115
  change.newContent || "",
123
116
  );
124
117
 
118
+ // Process line diffs
125
119
  const diffElements: React.ReactNode[] = [];
126
-
127
- // Process line diffs and apply word-level diff to changed lines
128
120
  lineDiffs.forEach((part, partIndex) => {
129
121
  if (part.added) {
130
122
  const lines = part.value
@@ -161,7 +153,60 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
161
153
  const lines = part.value
162
154
  .split("\n")
163
155
  .filter((line) => line !== "");
164
- lines.forEach((line, lineIndex) => {
156
+
157
+ const isFirstBlock = partIndex === 0;
158
+ const isLastBlock = partIndex === lineDiffs.length - 1;
159
+
160
+ let linesToDisplay = lines;
161
+ let showEllipsisTop = false;
162
+ let showEllipsisBottom = false;
163
+
164
+ if (isFirstBlock && !isLastBlock) {
165
+ // First block: keep last 3
166
+ if (lines.length > 3) {
167
+ linesToDisplay = lines.slice(-3);
168
+ showEllipsisTop = true;
169
+ }
170
+ } else if (isLastBlock && !isFirstBlock) {
171
+ // Last block: keep first 3
172
+ if (lines.length > 3) {
173
+ linesToDisplay = lines.slice(0, 3);
174
+ showEllipsisBottom = true;
175
+ }
176
+ } else if (!isFirstBlock && !isLastBlock) {
177
+ // Middle block: keep first 3 and last 3
178
+ if (lines.length > 6) {
179
+ linesToDisplay = [...lines.slice(0, 3), ...lines.slice(-3)];
180
+ showEllipsisTop = false; // We'll put ellipsis in the middle
181
+ }
182
+ } else if (isFirstBlock && isLastBlock) {
183
+ // Only one block (no changes?) - keep all or apply a general limit
184
+ // For now, let's keep all if it's the only block
185
+ }
186
+
187
+ if (showEllipsisTop) {
188
+ diffElements.push(
189
+ <Box key={`ellipsis-top-${changeIndex}-${partIndex}`}>
190
+ <Text color="gray"> ...</Text>
191
+ </Box>,
192
+ );
193
+ }
194
+
195
+ linesToDisplay.forEach((line, lineIndex) => {
196
+ // If it's a middle block and we are at the split point
197
+ if (
198
+ !isFirstBlock &&
199
+ !isLastBlock &&
200
+ lines.length > 6 &&
201
+ lineIndex === 3
202
+ ) {
203
+ diffElements.push(
204
+ <Box key={`ellipsis-mid-${changeIndex}-${partIndex}`}>
205
+ <Text color="gray"> ...</Text>
206
+ </Box>,
207
+ );
208
+ }
209
+
165
210
  diffElements.push(
166
211
  <Box
167
212
  key={`context-${changeIndex}-${partIndex}-${lineIndex}`}
@@ -172,73 +217,57 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
172
217
  </Box>,
173
218
  );
174
219
  });
220
+
221
+ if (showEllipsisBottom) {
222
+ diffElements.push(
223
+ <Box key={`ellipsis-bottom-${changeIndex}-${partIndex}`}>
224
+ <Text color="gray"> ...</Text>
225
+ </Box>,
226
+ );
227
+ }
175
228
  }
176
229
  });
177
230
 
178
- // Now look for pairs of removed/added lines that can be word-diffed
179
- let i = 0;
180
-
181
- while (i < diffElements.length) {
182
- const current = diffElements[i];
183
- const next =
184
- i + 1 < diffElements.length ? diffElements[i + 1] : null;
231
+ // If it's a single line change (one removed, one added), use word-level diff
232
+ if (
233
+ diffElements.length === 2 &&
234
+ React.isValidElement(diffElements[0]) &&
235
+ React.isValidElement(diffElements[1]) &&
236
+ typeof diffElements[0].key === "string" &&
237
+ diffElements[0].key.includes("remove-") &&
238
+ typeof diffElements[1].key === "string" &&
239
+ diffElements[1].key.includes("add-")
240
+ ) {
241
+ const removedText = extractTextFromElement(diffElements[0]);
242
+ const addedText = extractTextFromElement(diffElements[1]);
185
243
 
186
- // Check if we have a removed line followed by an added line
187
- const currentKey = React.isValidElement(current) ? current.key : "";
188
- const nextKey = React.isValidElement(next) ? next.key : "";
189
-
190
- const isCurrentRemoved =
191
- typeof currentKey === "string" && currentKey.includes("remove-");
192
- const isNextAdded =
193
- typeof nextKey === "string" && nextKey.includes("add-");
194
-
195
- if (
196
- isCurrentRemoved &&
197
- isNextAdded &&
198
- React.isValidElement(current) &&
199
- React.isValidElement(next)
200
- ) {
201
- // Extract the text content from the removed and added lines
202
- const removedText = extractTextFromElement(current);
203
- const addedText = extractTextFromElement(next);
204
-
205
- if (removedText && addedText) {
206
- // Apply word-level diff
207
- const { removedParts, addedParts } = renderWordLevelDiff(
208
- removedText,
209
- addedText,
210
- `word-${changeIndex}-${i}`,
211
- );
212
-
213
- allElements.push(
214
- <Box
215
- key={`word-diff-removed-${changeIndex}-${i}`}
216
- flexDirection="row"
217
- >
218
- <Text color="red">-</Text>
219
- {removedParts}
220
- </Box>,
221
- );
222
- allElements.push(
223
- <Box
224
- key={`word-diff-added-${changeIndex}-${i}`}
225
- flexDirection="row"
226
- >
227
- <Text color="green">+</Text>
228
- {addedParts}
229
- </Box>,
230
- );
244
+ if (removedText && addedText) {
245
+ const { removedParts, addedParts } = renderWordLevelDiff(
246
+ removedText,
247
+ addedText,
248
+ `word-${changeIndex}`,
249
+ );
231
250
 
232
- i += 2; // Skip the next element since we processed it
233
- } else {
234
- // Fallback to original elements
235
- allElements.push(current);
236
- i += 1;
237
- }
251
+ allElements.push(
252
+ <Box
253
+ key={`word-diff-removed-${changeIndex}`}
254
+ flexDirection="row"
255
+ >
256
+ <Text color="red">-</Text>
257
+ {removedParts}
258
+ </Box>,
259
+ );
260
+ allElements.push(
261
+ <Box key={`word-diff-added-${changeIndex}`} flexDirection="row">
262
+ <Text color="green">+</Text>
263
+ {addedParts}
264
+ </Box>,
265
+ );
238
266
  } else {
239
- allElements.push(current);
240
- i += 1;
267
+ allElements.push(...diffElements);
241
268
  }
269
+ } else {
270
+ allElements.push(...diffElements);
242
271
  }
243
272
  } catch (error) {
244
273
  console.warn(
@@ -255,21 +284,7 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
255
284
  }
256
285
  });
257
286
 
258
- const isTruncated = !isExpanded && allElements.length > maxHeight;
259
- const displayElements = isTruncated
260
- ? allElements.slice(0, maxHeight - 1)
261
- : allElements;
262
-
263
- return (
264
- <Box flexDirection="column">
265
- {displayElements}
266
- {isTruncated && (
267
- <Text color="yellow" dimColor>
268
- ... (truncated {allElements.length - (maxHeight - 1)} more lines)
269
- </Text>
270
- )}
271
- </Box>
272
- );
287
+ return <Box flexDirection="column">{allElements}</Box>;
273
288
  } catch (error) {
274
289
  console.warn("Error rendering expanded diff:", error);
275
290
  return (
@@ -280,26 +295,6 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
280
295
  }
281
296
  };
282
297
 
283
- // Helper function to extract text content from a React element
284
- const extractTextFromElement = (element: React.ReactNode): string | null => {
285
- if (!React.isValidElement(element)) return null;
286
-
287
- // Navigate through Box -> Text structure
288
- const children = (
289
- element.props as unknown as { children?: React.ReactNode[] }
290
- ).children;
291
- if (Array.isArray(children) && children.length >= 2) {
292
- const textElement = children[1]; // Second child should be the Text with content
293
- if (
294
- React.isValidElement(textElement) &&
295
- (textElement.props as unknown as { children?: string }).children
296
- ) {
297
- return (textElement.props as unknown as { children: string }).children;
298
- }
299
- }
300
- return null;
301
- };
302
-
303
298
  // Don't render anything if no diff should be shown
304
299
  if (!showDiff) {
305
300
  return null;
@@ -316,3 +311,23 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
316
311
  </Box>
317
312
  );
318
313
  };
314
+
315
+ // Helper function to extract text content from a React element
316
+ const extractTextFromElement = (element: React.ReactNode): string | null => {
317
+ if (!React.isValidElement(element)) return null;
318
+
319
+ // Navigate through Box -> Text structure
320
+ const children = (
321
+ element.props as unknown as { children?: React.ReactNode[] }
322
+ ).children;
323
+ if (Array.isArray(children) && children.length >= 2) {
324
+ const textElement = children[1]; // Second child should be the Text with content
325
+ if (
326
+ React.isValidElement(textElement) &&
327
+ (textElement.props as unknown as { children?: string }).children
328
+ ) {
329
+ return (textElement.props as unknown as { children: string }).children;
330
+ }
331
+ }
332
+ return null;
333
+ };
@@ -52,7 +52,6 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
52
52
  borderBottom={false}
53
53
  borderLeft={false}
54
54
  borderRight={false}
55
- paddingTop={1}
56
55
  >
57
56
  <Text color="yellow">📁 No files found for "{searchQuery}"</Text>
58
57
  <Text dimColor>Press Escape to cancel</Text>
@@ -91,7 +90,6 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
91
90
  borderBottom={false}
92
91
  borderLeft={false}
93
92
  borderRight={false}
94
- paddingTop={1}
95
93
  >
96
94
  <Text color="cyan" bold>
97
95
  📁 Select File/Directory{" "}
@@ -13,6 +13,7 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
13
13
  onSelect,
14
14
  onCancel,
15
15
  }) => {
16
+ const MAX_VISIBLE_ITEMS = 5;
16
17
  const [selectedIndex, setSelectedIndex] = useState(0);
17
18
  const [entries, setEntries] = useState<PromptEntry[]>([]);
18
19
 
@@ -30,8 +31,8 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
30
31
  useEffect(() => {
31
32
  const fetchHistory = async () => {
32
33
  const results = await PromptHistoryManager.searchHistory(searchQuery);
33
- const limitedResults = results.slice(0, 10);
34
- setEntries(limitedResults); // Limit to 10 results
34
+ const limitedResults = results.slice(0, 20);
35
+ setEntries(limitedResults); // Limit to 20 results
35
36
  setSelectedIndex(0);
36
37
  };
37
38
  fetchHistory();
@@ -75,7 +76,6 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
75
76
  borderBottom={false}
76
77
  borderLeft={false}
77
78
  borderRight={false}
78
- paddingTop={1}
79
79
  >
80
80
  <Text color="yellow">
81
81
  No history found {searchQuery && `for "${searchQuery}"`}
@@ -102,6 +102,19 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
102
102
  }
103
103
  };
104
104
 
105
+ // Calculate visible window
106
+ const startIndex = Math.max(
107
+ 0,
108
+ Math.min(
109
+ selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2),
110
+ Math.max(0, entries.length - MAX_VISIBLE_ITEMS),
111
+ ),
112
+ );
113
+ const visibleEntries = entries.slice(
114
+ startIndex,
115
+ startIndex + MAX_VISIBLE_ITEMS,
116
+ );
117
+
105
118
  return (
106
119
  <Box
107
120
  flexDirection="column"
@@ -110,7 +123,6 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
110
123
  borderBottom={false}
111
124
  borderLeft={false}
112
125
  borderRight={false}
113
- paddingTop={1}
114
126
  gap={1}
115
127
  >
116
128
  <Box>
@@ -119,24 +131,36 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
119
131
  </Text>
120
132
  </Box>
121
133
 
122
- {entries.map((entry, index) => (
123
- <Box key={index} flexDirection="column">
124
- <Text
125
- color={index === selectedIndex ? "black" : "white"}
126
- backgroundColor={index === selectedIndex ? "blue" : undefined}
127
- wrap="truncate-end"
128
- >
129
- {entry.prompt.replace(/\n/g, " ")}
130
- </Text>
131
- {index === selectedIndex && (
132
- <Box marginLeft={4}>
133
- <Text color="gray" dimColor>
134
- {formatTimestamp(entry.timestamp)}
135
- </Text>
134
+ <Box flexDirection="column">
135
+ {visibleEntries.map((entry, index) => {
136
+ const actualIndex = startIndex + index;
137
+ const isSelected = actualIndex === selectedIndex;
138
+ return (
139
+ <Box
140
+ key={actualIndex}
141
+ flexDirection="row"
142
+ justifyContent="space-between"
143
+ >
144
+ <Box flexShrink={1}>
145
+ <Text
146
+ color={isSelected ? "black" : "white"}
147
+ backgroundColor={isSelected ? "blue" : undefined}
148
+ wrap="truncate-end"
149
+ >
150
+ {entry.prompt.replace(/\n/g, " ")}
151
+ </Text>
152
+ </Box>
153
+ {isSelected && (
154
+ <Box marginLeft={2} flexShrink={0}>
155
+ <Text color="gray" dimColor>
156
+ {formatTimestamp(entry.timestamp)}
157
+ </Text>
158
+ </Box>
159
+ )}
136
160
  </Box>
137
- )}
138
- </Box>
139
- ))}
161
+ );
162
+ })}
163
+ </Box>
140
164
 
141
165
  <Box>
142
166
  <Text dimColor>
@@ -4,8 +4,7 @@ import { useInput } from "ink";
4
4
  import { FileSelector } from "./FileSelector.js";
5
5
  import { CommandSelector } from "./CommandSelector.js";
6
6
  import { HistorySearch } from "./HistorySearch.js";
7
- import { MemoryTypeSelector } from "./MemoryTypeSelector.js";
8
- import { BashShellManager } from "./BashShellManager.js";
7
+ import { BackgroundTaskManager } from "./BackgroundTaskManager.js";
9
8
  import { McpManager } from "./McpManager.js";
10
9
  import { RewindCommand } from "./RewindCommand.js";
11
10
  import { useInputManager } from "../hooks/useInputManager.js";
@@ -14,7 +13,7 @@ import { useChat } from "../contexts/useChat.js";
14
13
  import type { McpServerStatus, SlashCommand } from "wave-agent-sdk";
15
14
 
16
15
  export const INPUT_PLACEHOLDER_TEXT =
17
- "Type your message (use @ to reference files, / for commands, # to add memory, Ctrl+R to search history)...";
16
+ "Type your message (use @ to reference files, / for commands, Ctrl+R to search history, Ctrl+O to expand messages, Ctrl+T to toggle tasks)...";
18
17
 
19
18
  export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(
20
19
  0,
@@ -25,13 +24,11 @@ export interface InputBoxProps {
25
24
  isLoading?: boolean;
26
25
  isCommandRunning?: boolean;
27
26
  workdir?: string;
28
- userInputHistory?: string[];
29
27
  sendMessage?: (
30
28
  message: string,
31
29
  images?: Array<{ path: string; mimeType: string }>,
32
30
  ) => void;
33
31
  abortMessage?: () => void;
34
- saveMemory?: (message: string, type: "project" | "user") => Promise<void>;
35
32
  // MCP related properties
36
33
  mcpServers?: McpServerStatus[];
37
34
  connectMcpServer?: (serverName: string) => Promise<boolean>;
@@ -44,10 +41,8 @@ export interface InputBoxProps {
44
41
  export const InputBox: React.FC<InputBoxProps> = ({
45
42
  isLoading = false,
46
43
  isCommandRunning = false,
47
- userInputHistory = [],
48
44
  sendMessage = () => {},
49
45
  abortMessage = () => {},
50
- saveMemory = async () => {},
51
46
  mcpServers = [],
52
47
  connectMcpServer = async () => false,
53
48
  disconnectMcpServer = async () => false,
@@ -58,7 +53,9 @@ export const InputBox: React.FC<InputBoxProps> = ({
58
53
  permissionMode: chatPermissionMode,
59
54
  setPermissionMode: setChatPermissionMode,
60
55
  handleRewindSelect,
56
+ backgroundCurrentTask,
61
57
  messages,
58
+ getFullMessageThread,
62
59
  } = useChat();
63
60
 
64
61
  // Input manager with all input state and functionality (including images)
@@ -82,26 +79,19 @@ export const InputBox: React.FC<InputBoxProps> = ({
82
79
  handleCancelCommandSelect,
83
80
  handleHistorySearchSelect,
84
81
  handleCancelHistorySearch,
85
- // Memory type selector
86
- showMemoryTypeSelector,
87
- memoryMessage,
88
- handleMemoryTypeSelect,
89
- handleCancelMemoryTypeSelect,
90
82
  // History search
91
83
  showHistorySearch,
92
84
  historySearchQuery,
93
- // Bash/MCP Manager
94
- showBashManager,
85
+ // Task/MCP Manager
86
+ showBackgroundTaskManager,
95
87
  showMcpManager,
96
88
  showRewindManager,
97
- setShowBashManager,
89
+ setShowBackgroundTaskManager,
98
90
  setShowMcpManager,
99
91
  setShowRewindManager,
100
92
  // Permission mode
101
93
  permissionMode,
102
94
  setPermissionMode,
103
- // Input history
104
- setUserInputHistory,
105
95
  // Main handler
106
96
  handleInput,
107
97
  // Manager ready state
@@ -109,8 +99,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
109
99
  } = useInputManager({
110
100
  onSendMessage: sendMessage,
111
101
  onHasSlashCommand: hasSlashCommand,
112
- onSaveMemory: saveMemory,
113
102
  onAbortMessage: abortMessage,
103
+ onBackgroundCurrentTask: backgroundCurrentTask,
114
104
  onPermissionModeChange: setChatPermissionMode,
115
105
  });
116
106
 
@@ -119,11 +109,6 @@ export const InputBox: React.FC<InputBoxProps> = ({
119
109
  setPermissionMode(chatPermissionMode);
120
110
  }, [chatPermissionMode, setPermissionMode]);
121
111
 
122
- // Set user input history when it changes
123
- useEffect(() => {
124
- setUserInputHistory(userInputHistory);
125
- }, [userInputHistory, setUserInputHistory]);
126
-
127
112
  // Use the InputManager's unified input handler
128
113
  useInput(async (input, key) => {
129
114
  await handleInput(
@@ -175,6 +160,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
175
160
  messages={messages}
176
161
  onSelect={handleRewindSelectWithClose}
177
162
  onCancel={handleRewindCancel}
163
+ getFullMessageThread={getFullMessageThread}
178
164
  />
179
165
  );
180
166
  }
@@ -208,18 +194,12 @@ export const InputBox: React.FC<InputBoxProps> = ({
208
194
  />
209
195
  )}
210
196
 
211
- {showMemoryTypeSelector && (
212
- <MemoryTypeSelector
213
- message={memoryMessage}
214
- onSelect={handleMemoryTypeSelect}
215
- onCancel={handleCancelMemoryTypeSelect}
197
+ {showBackgroundTaskManager && (
198
+ <BackgroundTaskManager
199
+ onCancel={() => setShowBackgroundTaskManager(false)}
216
200
  />
217
201
  )}
218
202
 
219
- {showBashManager && (
220
- <BashShellManager onCancel={() => setShowBashManager(false)} />
221
- )}
222
-
223
203
  {showMcpManager && (
224
204
  <McpManager
225
205
  onCancel={() => setShowMcpManager(false)}
@@ -229,7 +209,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
229
209
  />
230
210
  )}
231
211
 
232
- {showBashManager || showMcpManager || showRewindManager || (
212
+ {showBackgroundTaskManager || showMcpManager || showRewindManager || (
233
213
  <Box flexDirection="column">
234
214
  <Box
235
215
  borderStyle="single"
@@ -251,7 +231,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
251
231
  )}
252
232
  </Text>
253
233
  </Box>
254
- <Box paddingRight={1}>
234
+ <Box paddingRight={1} justifyContent="space-between" width="100%">
255
235
  <Text color="gray">
256
236
  Mode:{" "}
257
237
  <Text color={permissionMode === "plan" ? "yellow" : "cyan"}>
@@ -0,0 +1,56 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+
4
+ export interface LoadingIndicatorProps {
5
+ isLoading?: boolean;
6
+ isCommandRunning?: boolean;
7
+ isCompressing?: boolean;
8
+ latestTotalTokens?: number;
9
+ }
10
+
11
+ export const LoadingIndicator = ({
12
+ isLoading = false,
13
+ isCommandRunning = false,
14
+ isCompressing = false,
15
+ latestTotalTokens = 0,
16
+ }: LoadingIndicatorProps) => {
17
+ return (
18
+ <Box flexDirection="column">
19
+ {isLoading && (
20
+ <Box>
21
+ <Text color="yellow">💭 AI is thinking... </Text>
22
+ {latestTotalTokens > 0 && (
23
+ <>
24
+ <Text color="gray" dimColor>
25
+ |{" "}
26
+ </Text>
27
+ <Text color="blue" bold>
28
+ {latestTotalTokens.toLocaleString()}
29
+ </Text>
30
+ <Text color="gray" dimColor>
31
+ {" "}
32
+ tokens{" "}
33
+ </Text>
34
+ </>
35
+ )}
36
+ <Text color="gray" dimColor>
37
+ |{" "}
38
+ </Text>
39
+ <Text color="red" bold>
40
+ Esc
41
+ </Text>
42
+ <Text color="gray" dimColor>
43
+ {" "}
44
+ to abort
45
+ </Text>
46
+ </Box>
47
+ )}
48
+ {isCommandRunning && <Text color="blue">🚀 Command is running...</Text>}
49
+ {isCompressing && (
50
+ <Text color="magenta">🗜️ Compressing message history...</Text>
51
+ )}
52
+ </Box>
53
+ );
54
+ };
55
+
56
+ LoadingIndicator.displayName = "LoadingIndicator";