wave-code 0.5.0 → 0.6.0

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 (88) hide show
  1. package/dist/components/BackgroundTaskManager.d.ts +6 -0
  2. package/dist/components/BackgroundTaskManager.d.ts.map +1 -0
  3. package/dist/components/{TaskManager.js → BackgroundTaskManager.js} +1 -1
  4. package/dist/components/ChatInterface.d.ts.map +1 -1
  5. package/dist/components/ChatInterface.js +55 -5
  6. package/dist/components/CommandSelector.d.ts.map +1 -1
  7. package/dist/components/CommandSelector.js +10 -2
  8. package/dist/components/CompressDisplay.d.ts.map +1 -1
  9. package/dist/components/CompressDisplay.js +6 -10
  10. package/dist/components/ConfirmationDetails.d.ts +9 -0
  11. package/dist/components/ConfirmationDetails.d.ts.map +1 -0
  12. package/dist/components/ConfirmationDetails.js +53 -0
  13. package/dist/components/{Confirmation.d.ts → ConfirmationSelector.d.ts} +3 -3
  14. package/dist/components/ConfirmationSelector.d.ts.map +1 -0
  15. package/dist/components/{Confirmation.js → ConfirmationSelector.js} +34 -96
  16. package/dist/components/DiffDisplay.d.ts.map +1 -1
  17. package/dist/components/DiffDisplay.js +44 -1
  18. package/dist/components/FileSelector.d.ts.map +1 -1
  19. package/dist/components/FileSelector.js +2 -2
  20. package/dist/components/HistorySearch.d.ts.map +1 -1
  21. package/dist/components/HistorySearch.js +12 -4
  22. package/dist/components/InputBox.d.ts +1 -2
  23. package/dist/components/InputBox.d.ts.map +1 -1
  24. package/dist/components/InputBox.js +5 -9
  25. package/dist/components/LoadingIndicator.d.ts +11 -0
  26. package/dist/components/LoadingIndicator.d.ts.map +1 -0
  27. package/dist/components/LoadingIndicator.js +6 -0
  28. package/dist/components/Markdown.d.ts.map +1 -1
  29. package/dist/components/Markdown.js +114 -121
  30. package/dist/components/MessageItem.d.ts.map +1 -1
  31. package/dist/components/MessageItem.js +1 -2
  32. package/dist/components/MessageList.d.ts +2 -3
  33. package/dist/components/MessageList.d.ts.map +1 -1
  34. package/dist/components/MessageList.js +7 -7
  35. package/dist/components/PlanDisplay.d.ts.map +1 -1
  36. package/dist/components/PlanDisplay.js +4 -12
  37. package/dist/components/SubagentBlock.d.ts.map +1 -1
  38. package/dist/components/SubagentBlock.js +9 -6
  39. package/dist/components/TaskList.d.ts +3 -0
  40. package/dist/components/TaskList.d.ts.map +1 -0
  41. package/dist/components/TaskList.js +49 -0
  42. package/dist/components/ToolResultDisplay.js +1 -1
  43. package/dist/contexts/useChat.d.ts +5 -2
  44. package/dist/contexts/useChat.d.ts.map +1 -1
  45. package/dist/contexts/useChat.js +25 -25
  46. package/dist/hooks/useInputManager.d.ts +2 -7
  47. package/dist/hooks/useInputManager.d.ts.map +1 -1
  48. package/dist/hooks/useInputManager.js +8 -40
  49. package/dist/hooks/useTasks.d.ts +2 -0
  50. package/dist/hooks/useTasks.d.ts.map +1 -0
  51. package/dist/hooks/useTasks.js +5 -0
  52. package/dist/managers/InputManager.d.ts +4 -19
  53. package/dist/managers/InputManager.d.ts.map +1 -1
  54. package/dist/managers/InputManager.js +22 -65
  55. package/package.json +5 -6
  56. package/src/components/{TaskManager.tsx → BackgroundTaskManager.tsx} +4 -2
  57. package/src/components/ChatInterface.tsx +100 -20
  58. package/src/components/CommandSelector.tsx +35 -17
  59. package/src/components/CompressDisplay.tsx +5 -22
  60. package/src/components/ConfirmationDetails.tsx +108 -0
  61. package/src/components/{Confirmation.tsx → ConfirmationSelector.tsx} +69 -184
  62. package/src/components/DiffDisplay.tsx +62 -1
  63. package/src/components/FileSelector.tsx +0 -2
  64. package/src/components/HistorySearch.tsx +45 -21
  65. package/src/components/InputBox.tsx +9 -24
  66. package/src/components/LoadingIndicator.tsx +56 -0
  67. package/src/components/Markdown.tsx +126 -323
  68. package/src/components/MessageItem.tsx +1 -3
  69. package/src/components/MessageList.tsx +10 -67
  70. package/src/components/PlanDisplay.tsx +4 -27
  71. package/src/components/SubagentBlock.tsx +25 -16
  72. package/src/components/TaskList.tsx +70 -0
  73. package/src/components/ToolResultDisplay.tsx +2 -2
  74. package/src/contexts/useChat.tsx +38 -33
  75. package/src/hooks/useInputManager.ts +9 -47
  76. package/src/hooks/useTasks.ts +6 -0
  77. package/src/managers/InputManager.ts +25 -83
  78. package/dist/components/Confirmation.d.ts.map +0 -1
  79. package/dist/components/MemoryDisplay.d.ts +0 -8
  80. package/dist/components/MemoryDisplay.d.ts.map +0 -1
  81. package/dist/components/MemoryDisplay.js +0 -25
  82. package/dist/components/MemoryTypeSelector.d.ts +0 -8
  83. package/dist/components/MemoryTypeSelector.d.ts.map +0 -1
  84. package/dist/components/MemoryTypeSelector.js +0 -38
  85. package/dist/components/TaskManager.d.ts +0 -6
  86. package/dist/components/TaskManager.d.ts.map +0 -1
  87. package/src/components/MemoryDisplay.tsx +0 -62
  88. package/src/components/MemoryTypeSelector.tsx +0 -98
@@ -3,44 +3,9 @@ import { Box, Text, useInput } from "ink";
3
3
  import type { PermissionDecision, AskUserQuestionInput } from "wave-agent-sdk";
4
4
  import {
5
5
  BASH_TOOL_NAME,
6
- EDIT_TOOL_NAME,
7
- MULTI_EDIT_TOOL_NAME,
8
- DELETE_FILE_TOOL_NAME,
9
- WRITE_TOOL_NAME,
10
6
  EXIT_PLAN_MODE_TOOL_NAME,
11
7
  ASK_USER_QUESTION_TOOL_NAME,
12
8
  } from "wave-agent-sdk";
13
- import { DiffDisplay } from "./DiffDisplay.js";
14
- import { PlanDisplay } from "./PlanDisplay.js";
15
-
16
- // Helper function to generate descriptive action text
17
- const getActionDescription = (
18
- toolName: string,
19
- toolInput?: Record<string, unknown>,
20
- ): string => {
21
- if (!toolInput) {
22
- return "Execute operation";
23
- }
24
-
25
- switch (toolName) {
26
- case BASH_TOOL_NAME:
27
- return `Execute command: ${toolInput.command || "unknown command"}`;
28
- case EDIT_TOOL_NAME:
29
- return `Edit file: ${toolInput.file_path || "unknown file"}`;
30
- case MULTI_EDIT_TOOL_NAME:
31
- return `Edit multiple sections in: ${toolInput.file_path || "unknown file"}`;
32
- case DELETE_FILE_TOOL_NAME:
33
- return `Delete file: ${toolInput.target_file || "unknown file"}`;
34
- case WRITE_TOOL_NAME:
35
- return `Write to file: ${toolInput.file_path || "unknown file"}`;
36
- case EXIT_PLAN_MODE_TOOL_NAME:
37
- return "Review and approve the plan";
38
- case ASK_USER_QUESTION_TOOL_NAME:
39
- return "Answer questions to clarify intent";
40
- default:
41
- return "Execute operation";
42
- }
43
- };
44
9
 
45
10
  const getHeaderColor = (header: string) => {
46
11
  const colors = ["red", "green", "blue", "magenta", "cyan"] as const;
@@ -51,7 +16,7 @@ const getHeaderColor = (header: string) => {
51
16
  return colors[Math.abs(hash) % colors.length];
52
17
  };
53
18
 
54
- export interface ConfirmationProps {
19
+ export interface ConfirmationSelectorProps {
55
20
  toolName: string;
56
21
  toolInput?: Record<string, unknown>;
57
22
  suggestedPrefix?: string;
@@ -66,10 +31,10 @@ interface ConfirmationState {
66
31
  selectedOption: "allow" | "auto" | "alternative";
67
32
  alternativeText: string;
68
33
  alternativeCursorPosition: number;
69
- hasUserInput: boolean; // to hide placeholder
34
+ hasUserInput: boolean;
70
35
  }
71
36
 
72
- export const Confirmation: React.FC<ConfirmationProps> = ({
37
+ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
73
38
  toolName,
74
39
  toolInput,
75
40
  suggestedPrefix,
@@ -86,7 +51,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
86
51
  hasUserInput: false,
87
52
  });
88
53
 
89
- // Specialized state for AskUserQuestion
90
54
  const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
91
55
  const [selectedOptionIndex, setSelectedOptionIndex] = useState(0);
92
56
  const [selectedOptionIndices, setSelectedOptionIndices] = useState<
@@ -114,7 +78,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
114
78
  };
115
79
 
116
80
  useInput((input, key) => {
117
- // Handle ESC to cancel and abort
118
81
  if (key.escape) {
119
82
  onCancel();
120
83
  onAbort();
@@ -123,10 +86,8 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
123
86
 
124
87
  if (toolName === ASK_USER_QUESTION_TOOL_NAME) {
125
88
  if (!currentQuestion) return;
126
-
127
89
  const options = [...currentQuestion.options, { label: "Other" }];
128
- const isMultiSelect = !!currentQuestion.multiSelect;
129
-
90
+ const isMultiSelect = currentQuestion.multiSelect;
130
91
  const isOtherFocused = selectedOptionIndex === options.length - 1;
131
92
 
132
93
  if (key.return) {
@@ -135,7 +96,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
135
96
  const selectedLabels = Array.from(selectedOptionIndices)
136
97
  .filter((i) => i < currentQuestion.options.length)
137
98
  .map((i) => currentQuestion.options[i].label);
138
-
139
99
  const isOtherChecked = selectedOptionIndices.has(options.length - 1);
140
100
  if (isOtherChecked && otherText.trim()) {
141
101
  selectedLabels.push(otherText.trim());
@@ -148,15 +108,12 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
148
108
  answer = options[selectedOptionIndex].label;
149
109
  }
150
110
  }
151
-
152
111
  if (!answer) return;
153
-
154
112
  const newAnswers = {
155
113
  ...userAnswers,
156
114
  [currentQuestion.question]: answer,
157
115
  };
158
116
  setUserAnswers(newAnswers);
159
-
160
117
  if (currentQuestionIndex < questions.length - 1) {
161
118
  setCurrentQuestionIndex(currentQuestionIndex + 1);
162
119
  setSelectedOptionIndex(0);
@@ -164,7 +121,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
164
121
  setOtherText("");
165
122
  setOtherCursorPosition(0);
166
123
  } else {
167
- // All questions answered
168
124
  onDecision({
169
125
  behavior: "allow",
170
126
  message: JSON.stringify(newAnswers),
@@ -180,33 +136,23 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
180
136
  ) {
181
137
  setSelectedOptionIndices((prev) => {
182
138
  const next = new Set(prev);
183
- if (next.has(selectedOptionIndex)) {
184
- next.delete(selectedOptionIndex);
185
- } else {
186
- next.add(selectedOptionIndex);
187
- }
139
+ if (next.has(selectedOptionIndex)) next.delete(selectedOptionIndex);
140
+ else next.add(selectedOptionIndex);
188
141
  return next;
189
142
  });
190
143
  return;
191
144
  }
192
-
193
- if (!isOtherFocused) {
194
- return;
195
- }
196
- // If isOtherFocused is true, fall through to handle space as text input
145
+ if (!isOtherFocused) return;
197
146
  }
198
147
 
199
148
  if (key.upArrow) {
200
- if (selectedOptionIndex > 0) {
149
+ if (selectedOptionIndex > 0)
201
150
  setSelectedOptionIndex(selectedOptionIndex - 1);
202
- }
203
151
  return;
204
152
  }
205
-
206
153
  if (key.downArrow) {
207
- if (selectedOptionIndex < options.length - 1) {
154
+ if (selectedOptionIndex < options.length - 1)
208
155
  setSelectedOptionIndex(selectedOptionIndex + 1);
209
- }
210
156
  return;
211
157
  }
212
158
 
@@ -223,34 +169,29 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
223
169
  }
224
170
  if (key.backspace || key.delete) {
225
171
  if (otherCursorPosition > 0) {
226
- setOtherText((prev) => {
227
- const next =
172
+ setOtherText(
173
+ (prev) =>
228
174
  prev.slice(0, otherCursorPosition - 1) +
229
- prev.slice(otherCursorPosition);
230
- return next;
231
- });
175
+ prev.slice(otherCursorPosition),
176
+ );
232
177
  setOtherCursorPosition((prev) => prev - 1);
233
178
  }
234
179
  return;
235
180
  }
236
181
  if (input && !key.ctrl && !key.meta) {
237
- setOtherText((prev) => {
238
- const next =
182
+ setOtherText(
183
+ (prev) =>
239
184
  prev.slice(0, otherCursorPosition) +
240
185
  input +
241
- prev.slice(otherCursorPosition);
242
- return next;
243
- });
186
+ prev.slice(otherCursorPosition),
187
+ );
244
188
  setOtherCursorPosition((prev) => prev + input.length);
245
189
  return;
246
190
  }
247
- return;
248
191
  }
249
-
250
192
  return;
251
193
  }
252
194
 
253
- // Handle Enter to confirm selection
254
195
  if (key.return) {
255
196
  if (state.selectedOption === "allow") {
256
197
  if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
@@ -263,24 +204,12 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
263
204
  const rule = suggestedPrefix
264
205
  ? `Bash(${suggestedPrefix}*)`
265
206
  : `Bash(${toolInput?.command})`;
266
- onDecision({
267
- behavior: "allow",
268
- newPermissionRule: rule,
269
- });
207
+ onDecision({ behavior: "allow", newPermissionRule: rule });
270
208
  } else {
271
- onDecision({
272
- behavior: "allow",
273
- newPermissionMode: "acceptEdits",
274
- });
275
- }
276
- } else {
277
- // For alternative option, require text input
278
- if (state.alternativeText.trim()) {
279
- onDecision({
280
- behavior: "deny",
281
- message: state.alternativeText.trim(),
282
- });
209
+ onDecision({ behavior: "allow", newPermissionMode: "acceptEdits" });
283
210
  }
211
+ } else if (state.alternativeText.trim()) {
212
+ onDecision({ behavior: "deny", message: state.alternativeText.trim() });
284
213
  }
285
214
  return;
286
215
  }
@@ -308,15 +237,13 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
308
237
  }
309
238
  }
310
239
 
311
- // Handle arrow keys for navigation
312
240
  if (key.upArrow) {
313
241
  setState((prev) => {
314
- if (prev.selectedOption === "alternative") {
242
+ if (prev.selectedOption === "alternative")
315
243
  return {
316
244
  ...prev,
317
245
  selectedOption: hidePersistentOption ? "allow" : "auto",
318
246
  };
319
- }
320
247
  if (prev.selectedOption === "auto")
321
248
  return { ...prev, selectedOption: "allow" };
322
249
  return prev;
@@ -326,12 +253,11 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
326
253
 
327
254
  if (key.downArrow) {
328
255
  setState((prev) => {
329
- if (prev.selectedOption === "allow") {
256
+ if (prev.selectedOption === "allow")
330
257
  return {
331
258
  ...prev,
332
259
  selectedOption: hidePersistentOption ? "alternative" : "auto",
333
260
  };
334
- }
335
261
  if (prev.selectedOption === "auto")
336
262
  return { ...prev, selectedOption: "alternative" };
337
263
  return prev;
@@ -339,9 +265,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
339
265
  return;
340
266
  }
341
267
 
342
- // Handle text input for alternative option
343
268
  if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
344
- // Focus on alternative option when user starts typing
345
269
  setState((prev) => {
346
270
  const nextText =
347
271
  prev.alternativeText.slice(0, prev.alternativeCursorPosition) +
@@ -359,7 +283,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
359
283
  return;
360
284
  }
361
285
 
362
- // Handle backspace and delete
363
286
  if (key.backspace || key.delete) {
364
287
  setState((prev) => {
365
288
  if (prev.alternativeCursorPosition > 0) {
@@ -385,22 +308,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
385
308
  state.selectedOption === "alternative" && !state.hasUserInput;
386
309
 
387
310
  return (
388
- <Box
389
- flexDirection="column"
390
- borderStyle="single"
391
- borderColor="yellow"
392
- borderBottom={false}
393
- borderLeft={false}
394
- borderRight={false}
395
- paddingTop={1}
396
- >
397
- <Text color="yellow" bold>
398
- Tool: {toolName}
399
- </Text>
400
- <Text color="yellow">{getActionDescription(toolName, toolInput)}</Text>
401
-
402
- <DiffDisplay toolName={toolName} parameters={JSON.stringify(toolInput)} />
403
-
311
+ <Box flexDirection="column">
404
312
  {toolName === ASK_USER_QUESTION_TOOL_NAME &&
405
313
  currentQuestion &&
406
314
  !isExpanded && (
@@ -417,61 +325,52 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
417
325
  </Box>
418
326
  <Text bold>{currentQuestion.question}</Text>
419
327
  </Box>
420
-
421
328
  <Box flexDirection="column">
422
- {(() => {
423
- const isMultiSelect = !!currentQuestion.multiSelect;
424
- return [...currentQuestion.options, { label: "Other" }].map(
425
- (option, index) => {
426
- const isSelected = selectedOptionIndex === index;
427
- const isChecked = isMultiSelect
428
- ? selectedOptionIndices.has(index)
429
- : isSelected;
430
- const isOther = index === currentQuestion.options.length;
431
- const isRecommended = !isOther && option.isRecommended;
432
-
433
- return (
434
- <Box key={index}>
435
- <Text
436
- color={isSelected ? "black" : "white"}
437
- backgroundColor={isSelected ? "yellow" : undefined}
438
- >
439
- {isSelected ? "> " : " "}
440
- {isMultiSelect ? (isChecked ? "[x] " : "[ ] ") : ""}
441
- {option.label}
442
- {isRecommended && (
443
- <Text color="green" bold>
444
- {" "}
445
- (Recommended)
446
- </Text>
447
- )}
448
- {option.description ? ` - ${option.description}` : ""}
449
- {isOther && isSelected && (
450
- <Text>
451
- :{" "}
452
- {otherText ? (
453
- <>
454
- {otherText.slice(0, otherCursorPosition)}
455
- <Text backgroundColor="white" color="black">
456
- {otherText[otherCursorPosition] || " "}
457
- </Text>
458
- {otherText.slice(otherCursorPosition + 1)}
459
- </>
460
- ) : (
461
- <Text color="gray" dimColor>
462
- [Type your answer...]
329
+ {[...currentQuestion.options, { label: "Other" }].map(
330
+ (option, index) => {
331
+ const isSelected = selectedOptionIndex === index;
332
+ const isChecked = currentQuestion.multiSelect
333
+ ? selectedOptionIndices.has(index)
334
+ : isSelected;
335
+ const isOther = index === currentQuestion.options.length;
336
+ return (
337
+ <Box key={index}>
338
+ <Text
339
+ color={isSelected ? "black" : "white"}
340
+ backgroundColor={isSelected ? "yellow" : undefined}
341
+ >
342
+ {isSelected ? "> " : " "}
343
+ {currentQuestion.multiSelect
344
+ ? isChecked
345
+ ? "[x] "
346
+ : "[ ] "
347
+ : ""}
348
+ {option.label}
349
+ {option.description ? ` - ${option.description}` : ""}
350
+ {isOther && isSelected && (
351
+ <Text>
352
+ :{" "}
353
+ {otherText ? (
354
+ <>
355
+ {otherText.slice(0, otherCursorPosition)}
356
+ <Text backgroundColor="white" color="black">
357
+ {otherText[otherCursorPosition] || " "}
463
358
  </Text>
464
- )}
465
- </Text>
466
- )}
467
- </Text>
468
- </Box>
469
- );
470
- },
471
- );
472
- })()}
359
+ {otherText.slice(otherCursorPosition + 1)}
360
+ </>
361
+ ) : (
362
+ <Text color="gray" dimColor>
363
+ [Type your answer...]
364
+ </Text>
365
+ )}
366
+ </Text>
367
+ )}
368
+ </Text>
369
+ </Box>
370
+ );
371
+ },
372
+ )}
473
373
  </Box>
474
-
475
374
  <Box marginTop={1}>
476
375
  <Text dimColor>
477
376
  Question {currentQuestionIndex + 1} of {questions.length} •
@@ -482,23 +381,12 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
482
381
  </Box>
483
382
  )}
484
383
 
485
- {toolName !== ASK_USER_QUESTION_TOOL_NAME &&
486
- toolName === EXIT_PLAN_MODE_TOOL_NAME &&
487
- !!toolInput?.plan_content && (
488
- <PlanDisplay
489
- plan={toolInput.plan_content as string}
490
- isExpanded={isExpanded}
491
- />
492
- )}
493
-
494
384
  {toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (
495
385
  <>
496
386
  <Box marginTop={1}>
497
387
  <Text>Do you want to proceed?</Text>
498
388
  </Box>
499
-
500
389
  <Box marginTop={1} flexDirection="column">
501
- {/* Option 1: Yes */}
502
390
  <Box key="allow-option">
503
391
  <Text
504
392
  color={state.selectedOption === "allow" ? "black" : "white"}
@@ -513,8 +401,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
513
401
  : "Yes"}
514
402
  </Text>
515
403
  </Box>
516
-
517
- {/* Option 2: Auto-accept/Persistent */}
518
404
  {!hidePersistentOption && (
519
405
  <Box key="auto-option">
520
406
  <Text
@@ -529,8 +415,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
529
415
  </Text>
530
416
  </Box>
531
417
  )}
532
-
533
- {/* Option 3: Alternative */}
534
418
  <Box key="alternative-option">
535
419
  <Text
536
420
  color={
@@ -571,7 +455,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
571
455
  </Text>
572
456
  </Box>
573
457
  </Box>
574
-
575
458
  <Box marginTop={1}>
576
459
  <Text dimColor>Use ↑↓ to navigate • ESC to cancel</Text>
577
460
  </Box>
@@ -580,3 +463,5 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
580
463
  </Box>
581
464
  );
582
465
  };
466
+
467
+ ConfirmationSelector.displayName = "ConfirmationSelector";
@@ -153,7 +153,60 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
153
153
  const lines = part.value
154
154
  .split("\n")
155
155
  .filter((line) => line !== "");
156
- 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
+
157
210
  diffElements.push(
158
211
  <Box
159
212
  key={`context-${changeIndex}-${partIndex}-${lineIndex}`}
@@ -164,6 +217,14 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
164
217
  </Box>,
165
218
  );
166
219
  });
220
+
221
+ if (showEllipsisBottom) {
222
+ diffElements.push(
223
+ <Box key={`ellipsis-bottom-${changeIndex}-${partIndex}`}>
224
+ <Text color="gray"> ...</Text>
225
+ </Box>,
226
+ );
227
+ }
167
228
  }
168
229
  });
169
230
 
@@ -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>